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