Relicensing TinyCC
[tinycc.git] / tccasm.c
1 /*
2  *  GAS like assembler for TCC
3  * 
4  *  Copyright (c) 2001-2004 Fabrice Bellard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "tcc.h"
22 #ifdef CONFIG_TCC_ASM
23
24 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
25 {
26     char buf[64];
27     TokenSym *ts;
28
29     snprintf(buf, sizeof(buf), "L..%u", n);
30     ts = tok_alloc(buf, strlen(buf));
31     return ts->tok;
32 }
33
34 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
35
36 /* We do not use the C expression parser to handle symbols. Maybe the
37    C expression parser could be tweaked to do so. */
38
39 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
40 {
41     Sym *sym;
42     int op, n, label;
43     const char *p;
44
45     switch(tok) {
46     case TOK_PPNUM:
47         p = tokc.cstr->data;
48         n = strtoul(p, (char **)&p, 0);
49         if (*p == 'b' || *p == 'f') {
50             /* backward or forward label */
51             label = asm_get_local_label_name(s1, n);
52             sym = label_find(label);
53             if (*p == 'b') {
54                 /* backward : find the last corresponding defined label */
55                 if (sym && sym->r == 0)
56                     sym = sym->prev_tok;
57                 if (!sym)
58                     tcc_error("local label '%d' not found backward", n);
59             } else {
60                 /* forward */
61                 if (!sym || sym->r) {
62                     /* if the last label is defined, then define a new one */
63                     sym = label_push(&s1->asm_labels, label, 0);
64                     sym->type.t = VT_STATIC | VT_VOID;
65                 }
66             }
67             pe->v = 0;
68             pe->sym = sym;
69         } else if (*p == '\0') {
70             pe->v = n;
71             pe->sym = NULL;
72         } else {
73             tcc_error("invalid number syntax");
74         }
75         next();
76         break;
77     case '+':
78         next();
79         asm_expr_unary(s1, pe);
80         break;
81     case '-':
82     case '~':
83         op = tok;
84         next();
85         asm_expr_unary(s1, pe);
86         if (pe->sym)
87             tcc_error("invalid operation with label");
88         if (op == '-')
89             pe->v = -pe->v;
90         else
91             pe->v = ~pe->v;
92         break;
93     case TOK_CCHAR:
94     case TOK_LCHAR:
95         pe->v = tokc.i;
96         pe->sym = NULL;
97         next();
98         break;
99     case '(':
100         next();
101         asm_expr(s1, pe);
102         skip(')');
103         break;
104     default:
105         if (tok >= TOK_IDENT) {
106             /* label case : if the label was not found, add one */
107             sym = label_find(tok);
108             if (!sym) {
109                 sym = label_push(&s1->asm_labels, tok, 0);
110                 /* NOTE: by default, the symbol is global */
111                 sym->type.t = VT_VOID;
112             }
113             if (sym->r == SHN_ABS) {
114                 /* if absolute symbol, no need to put a symbol value */
115                 pe->v = sym->jnext;
116                 pe->sym = NULL;
117             } else {
118                 pe->v = 0;
119                 pe->sym = sym;
120             }
121             next();
122         } else {
123             tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
124         }
125         break;
126     }
127 }
128     
129 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
130 {
131     int op;
132     ExprValue e2;
133
134     asm_expr_unary(s1, pe);
135     for(;;) {
136         op = tok;
137         if (op != '*' && op != '/' && op != '%' && 
138             op != TOK_SHL && op != TOK_SAR)
139             break;
140         next();
141         asm_expr_unary(s1, &e2);
142         if (pe->sym || e2.sym)
143             tcc_error("invalid operation with label");
144         switch(op) {
145         case '*':
146             pe->v *= e2.v;
147             break;
148         case '/':  
149             if (e2.v == 0) {
150             div_error:
151                 tcc_error("division by zero");
152             }
153             pe->v /= e2.v;
154             break;
155         case '%':  
156             if (e2.v == 0)
157                 goto div_error;
158             pe->v %= e2.v;
159             break;
160         case TOK_SHL:
161             pe->v <<= e2.v;
162             break;
163         default:
164         case TOK_SAR:
165             pe->v >>= e2.v;
166             break;
167         }
168     }
169 }
170
171 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
172 {
173     int op;
174     ExprValue e2;
175
176     asm_expr_prod(s1, pe);
177     for(;;) {
178         op = tok;
179         if (op != '&' && op != '|' && op != '^')
180             break;
181         next();
182         asm_expr_prod(s1, &e2);
183         if (pe->sym || e2.sym)
184             tcc_error("invalid operation with label");
185         switch(op) {
186         case '&':
187             pe->v &= e2.v;
188             break;
189         case '|':  
190             pe->v |= e2.v;
191             break;
192         default:
193         case '^':
194             pe->v ^= e2.v;
195             break;
196         }
197     }
198 }
199
200 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
201 {
202     int op;
203     ExprValue e2;
204
205     asm_expr_logic(s1, pe);
206     for(;;) {
207         op = tok;
208         if (op != '+' && op != '-')
209             break;
210         next();
211         asm_expr_logic(s1, &e2);
212         if (op == '+') {
213             if (pe->sym != NULL && e2.sym != NULL)
214                 goto cannot_relocate;
215             pe->v += e2.v;
216             if (pe->sym == NULL && e2.sym != NULL)
217                 pe->sym = e2.sym;
218         } else {
219             pe->v -= e2.v;
220             /* NOTE: we are less powerful than gas in that case
221                because we store only one symbol in the expression */
222             if (!pe->sym && !e2.sym) {
223                 /* OK */
224             } else if (pe->sym && !e2.sym) {
225                 /* OK */
226             } else if (pe->sym && e2.sym) {
227                 if (pe->sym == e2.sym) { 
228                     /* OK */
229                 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
230                     /* we also accept defined symbols in the same section */
231                     pe->v += pe->sym->jnext - e2.sym->jnext;
232                 } else {
233                     goto cannot_relocate;
234                 }
235                 pe->sym = NULL; /* same symbols can be substracted to NULL */
236             } else {
237             cannot_relocate:
238                 tcc_error("invalid operation with label");
239             }
240         }
241     }
242 }
243
244 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
245 {
246     asm_expr_sum(s1, pe);
247 }
248
249 ST_FUNC int asm_int_expr(TCCState *s1)
250 {
251     ExprValue e;
252     asm_expr(s1, &e);
253     if (e.sym)
254         expect("constant");
255     return e.v;
256 }
257
258 /* NOTE: the same name space as C labels is used to avoid using too
259    much memory when storing labels in TokenStrings */
260 static void asm_new_label1(TCCState *s1, int label, int is_local,
261                            int sh_num, int value)
262 {
263     Sym *sym;
264
265     sym = label_find(label);
266     if (sym) {
267         if (sym->r) {
268             /* the label is already defined */
269             if (!is_local) {
270                 tcc_error("assembler label '%s' already defined", 
271                       get_tok_str(label, NULL));
272             } else {
273                 /* redefinition of local labels is possible */
274                 goto new_label;
275             }
276         }
277     } else {
278     new_label:
279         sym = label_push(&s1->asm_labels, label, 0);
280         sym->type.t = VT_STATIC | VT_VOID;
281     }
282     sym->r = sh_num;
283     sym->jnext = value;
284 }
285
286 static void asm_new_label(TCCState *s1, int label, int is_local)
287 {
288     asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
289 }
290
291 static void asm_free_labels(TCCState *st)
292 {
293     Sym *s, *s1;
294     Section *sec;
295     
296     for(s = st->asm_labels; s != NULL; s = s1) {
297         s1 = s->prev;
298         /* define symbol value in object file */
299         if (s->r) {
300             if (s->r == SHN_ABS)
301                 sec = SECTION_ABS;
302             else
303                 sec = st->sections[s->r];
304             put_extern_sym2(s, sec, s->jnext, 0, 0);
305         }
306         /* remove label */
307         table_ident[s->v - TOK_IDENT]->sym_label = NULL;
308         sym_free(s);
309     }
310     st->asm_labels = NULL;
311 }
312
313 static void use_section1(TCCState *s1, Section *sec)
314 {
315     cur_text_section->data_offset = ind;
316     cur_text_section = sec;
317     ind = cur_text_section->data_offset;
318 }
319
320 static void use_section(TCCState *s1, const char *name)
321 {
322     Section *sec;
323     sec = find_section(s1, name);
324     use_section1(s1, sec);
325 }
326
327 static void asm_parse_directive(TCCState *s1)
328 {
329     int n, offset, v, size, tok1;
330     Section *sec;
331     uint8_t *ptr;
332
333     /* assembler directive */
334     next();
335     sec = cur_text_section;
336     switch(tok) {
337     case TOK_ASM_align:
338     case TOK_ASM_skip:
339     case TOK_ASM_space:
340         tok1 = tok;
341         next();
342         n = asm_int_expr(s1);
343         if (tok1 == TOK_ASM_align) {
344             if (n < 0 || (n & (n-1)) != 0)
345                 tcc_error("alignment must be a positive power of two");
346             offset = (ind + n - 1) & -n;
347             size = offset - ind;
348             /* the section must have a compatible alignment */
349             if (sec->sh_addralign < n)
350                 sec->sh_addralign = n;
351         } else {
352             size = n;
353         }
354         v = 0;
355         if (tok == ',') {
356             next();
357             v = asm_int_expr(s1);
358         }
359     zero_pad:
360         if (sec->sh_type != SHT_NOBITS) {
361             sec->data_offset = ind;
362             ptr = section_ptr_add(sec, size);
363             memset(ptr, v, size);
364         }
365         ind += size;
366         break;
367     case TOK_ASM_quad:
368         next();
369         for(;;) {
370             uint64_t vl;
371             const char *p;
372
373             p = tokc.cstr->data;
374             if (tok != TOK_PPNUM) {
375             error_constant:
376                 tcc_error("64 bit constant");
377             }
378             vl = strtoll(p, (char **)&p, 0);
379             if (*p != '\0')
380                 goto error_constant;
381             next();
382             if (sec->sh_type != SHT_NOBITS) {
383                 /* XXX: endianness */
384                 gen_le32(vl);
385                 gen_le32(vl >> 32);
386             } else {
387                 ind += 8;
388             }
389             if (tok != ',')
390                 break;
391             next();
392         }
393         break;
394     case TOK_ASM_byte:
395         size = 1;
396         goto asm_data;
397     case TOK_ASM_word:
398     case TOK_SHORT:
399         size = 2;
400         goto asm_data;
401     case TOK_LONG:
402     case TOK_INT:
403         size = 4;
404     asm_data:
405         next();
406         for(;;) {
407             ExprValue e;
408             asm_expr(s1, &e);
409             if (sec->sh_type != SHT_NOBITS) {
410                 if (size == 4) {
411                     gen_expr32(&e);
412                 } else {
413                     if (e.sym)
414                         expect("constant");
415                     if (size == 1)
416                         g(e.v);
417                     else
418                         gen_le16(e.v);
419                 }
420             } else {
421                 ind += size;
422             }
423             if (tok != ',')
424                 break;
425             next();
426         }
427         break;
428     case TOK_ASM_fill:
429         {
430             int repeat, size, val, i, j;
431             uint8_t repeat_buf[8];
432             next();
433             repeat = asm_int_expr(s1);
434             if (repeat < 0) {
435                 tcc_error("repeat < 0; .fill ignored");
436                 break;
437             }
438             size = 1;
439             val = 0;
440             if (tok == ',') {
441                 next();
442                 size = asm_int_expr(s1);
443                 if (size < 0) {
444                     tcc_error("size < 0; .fill ignored");
445                     break;
446                 }
447                 if (size > 8)
448                     size = 8;
449                 if (tok == ',') {
450                     next();
451                     val = asm_int_expr(s1);
452                 }
453             }
454             /* XXX: endianness */
455             repeat_buf[0] = val;
456             repeat_buf[1] = val >> 8;
457             repeat_buf[2] = val >> 16;
458             repeat_buf[3] = val >> 24;
459             repeat_buf[4] = 0;
460             repeat_buf[5] = 0;
461             repeat_buf[6] = 0;
462             repeat_buf[7] = 0;
463             for(i = 0; i < repeat; i++) {
464                 for(j = 0; j < size; j++) {
465                     g(repeat_buf[j]);
466                 }
467             }
468         }
469         break;
470     case TOK_ASM_org:
471         {
472             unsigned long n;
473             next();
474             /* XXX: handle section symbols too */
475             n = asm_int_expr(s1);
476             if (n < ind)
477                 tcc_error("attempt to .org backwards");
478             v = 0;
479             size = n - ind;
480             goto zero_pad;
481         }
482         break;
483     case TOK_ASM_globl:
484     case TOK_ASM_global:
485     case TOK_ASM_weak:
486     tok1 = tok;
487         do { 
488             Sym *sym;
489
490             next();
491             sym = label_find(tok);
492             if (!sym) {
493                 sym = label_push(&s1->asm_labels, tok, 0);
494                 sym->type.t = VT_VOID;
495             }
496             sym->type.t &= ~VT_STATIC;
497             if (tok1 == TOK_ASM_weak)
498                 sym->type.t |= VT_WEAK;
499             next();
500         } while (tok == ',');
501         break;
502     case TOK_ASM_string:
503     case TOK_ASM_ascii:
504     case TOK_ASM_asciz:
505         {
506             const uint8_t *p;
507             int i, size, t;
508
509             t = tok;
510             next();
511             for(;;) {
512                 if (tok != TOK_STR)
513                     expect("string constant");
514                 p = tokc.cstr->data;
515                 size = tokc.cstr->size;
516                 if (t == TOK_ASM_ascii && size > 0)
517                     size--;
518                 for(i = 0; i < size; i++)
519                     g(p[i]);
520                 next();
521                 if (tok == ',') {
522                     next();
523                 } else if (tok != TOK_STR) {
524                     break;
525                 }
526             }
527         }
528         break;
529     case TOK_ASM_text:
530     case TOK_ASM_data:
531     case TOK_ASM_bss:
532         { 
533             char sname[64];
534             tok1 = tok;
535             n = 0;
536             next();
537             if (tok != ';' && tok != TOK_LINEFEED) {
538                 n = asm_int_expr(s1);
539                 next();
540             }
541             sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
542             use_section(s1, sname);
543         }
544         break;
545     case TOK_ASM_file:
546         {
547             char filename[512];
548
549             filename[0] = '\0';
550             next();
551
552             if (tok == TOK_STR)
553                 pstrcat(filename, sizeof(filename), tokc.cstr->data);
554             else
555                 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
556
557             if (s1->warn_unsupported)
558                 tcc_warning("ignoring .file %s", filename);
559
560             next();
561         }
562         break;
563     case TOK_ASM_ident:
564         {
565             char ident[256];
566
567             ident[0] = '\0';
568             next();
569
570             if (tok == TOK_STR)
571                 pstrcat(ident, sizeof(ident), tokc.cstr->data);
572             else
573                 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
574
575             if (s1->warn_unsupported)
576                 tcc_warning("ignoring .ident %s", ident);
577
578             next();
579         }
580         break;
581     case TOK_ASM_size:
582         { 
583             Sym *sym;
584
585             next();
586             sym = label_find(tok);
587             if (!sym) {
588                 tcc_error("label not found: %s", get_tok_str(tok, NULL));
589             }
590
591             next();
592             skip(',');
593             /* XXX .size name,label2-label1 */
594             if (s1->warn_unsupported)
595                 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
596
597             while (tok != '\n' && tok != CH_EOF) {
598                 next();
599             }
600         }
601         break;
602     case TOK_ASM_type:
603         { 
604             Sym *sym;
605             const char *newtype;
606
607             next();
608             sym = label_find(tok);
609             if (!sym) {
610                 sym = label_push(&s1->asm_labels, tok, 0);
611                 sym->type.t = VT_VOID;
612             }
613
614             next();
615             skip(',');
616             if (tok == TOK_STR) {
617                 newtype = tokc.cstr->data;
618             } else {
619                 if (tok == '@' || tok == '%')
620                     skip(tok);
621                 newtype = get_tok_str(tok, NULL);
622             }
623
624             if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
625                 sym->type.t = VT_FUNC;
626             }
627             else if (s1->warn_unsupported)
628                 tcc_warning("change type of '%s' from 0x%x to '%s' ignored", 
629                     get_tok_str(sym->v, NULL), sym->type.t, newtype);
630
631             next();
632         }
633         break;
634     case TOK_SECTION1:
635         {
636             char sname[256];
637
638             /* XXX: support more options */
639             next();
640             sname[0] = '\0';
641             while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
642                 if (tok == TOK_STR)
643                     pstrcat(sname, sizeof(sname), tokc.cstr->data);
644                 else
645                     pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
646                 next();
647             }
648             if (tok == ',') {
649                 /* skip section options */
650                 next();
651                 if (tok != TOK_STR)
652                     expect("string constant");
653                 next();
654             }
655             last_text_section = cur_text_section;
656             use_section(s1, sname);
657         }
658         break;
659     case TOK_ASM_previous:
660         { 
661             Section *sec;
662             next();
663             if (!last_text_section)
664                 tcc_error("no previous section referenced");
665             sec = cur_text_section;
666             use_section1(s1, last_text_section);
667             last_text_section = sec;
668         }
669         break;
670 #ifdef TCC_TARGET_I386
671     case TOK_ASM_code16:
672         {
673             next();
674             s1->seg_size = 16;
675         }
676         break;
677     case TOK_ASM_code32:
678         {
679             next();
680             s1->seg_size = 32;
681         }
682         break;
683 #endif
684 #ifdef TCC_TARGET_X86_64
685     /* added for compatibility with GAS */
686     case TOK_ASM_code64:
687         next();
688         break;
689 #endif
690     default:
691         tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
692         break;
693     }
694 }
695
696
697 /* assemble a file */
698 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
699 {
700     int opcode;
701
702 #if 0
703     /* print stats about opcodes */
704     {
705         const ASMInstr *pa;
706         int freq[4];
707         int op_vals[500];
708         int nb_op_vals, i, j;
709
710         nb_op_vals = 0;
711         memset(freq, 0, sizeof(freq));
712         for(pa = asm_instrs; pa->sym != 0; pa++) {
713             freq[pa->nb_ops]++;
714             for(i=0;i<pa->nb_ops;i++) {
715                 for(j=0;j<nb_op_vals;j++) {
716                     if (pa->op_type[i] == op_vals[j])
717                         goto found;
718                 }
719                 op_vals[nb_op_vals++] = pa->op_type[i];
720             found: ;
721             }
722         }
723         for(i=0;i<nb_op_vals;i++) {
724             int v = op_vals[i];
725             if ((v & (v - 1)) != 0)
726                 printf("%3d: %08x\n", i, v);
727         }
728         printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
729                sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
730                freq[0], freq[1], freq[2], freq[3]);
731     }
732 #endif
733
734     /* XXX: undefine C labels */
735
736     ch = file->buf_ptr[0];
737     tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
738     parse_flags = PARSE_FLAG_ASM_COMMENTS;
739     if (do_preprocess)
740         parse_flags |= PARSE_FLAG_PREPROCESS;
741     next();
742     for(;;) {
743         if (tok == TOK_EOF)
744             break;
745         parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
746     redo:
747         if (tok == '#') {
748             /* horrible gas comment */
749             while (tok != TOK_LINEFEED)
750                 next();
751         } else if (tok == '.') {
752             asm_parse_directive(s1);
753         } else if (tok == TOK_PPNUM) {
754             const char *p;
755             int n;
756             p = tokc.cstr->data;
757             n = strtoul(p, (char **)&p, 10);
758             if (*p != '\0')
759                 expect("':'");
760             /* new local label */
761             asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
762             next();
763             skip(':');
764             goto redo;
765         } else if (tok >= TOK_IDENT) {
766             /* instruction or label */
767             opcode = tok;
768             next();
769             if (tok == ':') {
770                 /* new label */
771                 asm_new_label(s1, opcode, 0);
772                 next();
773                 goto redo;
774             } else if (tok == '=') {
775                 int n;
776                 next();
777                 n = asm_int_expr(s1);
778                 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
779                 goto redo;
780             } else {
781                 asm_opcode(s1, opcode);
782             }
783         }
784         /* end of line */
785         if (tok != ';' && tok != TOK_LINEFEED){
786             expect("end of line");
787         }
788         parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
789         next();
790     }
791
792     asm_free_labels(s1);
793
794     return 0;
795 }
796
797 /* Assemble the current file */
798 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
799 {
800     Sym *define_start;
801     int ret;
802
803     preprocess_init(s1);
804
805     /* default section is text */
806     cur_text_section = text_section;
807     ind = cur_text_section->data_offset;
808
809     define_start = define_stack;
810
811     /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
812        symbols can be safely used */
813     put_elf_sym(symtab_section, 0, 0,
814                 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
815                 SHN_ABS, file->filename);
816
817     ret = tcc_assemble_internal(s1, do_preprocess);
818
819     cur_text_section->data_offset = ind;
820
821     free_defines(define_start); 
822
823     return ret;
824 }
825
826 /********************************************************************/
827 /* GCC inline asm support */
828
829 /* assemble the string 'str' in the current C compilation unit without
830    C preprocessing. NOTE: str is modified by modifying the '\0' at the
831    end */
832 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
833 {
834     int saved_parse_flags;
835     const int *saved_macro_ptr;
836
837     saved_parse_flags = parse_flags;
838     saved_macro_ptr = macro_ptr;
839
840     tcc_open_bf(s1, ":asm:", len);
841     memcpy(file->buffer, str, len);
842
843     macro_ptr = NULL;
844     tcc_assemble_internal(s1, 0);
845     tcc_close();
846
847     parse_flags = saved_parse_flags;
848     macro_ptr = saved_macro_ptr;
849 }
850
851 /* find a constraint by its number or id (gcc 3 extended
852    syntax). return -1 if not found. Return in *pp in char after the
853    constraint */
854 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, 
855                            const char *name, const char **pp)
856 {
857     int index;
858     TokenSym *ts;
859     const char *p;
860
861     if (isnum(*name)) {
862         index = 0;
863         while (isnum(*name)) {
864             index = (index * 10) + (*name) - '0';
865             name++;
866         }
867         if ((unsigned)index >= nb_operands)
868             index = -1;
869     } else if (*name == '[') {
870         name++;
871         p = strchr(name, ']');
872         if (p) {
873             ts = tok_alloc(name, p - name);
874             for(index = 0; index < nb_operands; index++) {
875                 if (operands[index].id == ts->tok)
876                     goto found;
877             }
878             index = -1;
879         found:
880             name = p + 1;
881         } else {
882             index = -1;
883         }
884     } else {
885         index = -1;
886     }
887     if (pp)
888         *pp = name;
889     return index;
890 }
891
892 static void subst_asm_operands(ASMOperand *operands, int nb_operands, 
893                                int nb_outputs,
894                                CString *out_str, CString *in_str)
895 {
896     int c, index, modifier;
897     const char *str;
898     ASMOperand *op;
899     SValue sv;
900
901     cstr_new(out_str);
902     str = in_str->data;
903     for(;;) {
904         c = *str++;
905         if (c == '%') {
906             if (*str == '%') {
907                 str++;
908                 goto add_char;
909             }
910             modifier = 0;
911             if (*str == 'c' || *str == 'n' ||
912                 *str == 'b' || *str == 'w' || *str == 'h')
913                 modifier = *str++;
914             index = find_constraint(operands, nb_operands, str, &str);
915             if (index < 0)
916                 tcc_error("invalid operand reference after %%");
917             op = &operands[index];
918             sv = *op->vt;
919             if (op->reg >= 0) {
920                 sv.r = op->reg;
921                 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
922                     sv.r |= VT_LVAL;
923             }
924             subst_asm_operand(out_str, &sv, modifier);
925         } else {
926         add_char:
927             cstr_ccat(out_str, c);
928             if (c == '\0')
929                 break;
930         }
931     }
932 }
933
934
935 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
936                                int is_output)
937 {
938     ASMOperand *op;
939     int nb_operands;
940
941     if (tok != ':') {
942         nb_operands = *nb_operands_ptr;
943         for(;;) {
944             if (nb_operands >= MAX_ASM_OPERANDS)
945                 tcc_error("too many asm operands");
946             op = &operands[nb_operands++];
947             op->id = 0;
948             if (tok == '[') {
949                 next();
950                 if (tok < TOK_IDENT)
951                     expect("identifier");
952                 op->id = tok;
953                 next();
954                 skip(']');
955             }
956             if (tok != TOK_STR)
957                 expect("string constant");
958             op->constraint = tcc_malloc(tokc.cstr->size);
959             strcpy(op->constraint, tokc.cstr->data);
960             next();
961             skip('(');
962             gexpr();
963             if (is_output) {
964                 test_lvalue();
965             } else {
966                 /* we want to avoid LLOCAL case, except when the 'm'
967                    constraint is used. Note that it may come from
968                    register storage, so we need to convert (reg)
969                    case */
970                 if ((vtop->r & VT_LVAL) &&
971                     ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
972                      (vtop->r & VT_VALMASK) < VT_CONST) &&
973                     !strchr(op->constraint, 'm')) {
974                     gv(RC_INT);
975                 }
976             }
977             op->vt = vtop;
978             skip(')');
979             if (tok == ',') {
980                 next();
981             } else {
982                 break;
983             }
984         }
985         *nb_operands_ptr = nb_operands;
986     }
987 }
988
989 /* parse the GCC asm() instruction */
990 ST_FUNC void asm_instr(void)
991 {
992     CString astr, astr1;
993     ASMOperand operands[MAX_ASM_OPERANDS];
994     int nb_outputs, nb_operands, i, must_subst, out_reg;
995     uint8_t clobber_regs[NB_ASM_REGS];
996
997     next();
998     /* since we always generate the asm() instruction, we can ignore
999        volatile */
1000     if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1001         next();
1002     }
1003     parse_asm_str(&astr);
1004     nb_operands = 0;
1005     nb_outputs = 0;
1006     must_subst = 0;
1007     memset(clobber_regs, 0, sizeof(clobber_regs));
1008     if (tok == ':') {
1009         next();
1010         must_subst = 1;
1011         /* output args */
1012         parse_asm_operands(operands, &nb_operands, 1);
1013         nb_outputs = nb_operands;
1014         if (tok == ':') {
1015             next();
1016             if (tok != ')') {
1017                 /* input args */
1018                 parse_asm_operands(operands, &nb_operands, 0);
1019                 if (tok == ':') {
1020                     /* clobber list */
1021                     /* XXX: handle registers */
1022                     next();
1023                     for(;;) {
1024                         if (tok != TOK_STR)
1025                             expect("string constant");
1026                         asm_clobber(clobber_regs, tokc.cstr->data);
1027                         next();
1028                         if (tok == ',') {
1029                             next();
1030                         } else {
1031                             break;
1032                         }
1033                     }
1034                 }
1035             }
1036         }
1037     }
1038     skip(')');
1039     /* NOTE: we do not eat the ';' so that we can restore the current
1040        token after the assembler parsing */
1041     if (tok != ';')
1042         expect("';'");
1043     
1044     /* save all values in the memory */
1045     save_regs(0);
1046
1047     /* compute constraints */
1048     asm_compute_constraints(operands, nb_operands, nb_outputs, 
1049                             clobber_regs, &out_reg);
1050
1051     /* substitute the operands in the asm string. No substitution is
1052        done if no operands (GCC behaviour) */
1053 #ifdef ASM_DEBUG
1054     printf("asm: \"%s\"\n", (char *)astr.data);
1055 #endif
1056     if (must_subst) {
1057         subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1058         cstr_free(&astr);
1059     } else {
1060         astr1 = astr;
1061     }
1062 #ifdef ASM_DEBUG
1063     printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1064 #endif
1065
1066     /* generate loads */
1067     asm_gen_code(operands, nb_operands, nb_outputs, 0, 
1068                  clobber_regs, out_reg);    
1069
1070     /* assemble the string with tcc internal assembler */
1071     tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1072
1073     /* restore the current C token */
1074     next();
1075
1076     /* store the output values if needed */
1077     asm_gen_code(operands, nb_operands, nb_outputs, 1, 
1078                  clobber_regs, out_reg);
1079     
1080     /* free everything */
1081     for(i=0;i<nb_operands;i++) {
1082         ASMOperand *op;
1083         op = &operands[i];
1084         tcc_free(op->constraint);
1085         vpop();
1086     }
1087     cstr_free(&astr1);
1088 }
1089
1090 ST_FUNC void asm_global_instr(void)
1091 {
1092     CString astr;
1093
1094     next();
1095     parse_asm_str(&astr);
1096     skip(')');
1097     /* NOTE: we do not eat the ';' so that we can restore the current
1098        token after the assembler parsing */
1099     if (tok != ';')
1100         expect("';'");
1101     
1102 #ifdef ASM_DEBUG
1103     printf("asm_global: \"%s\"\n", (char *)astr.data);
1104 #endif
1105     cur_text_section = text_section;
1106     ind = cur_text_section->data_offset;
1107
1108     /* assemble the string with tcc internal assembler */
1109     tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1110     
1111     cur_text_section->data_offset = ind;
1112
1113     /* restore the current C token */
1114     next();
1115
1116     cstr_free(&astr);
1117 }
1118 #endif /* CONFIG_TCC_ASM */