Makefile: Add rules to create tags and TAGS.
[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 subtracted 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 case TOK_ASM_hidden:
487 tok1 = tok;
488 do {
489 Sym *sym;
490
491 next();
492 sym = label_find(tok);
493 if (!sym) {
494 sym = label_push(&s1->asm_labels, tok, 0);
495 sym->type.t = VT_VOID;
496 }
497 if (tok1 != TOK_ASM_hidden)
498 sym->type.t &= ~VT_STATIC;
499 if (tok1 == TOK_ASM_weak)
500 sym->type.t |= VT_WEAK;
501 else if (tok1 == TOK_ASM_hidden)
502 sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
503 next();
504 } while (tok == ',');
505 break;
506 case TOK_ASM_string:
507 case TOK_ASM_ascii:
508 case TOK_ASM_asciz:
509 {
510 const uint8_t *p;
511 int i, size, t;
512
513 t = tok;
514 next();
515 for(;;) {
516 if (tok != TOK_STR)
517 expect("string constant");
518 p = tokc.cstr->data;
519 size = tokc.cstr->size;
520 if (t == TOK_ASM_ascii && size > 0)
521 size--;
522 for(i = 0; i < size; i++)
523 g(p[i]);
524 next();
525 if (tok == ',') {
526 next();
527 } else if (tok != TOK_STR) {
528 break;
529 }
530 }
531 }
532 break;
533 case TOK_ASM_text:
534 case TOK_ASM_data:
535 case TOK_ASM_bss:
536 {
537 char sname[64];
538 tok1 = tok;
539 n = 0;
540 next();
541 if (tok != ';' && tok != TOK_LINEFEED) {
542 n = asm_int_expr(s1);
543 next();
544 }
545 sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
546 use_section(s1, sname);
547 }
548 break;
549 case TOK_ASM_file:
550 {
551 char filename[512];
552
553 filename[0] = '\0';
554 next();
555
556 if (tok == TOK_STR)
557 pstrcat(filename, sizeof(filename), tokc.cstr->data);
558 else
559 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
560
561 if (s1->warn_unsupported)
562 tcc_warning("ignoring .file %s", filename);
563
564 next();
565 }
566 break;
567 case TOK_ASM_ident:
568 {
569 char ident[256];
570
571 ident[0] = '\0';
572 next();
573
574 if (tok == TOK_STR)
575 pstrcat(ident, sizeof(ident), tokc.cstr->data);
576 else
577 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
578
579 if (s1->warn_unsupported)
580 tcc_warning("ignoring .ident %s", ident);
581
582 next();
583 }
584 break;
585 case TOK_ASM_size:
586 {
587 Sym *sym;
588
589 next();
590 sym = label_find(tok);
591 if (!sym) {
592 tcc_error("label not found: %s", get_tok_str(tok, NULL));
593 }
594
595 /* XXX .size name,label2-label1 */
596 if (s1->warn_unsupported)
597 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
598
599 next();
600 skip(',');
601 while (tok != '\n' && tok != CH_EOF) {
602 next();
603 }
604 }
605 break;
606 case TOK_ASM_type:
607 {
608 Sym *sym;
609 const char *newtype;
610
611 next();
612 sym = label_find(tok);
613 if (!sym) {
614 sym = label_push(&s1->asm_labels, tok, 0);
615 sym->type.t = VT_VOID;
616 }
617
618 next();
619 skip(',');
620 if (tok == TOK_STR) {
621 newtype = tokc.cstr->data;
622 } else {
623 if (tok == '@' || tok == '%')
624 skip(tok);
625 newtype = get_tok_str(tok, NULL);
626 }
627
628 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
629 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
630 }
631 else if (s1->warn_unsupported)
632 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
633 get_tok_str(sym->v, NULL), sym->type.t, newtype);
634
635 next();
636 }
637 break;
638 case TOK_SECTION1:
639 {
640 char sname[256];
641
642 /* XXX: support more options */
643 next();
644 sname[0] = '\0';
645 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
646 if (tok == TOK_STR)
647 pstrcat(sname, sizeof(sname), tokc.cstr->data);
648 else
649 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
650 next();
651 }
652 if (tok == ',') {
653 /* skip section options */
654 next();
655 if (tok != TOK_STR)
656 expect("string constant");
657 next();
658 }
659 last_text_section = cur_text_section;
660 use_section(s1, sname);
661 }
662 break;
663 case TOK_ASM_previous:
664 {
665 Section *sec;
666 next();
667 if (!last_text_section)
668 tcc_error("no previous section referenced");
669 sec = cur_text_section;
670 use_section1(s1, last_text_section);
671 last_text_section = sec;
672 }
673 break;
674 #ifdef TCC_TARGET_I386
675 case TOK_ASM_code16:
676 {
677 next();
678 s1->seg_size = 16;
679 }
680 break;
681 case TOK_ASM_code32:
682 {
683 next();
684 s1->seg_size = 32;
685 }
686 break;
687 #endif
688 #ifdef TCC_TARGET_X86_64
689 /* added for compatibility with GAS */
690 case TOK_ASM_code64:
691 next();
692 break;
693 #endif
694 default:
695 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
696 break;
697 }
698 }
699
700
701 /* assemble a file */
702 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
703 {
704 int opcode;
705
706 #if 0
707 /* print stats about opcodes */
708 {
709 const ASMInstr *pa;
710 int freq[4];
711 int op_vals[500];
712 int nb_op_vals, i, j;
713
714 nb_op_vals = 0;
715 memset(freq, 0, sizeof(freq));
716 for(pa = asm_instrs; pa->sym != 0; pa++) {
717 freq[pa->nb_ops]++;
718 for(i=0;i<pa->nb_ops;i++) {
719 for(j=0;j<nb_op_vals;j++) {
720 if (pa->op_type[i] == op_vals[j])
721 goto found;
722 }
723 op_vals[nb_op_vals++] = pa->op_type[i];
724 found: ;
725 }
726 }
727 for(i=0;i<nb_op_vals;i++) {
728 int v = op_vals[i];
729 if ((v & (v - 1)) != 0)
730 printf("%3d: %08x\n", i, v);
731 }
732 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
733 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
734 freq[0], freq[1], freq[2], freq[3]);
735 }
736 #endif
737
738 /* XXX: undefine C labels */
739
740 ch = file->buf_ptr[0];
741 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
742 parse_flags = PARSE_FLAG_ASM_COMMENTS;
743 if (do_preprocess)
744 parse_flags |= PARSE_FLAG_PREPROCESS;
745 next();
746 for(;;) {
747 if (tok == TOK_EOF)
748 break;
749 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
750 redo:
751 if (tok == '#') {
752 /* horrible gas comment */
753 while (tok != TOK_LINEFEED)
754 next();
755 } else if (tok == '.') {
756 asm_parse_directive(s1);
757 } else if (tok == TOK_PPNUM) {
758 const char *p;
759 int n;
760 p = tokc.cstr->data;
761 n = strtoul(p, (char **)&p, 10);
762 if (*p != '\0')
763 expect("':'");
764 /* new local label */
765 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
766 next();
767 skip(':');
768 goto redo;
769 } else if (tok >= TOK_IDENT) {
770 /* instruction or label */
771 opcode = tok;
772 next();
773 if (tok == ':') {
774 /* new label */
775 asm_new_label(s1, opcode, 0);
776 next();
777 goto redo;
778 } else if (tok == '=') {
779 int n;
780 next();
781 n = asm_int_expr(s1);
782 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
783 goto redo;
784 } else {
785 asm_opcode(s1, opcode);
786 }
787 }
788 /* end of line */
789 if (tok != ';' && tok != TOK_LINEFEED){
790 expect("end of line");
791 }
792 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
793 next();
794 }
795
796 asm_free_labels(s1);
797
798 return 0;
799 }
800
801 /* Assemble the current file */
802 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
803 {
804 Sym *define_start;
805 int ret;
806
807 preprocess_init(s1);
808
809 /* default section is text */
810 cur_text_section = text_section;
811 ind = cur_text_section->data_offset;
812
813 define_start = define_stack;
814
815 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
816 symbols can be safely used */
817 put_elf_sym(symtab_section, 0, 0,
818 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
819 SHN_ABS, file->filename);
820
821 ret = tcc_assemble_internal(s1, do_preprocess);
822
823 cur_text_section->data_offset = ind;
824
825 free_defines(define_start);
826
827 return ret;
828 }
829
830 /********************************************************************/
831 /* GCC inline asm support */
832
833 /* assemble the string 'str' in the current C compilation unit without
834 C preprocessing. NOTE: str is modified by modifying the '\0' at the
835 end */
836 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
837 {
838 int saved_parse_flags;
839 const int *saved_macro_ptr;
840
841 saved_parse_flags = parse_flags;
842 saved_macro_ptr = macro_ptr;
843
844 tcc_open_bf(s1, ":asm:", len);
845 memcpy(file->buffer, str, len);
846
847 macro_ptr = NULL;
848 tcc_assemble_internal(s1, 0);
849 tcc_close();
850
851 parse_flags = saved_parse_flags;
852 macro_ptr = saved_macro_ptr;
853 }
854
855 /* find a constraint by its number or id (gcc 3 extended
856 syntax). return -1 if not found. Return in *pp in char after the
857 constraint */
858 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
859 const char *name, const char **pp)
860 {
861 int index;
862 TokenSym *ts;
863 const char *p;
864
865 if (isnum(*name)) {
866 index = 0;
867 while (isnum(*name)) {
868 index = (index * 10) + (*name) - '0';
869 name++;
870 }
871 if ((unsigned)index >= nb_operands)
872 index = -1;
873 } else if (*name == '[') {
874 name++;
875 p = strchr(name, ']');
876 if (p) {
877 ts = tok_alloc(name, p - name);
878 for(index = 0; index < nb_operands; index++) {
879 if (operands[index].id == ts->tok)
880 goto found;
881 }
882 index = -1;
883 found:
884 name = p + 1;
885 } else {
886 index = -1;
887 }
888 } else {
889 index = -1;
890 }
891 if (pp)
892 *pp = name;
893 return index;
894 }
895
896 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
897 int nb_outputs,
898 CString *out_str, CString *in_str)
899 {
900 int c, index, modifier;
901 const char *str;
902 ASMOperand *op;
903 SValue sv;
904
905 cstr_new(out_str);
906 str = in_str->data;
907 for(;;) {
908 c = *str++;
909 if (c == '%') {
910 if (*str == '%') {
911 str++;
912 goto add_char;
913 }
914 modifier = 0;
915 if (*str == 'c' || *str == 'n' ||
916 *str == 'b' || *str == 'w' || *str == 'h')
917 modifier = *str++;
918 index = find_constraint(operands, nb_operands, str, &str);
919 if (index < 0)
920 tcc_error("invalid operand reference after %%");
921 op = &operands[index];
922 sv = *op->vt;
923 if (op->reg >= 0) {
924 sv.r = op->reg;
925 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
926 sv.r |= VT_LVAL;
927 }
928 subst_asm_operand(out_str, &sv, modifier);
929 } else {
930 add_char:
931 cstr_ccat(out_str, c);
932 if (c == '\0')
933 break;
934 }
935 }
936 }
937
938
939 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
940 int is_output)
941 {
942 ASMOperand *op;
943 int nb_operands;
944
945 if (tok != ':') {
946 nb_operands = *nb_operands_ptr;
947 for(;;) {
948 if (nb_operands >= MAX_ASM_OPERANDS)
949 tcc_error("too many asm operands");
950 op = &operands[nb_operands++];
951 op->id = 0;
952 if (tok == '[') {
953 next();
954 if (tok < TOK_IDENT)
955 expect("identifier");
956 op->id = tok;
957 next();
958 skip(']');
959 }
960 if (tok != TOK_STR)
961 expect("string constant");
962 op->constraint = tcc_malloc(tokc.cstr->size);
963 strcpy(op->constraint, tokc.cstr->data);
964 next();
965 skip('(');
966 gexpr();
967 if (is_output) {
968 test_lvalue();
969 } else {
970 /* we want to avoid LLOCAL case, except when the 'm'
971 constraint is used. Note that it may come from
972 register storage, so we need to convert (reg)
973 case */
974 if ((vtop->r & VT_LVAL) &&
975 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
976 (vtop->r & VT_VALMASK) < VT_CONST) &&
977 !strchr(op->constraint, 'm')) {
978 gv(RC_INT);
979 }
980 }
981 op->vt = vtop;
982 skip(')');
983 if (tok == ',') {
984 next();
985 } else {
986 break;
987 }
988 }
989 *nb_operands_ptr = nb_operands;
990 }
991 }
992
993 /* parse the GCC asm() instruction */
994 ST_FUNC void asm_instr(void)
995 {
996 CString astr, astr1;
997 ASMOperand operands[MAX_ASM_OPERANDS];
998 int nb_outputs, nb_operands, i, must_subst, out_reg;
999 uint8_t clobber_regs[NB_ASM_REGS];
1000
1001 next();
1002 /* since we always generate the asm() instruction, we can ignore
1003 volatile */
1004 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1005 next();
1006 }
1007 parse_asm_str(&astr);
1008 nb_operands = 0;
1009 nb_outputs = 0;
1010 must_subst = 0;
1011 memset(clobber_regs, 0, sizeof(clobber_regs));
1012 if (tok == ':') {
1013 next();
1014 must_subst = 1;
1015 /* output args */
1016 parse_asm_operands(operands, &nb_operands, 1);
1017 nb_outputs = nb_operands;
1018 if (tok == ':') {
1019 next();
1020 if (tok != ')') {
1021 /* input args */
1022 parse_asm_operands(operands, &nb_operands, 0);
1023 if (tok == ':') {
1024 /* clobber list */
1025 /* XXX: handle registers */
1026 next();
1027 for(;;) {
1028 if (tok != TOK_STR)
1029 expect("string constant");
1030 asm_clobber(clobber_regs, tokc.cstr->data);
1031 next();
1032 if (tok == ',') {
1033 next();
1034 } else {
1035 break;
1036 }
1037 }
1038 }
1039 }
1040 }
1041 }
1042 skip(')');
1043 /* NOTE: we do not eat the ';' so that we can restore the current
1044 token after the assembler parsing */
1045 if (tok != ';')
1046 expect("';'");
1047
1048 /* save all values in the memory */
1049 save_regs(0);
1050
1051 /* compute constraints */
1052 asm_compute_constraints(operands, nb_operands, nb_outputs,
1053 clobber_regs, &out_reg);
1054
1055 /* substitute the operands in the asm string. No substitution is
1056 done if no operands (GCC behaviour) */
1057 #ifdef ASM_DEBUG
1058 printf("asm: \"%s\"\n", (char *)astr.data);
1059 #endif
1060 if (must_subst) {
1061 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1062 cstr_free(&astr);
1063 } else {
1064 astr1 = astr;
1065 }
1066 #ifdef ASM_DEBUG
1067 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1068 #endif
1069
1070 /* generate loads */
1071 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1072 clobber_regs, out_reg);
1073
1074 /* assemble the string with tcc internal assembler */
1075 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1076
1077 /* restore the current C token */
1078 next();
1079
1080 /* store the output values if needed */
1081 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1082 clobber_regs, out_reg);
1083
1084 /* free everything */
1085 for(i=0;i<nb_operands;i++) {
1086 ASMOperand *op;
1087 op = &operands[i];
1088 tcc_free(op->constraint);
1089 vpop();
1090 }
1091 cstr_free(&astr1);
1092 }
1093
1094 ST_FUNC void asm_global_instr(void)
1095 {
1096 CString astr;
1097
1098 next();
1099 parse_asm_str(&astr);
1100 skip(')');
1101 /* NOTE: we do not eat the ';' so that we can restore the current
1102 token after the assembler parsing */
1103 if (tok != ';')
1104 expect("';'");
1105
1106 #ifdef ASM_DEBUG
1107 printf("asm_global: \"%s\"\n", (char *)astr.data);
1108 #endif
1109 cur_text_section = text_section;
1110 ind = cur_text_section->data_offset;
1111
1112 /* assemble the string with tcc internal assembler */
1113 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1114
1115 cur_text_section->data_offset = ind;
1116
1117 /* restore the current C token */
1118 next();
1119
1120 cstr_free(&astr);
1121 }
1122 #endif /* CONFIG_TCC_ASM */