Add support of asm label for functions.
[tinycc.git] / tccasm.c
blob20d1fbcc6f2ca7d93f47a30a4f666464c0ab1de5
1 /*
2 * GAS like assembler for TCC
3 *
4 * Copyright (c) 2001-2004 Fabrice Bellard
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.
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.
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
21 #ifdef CONFIG_TCC_ASM
23 #include "tcc.h"
25 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
27 char buf[64];
28 TokenSym *ts;
30 snprintf(buf, sizeof(buf), "L..%u", n);
31 ts = tok_alloc(buf, strlen(buf));
32 return ts->tok;
35 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
37 /* We do not use the C expression parser to handle symbols. Maybe the
38 C expression parser could be tweaked to do so. */
40 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
42 Sym *sym;
43 int op, n, label;
44 const char *p;
46 switch(tok) {
47 case TOK_PPNUM:
48 p = tokc.cstr->data;
49 n = strtoul(p, (char **)&p, 0);
50 if (*p == 'b' || *p == 'f') {
51 /* backward or forward label */
52 label = asm_get_local_label_name(s1, n);
53 sym = label_find(label);
54 if (*p == 'b') {
55 /* backward : find the last corresponding defined label */
56 if (sym && sym->r == 0)
57 sym = sym->prev_tok;
58 if (!sym)
59 error("local label '%d' not found backward", n);
60 } else {
61 /* forward */
62 if (!sym || sym->r) {
63 /* if the last label is defined, then define a new one */
64 sym = label_push(&s1->asm_labels, label, 0);
65 sym->type.t = VT_STATIC | VT_VOID;
68 pe->v = 0;
69 pe->sym = sym;
70 } else if (*p == '\0') {
71 pe->v = n;
72 pe->sym = NULL;
73 } else {
74 error("invalid number syntax");
76 next();
77 break;
78 case '+':
79 next();
80 asm_expr_unary(s1, pe);
81 break;
82 case '-':
83 case '~':
84 op = tok;
85 next();
86 asm_expr_unary(s1, pe);
87 if (pe->sym)
88 error("invalid operation with label");
89 if (op == '-')
90 pe->v = -pe->v;
91 else
92 pe->v = ~pe->v;
93 break;
94 case TOK_CCHAR:
95 case TOK_LCHAR:
96 pe->v = tokc.i;
97 pe->sym = NULL;
98 next();
99 break;
100 case '(':
101 next();
102 asm_expr(s1, pe);
103 skip(')');
104 break;
105 default:
106 if (tok >= TOK_IDENT) {
107 /* label case : if the label was not found, add one */
108 sym = label_find(tok);
109 if (!sym) {
110 sym = label_push(&s1->asm_labels, tok, 0);
111 /* NOTE: by default, the symbol is global */
112 sym->type.t = VT_VOID;
114 if (sym->r == SHN_ABS) {
115 /* if absolute symbol, no need to put a symbol value */
116 pe->v = sym->jnext;
117 pe->sym = NULL;
118 } else {
119 pe->v = 0;
120 pe->sym = sym;
122 next();
123 } else {
124 error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
126 break;
130 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
132 int op;
133 ExprValue e2;
135 asm_expr_unary(s1, pe);
136 for(;;) {
137 op = tok;
138 if (op != '*' && op != '/' && op != '%' &&
139 op != TOK_SHL && op != TOK_SAR)
140 break;
141 next();
142 asm_expr_unary(s1, &e2);
143 if (pe->sym || e2.sym)
144 error("invalid operation with label");
145 switch(op) {
146 case '*':
147 pe->v *= e2.v;
148 break;
149 case '/':
150 if (e2.v == 0) {
151 div_error:
152 error("division by zero");
154 pe->v /= e2.v;
155 break;
156 case '%':
157 if (e2.v == 0)
158 goto div_error;
159 pe->v %= e2.v;
160 break;
161 case TOK_SHL:
162 pe->v <<= e2.v;
163 break;
164 default:
165 case TOK_SAR:
166 pe->v >>= e2.v;
167 break;
172 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
174 int op;
175 ExprValue e2;
177 asm_expr_prod(s1, pe);
178 for(;;) {
179 op = tok;
180 if (op != '&' && op != '|' && op != '^')
181 break;
182 next();
183 asm_expr_prod(s1, &e2);
184 if (pe->sym || e2.sym)
185 error("invalid operation with label");
186 switch(op) {
187 case '&':
188 pe->v &= e2.v;
189 break;
190 case '|':
191 pe->v |= e2.v;
192 break;
193 default:
194 case '^':
195 pe->v ^= e2.v;
196 break;
201 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
203 int op;
204 ExprValue e2;
206 asm_expr_logic(s1, pe);
207 for(;;) {
208 op = tok;
209 if (op != '+' && op != '-')
210 break;
211 next();
212 asm_expr_logic(s1, &e2);
213 if (op == '+') {
214 if (pe->sym != NULL && e2.sym != NULL)
215 goto cannot_relocate;
216 pe->v += e2.v;
217 if (pe->sym == NULL && e2.sym != NULL)
218 pe->sym = e2.sym;
219 } else {
220 pe->v -= e2.v;
221 /* NOTE: we are less powerful than gas in that case
222 because we store only one symbol in the expression */
223 if (!pe->sym && !e2.sym) {
224 /* OK */
225 } else if (pe->sym && !e2.sym) {
226 /* OK */
227 } else if (pe->sym && e2.sym) {
228 if (pe->sym == e2.sym) {
229 /* OK */
230 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
231 /* we also accept defined symbols in the same section */
232 pe->v += pe->sym->jnext - e2.sym->jnext;
233 } else {
234 goto cannot_relocate;
236 pe->sym = NULL; /* same symbols can be substracted to NULL */
237 } else {
238 cannot_relocate:
239 error("invalid operation with label");
245 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
247 asm_expr_sum(s1, pe);
250 ST_FUNC int asm_int_expr(TCCState *s1)
252 ExprValue e;
253 asm_expr(s1, &e);
254 if (e.sym)
255 expect("constant");
256 return e.v;
259 /* NOTE: the same name space as C labels is used to avoid using too
260 much memory when storing labels in TokenStrings */
261 static void asm_new_label1(TCCState *s1, int label, int is_local,
262 int sh_num, int value)
264 Sym *sym;
266 sym = label_find(label);
267 if (sym) {
268 if (sym->r) {
269 /* the label is already defined */
270 if (!is_local) {
271 error("assembler label '%s' already defined",
272 get_tok_str(label, NULL));
273 } else {
274 /* redefinition of local labels is possible */
275 goto new_label;
278 } else {
279 new_label:
280 sym = label_push(&s1->asm_labels, label, 0);
281 sym->type.t = VT_STATIC | VT_VOID;
283 sym->r = sh_num;
284 sym->jnext = value;
287 static void asm_new_label(TCCState *s1, int label, int is_local)
289 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
292 static void asm_free_labels(TCCState *st)
294 Sym *s, *s1;
295 Section *sec;
297 for(s = st->asm_labels; s != NULL; s = s1) {
298 s1 = s->prev;
299 /* define symbol value in object file */
300 if (s->r) {
301 if (s->r == SHN_ABS)
302 sec = SECTION_ABS;
303 else
304 sec = st->sections[s->r];
305 put_extern_sym2(s, sec, s->jnext, 0, 0);
307 /* remove label */
308 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
309 sym_free(s);
311 st->asm_labels = NULL;
314 static void use_section1(TCCState *s1, Section *sec)
316 cur_text_section->data_offset = ind;
317 cur_text_section = sec;
318 ind = cur_text_section->data_offset;
321 static void use_section(TCCState *s1, const char *name)
323 Section *sec;
324 sec = find_section(s1, name);
325 use_section1(s1, sec);
328 static void asm_parse_directive(TCCState *s1)
330 int n, offset, v, size, tok1;
331 Section *sec;
332 uint8_t *ptr;
334 /* assembler directive */
335 next();
336 sec = cur_text_section;
337 switch(tok) {
338 case TOK_ASM_align:
339 case TOK_ASM_skip:
340 case TOK_ASM_space:
341 tok1 = tok;
342 next();
343 n = asm_int_expr(s1);
344 if (tok1 == TOK_ASM_align) {
345 if (n < 0 || (n & (n-1)) != 0)
346 error("alignment must be a positive power of two");
347 offset = (ind + n - 1) & -n;
348 size = offset - ind;
349 /* the section must have a compatible alignment */
350 if (sec->sh_addralign < n)
351 sec->sh_addralign = n;
352 } else {
353 size = n;
355 v = 0;
356 if (tok == ',') {
357 next();
358 v = asm_int_expr(s1);
360 zero_pad:
361 if (sec->sh_type != SHT_NOBITS) {
362 sec->data_offset = ind;
363 ptr = section_ptr_add(sec, size);
364 memset(ptr, v, size);
366 ind += size;
367 break;
368 case TOK_ASM_quad:
369 next();
370 for(;;) {
371 uint64_t vl;
372 const char *p;
374 p = tokc.cstr->data;
375 if (tok != TOK_PPNUM) {
376 error_constant:
377 error("64 bit constant");
379 vl = strtoll(p, (char **)&p, 0);
380 if (*p != '\0')
381 goto error_constant;
382 next();
383 if (sec->sh_type != SHT_NOBITS) {
384 /* XXX: endianness */
385 gen_le32(vl);
386 gen_le32(vl >> 32);
387 } else {
388 ind += 8;
390 if (tok != ',')
391 break;
392 next();
394 break;
395 case TOK_ASM_byte:
396 size = 1;
397 goto asm_data;
398 case TOK_ASM_word:
399 case TOK_SHORT:
400 size = 2;
401 goto asm_data;
402 case TOK_LONG:
403 case TOK_INT:
404 size = 4;
405 asm_data:
406 next();
407 for(;;) {
408 ExprValue e;
409 asm_expr(s1, &e);
410 if (sec->sh_type != SHT_NOBITS) {
411 if (size == 4) {
412 gen_expr32(&e);
413 } else {
414 if (e.sym)
415 expect("constant");
416 if (size == 1)
417 g(e.v);
418 else
419 gen_le16(e.v);
421 } else {
422 ind += size;
424 if (tok != ',')
425 break;
426 next();
428 break;
429 case TOK_ASM_fill:
431 int repeat, size, val, i, j;
432 uint8_t repeat_buf[8];
433 next();
434 repeat = asm_int_expr(s1);
435 if (repeat < 0) {
436 error("repeat < 0; .fill ignored");
437 break;
439 size = 1;
440 val = 0;
441 if (tok == ',') {
442 next();
443 size = asm_int_expr(s1);
444 if (size < 0) {
445 error("size < 0; .fill ignored");
446 break;
448 if (size > 8)
449 size = 8;
450 if (tok == ',') {
451 next();
452 val = asm_int_expr(s1);
455 /* XXX: endianness */
456 repeat_buf[0] = val;
457 repeat_buf[1] = val >> 8;
458 repeat_buf[2] = val >> 16;
459 repeat_buf[3] = val >> 24;
460 repeat_buf[4] = 0;
461 repeat_buf[5] = 0;
462 repeat_buf[6] = 0;
463 repeat_buf[7] = 0;
464 for(i = 0; i < repeat; i++) {
465 for(j = 0; j < size; j++) {
466 g(repeat_buf[j]);
470 break;
471 case TOK_ASM_org:
473 unsigned long n;
474 next();
475 /* XXX: handle section symbols too */
476 n = asm_int_expr(s1);
477 if (n < ind)
478 error("attempt to .org backwards");
479 v = 0;
480 size = n - ind;
481 goto zero_pad;
483 break;
484 case TOK_ASM_globl:
485 case TOK_ASM_global:
487 Sym *sym;
489 next();
490 sym = label_find(tok);
491 if (!sym) {
492 sym = label_push(&s1->asm_labels, tok, 0);
493 sym->type.t = VT_VOID;
495 sym->type.t &= ~VT_STATIC;
496 next();
498 break;
499 case TOK_ASM_string:
500 case TOK_ASM_ascii:
501 case TOK_ASM_asciz:
503 const uint8_t *p;
504 int i, size, t;
506 t = tok;
507 next();
508 for(;;) {
509 if (tok != TOK_STR)
510 expect("string constant");
511 p = tokc.cstr->data;
512 size = tokc.cstr->size;
513 if (t == TOK_ASM_ascii && size > 0)
514 size--;
515 for(i = 0; i < size; i++)
516 g(p[i]);
517 next();
518 if (tok == ',') {
519 next();
520 } else if (tok != TOK_STR) {
521 break;
525 break;
526 case TOK_ASM_text:
527 case TOK_ASM_data:
528 case TOK_ASM_bss:
530 char sname[64];
531 tok1 = tok;
532 n = 0;
533 next();
534 if (tok != ';' && tok != TOK_LINEFEED) {
535 n = asm_int_expr(s1);
536 next();
538 sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
539 use_section(s1, sname);
541 break;
542 case TOK_ASM_file:
544 char filename[512];
546 filename[0] = '\0';
547 next();
549 if (tok == TOK_STR)
550 pstrcat(filename, sizeof(filename), tokc.cstr->data);
551 else
552 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
554 if (s1->warn_unsupported)
555 warning("ignoring .file %s", filename);
557 next();
559 break;
560 case TOK_ASM_ident:
562 char ident[256];
564 ident[0] = '\0';
565 next();
567 if (tok == TOK_STR)
568 pstrcat(ident, sizeof(ident), tokc.cstr->data);
569 else
570 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
572 if (s1->warn_unsupported)
573 warning("ignoring .ident %s", ident);
575 next();
577 break;
578 case TOK_ASM_size:
580 Sym *sym;
582 next();
583 sym = label_find(tok);
584 if (!sym) {
585 error("label not found: %s", get_tok_str(tok, NULL));
588 next();
589 skip(',');
590 /* XXX .size name,label2-label1 */
591 if (s1->warn_unsupported)
592 warning("ignoring .size %s,*", get_tok_str(tok, NULL));
594 while (tok != '\n' && tok != CH_EOF) {
595 next();
598 break;
599 case TOK_ASM_type:
601 Sym *sym;
602 char newtype[64];
603 newtype[0] = 0;
605 next();
606 sym = label_find(tok);
607 if (!sym) {
608 sym = label_push(&s1->asm_labels, tok, 0);
609 sym->type.t = VT_VOID;
612 next();
613 skip(',');
614 skip('@');
615 if (tok == TOK_STR)
616 pstrcat(newtype, sizeof(newtype), tokc.cstr->data);
617 else
618 pstrcat(newtype, sizeof(newtype), get_tok_str(tok, NULL));
620 if (!strcmp(newtype, "function")) {
621 sym->type.t = VT_FUNC;
623 else if (s1->warn_unsupported)
624 warning("change type of '%s' from 0x%x to '%s' ignored",
625 get_tok_str(sym->v, NULL), sym->type.t, newtype);
627 next();
629 break;
630 case TOK_SECTION1:
632 char sname[256];
634 /* XXX: support more options */
635 next();
636 sname[0] = '\0';
637 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
638 if (tok == TOK_STR)
639 pstrcat(sname, sizeof(sname), tokc.cstr->data);
640 else
641 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
642 next();
644 if (tok == ',') {
645 /* skip section options */
646 next();
647 if (tok != TOK_STR)
648 expect("string constant");
649 next();
651 last_text_section = cur_text_section;
652 use_section(s1, sname);
654 break;
655 case TOK_ASM_previous:
657 Section *sec;
658 next();
659 if (!last_text_section)
660 error("no previous section referenced");
661 sec = cur_text_section;
662 use_section1(s1, last_text_section);
663 last_text_section = sec;
665 break;
666 #ifdef TCC_TARGET_I386
667 case TOK_ASM_code16:
669 next();
670 s1->seg_size = 16;
672 break;
673 case TOK_ASM_code32:
675 next();
676 s1->seg_size = 32;
678 break;
679 #endif
680 #ifdef TCC_TARGET_X86_64
681 /* added for compatibility with GAS */
682 case TOK_ASM_code64:
683 next();
684 break;
685 #endif
686 default:
687 error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
688 break;
693 /* assemble a file */
694 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
696 int opcode;
698 #if 0
699 /* print stats about opcodes */
701 const ASMInstr *pa;
702 int freq[4];
703 int op_vals[500];
704 int nb_op_vals, i, j;
706 nb_op_vals = 0;
707 memset(freq, 0, sizeof(freq));
708 for(pa = asm_instrs; pa->sym != 0; pa++) {
709 freq[pa->nb_ops]++;
710 for(i=0;i<pa->nb_ops;i++) {
711 for(j=0;j<nb_op_vals;j++) {
712 if (pa->op_type[i] == op_vals[j])
713 goto found;
715 op_vals[nb_op_vals++] = pa->op_type[i];
716 found: ;
719 for(i=0;i<nb_op_vals;i++) {
720 int v = op_vals[i];
721 if ((v & (v - 1)) != 0)
722 printf("%3d: %08x\n", i, v);
724 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
725 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
726 freq[0], freq[1], freq[2], freq[3]);
728 #endif
730 /* XXX: undefine C labels */
732 ch = file->buf_ptr[0];
733 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
734 parse_flags = PARSE_FLAG_ASM_COMMENTS;
735 if (do_preprocess)
736 parse_flags |= PARSE_FLAG_PREPROCESS;
737 next();
738 for(;;) {
739 if (tok == TOK_EOF)
740 break;
741 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
742 redo:
743 if (tok == '#') {
744 /* horrible gas comment */
745 while (tok != TOK_LINEFEED)
746 next();
747 } else if (tok == '.') {
748 asm_parse_directive(s1);
749 } else if (tok == TOK_PPNUM) {
750 const char *p;
751 int n;
752 p = tokc.cstr->data;
753 n = strtoul(p, (char **)&p, 10);
754 if (*p != '\0')
755 expect("':'");
756 /* new local label */
757 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
758 next();
759 skip(':');
760 goto redo;
761 } else if (tok >= TOK_IDENT) {
762 /* instruction or label */
763 opcode = tok;
764 next();
765 if (tok == ':') {
766 /* new label */
767 asm_new_label(s1, opcode, 0);
768 next();
769 goto redo;
770 } else if (tok == '=') {
771 int n;
772 next();
773 n = asm_int_expr(s1);
774 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
775 goto redo;
776 } else {
777 asm_opcode(s1, opcode);
780 /* end of line */
781 if (tok != ';' && tok != TOK_LINEFEED){
782 expect("end of line");
784 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
785 next();
788 asm_free_labels(s1);
790 return 0;
793 /* Assemble the current file */
794 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
796 Sym *define_start;
797 int ret;
799 preprocess_init(s1);
801 /* default section is text */
802 cur_text_section = text_section;
803 ind = cur_text_section->data_offset;
805 define_start = define_stack;
807 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
808 symbols can be safely used */
809 put_elf_sym(symtab_section, 0, 0,
810 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
811 SHN_ABS, file->filename);
813 ret = tcc_assemble_internal(s1, do_preprocess);
815 cur_text_section->data_offset = ind;
817 free_defines(define_start);
819 return ret;
822 /********************************************************************/
823 /* GCC inline asm support */
825 /* assemble the string 'str' in the current C compilation unit without
826 C preprocessing. NOTE: str is modified by modifying the '\0' at the
827 end */
828 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
830 BufferedFile *bf, *saved_file;
831 int saved_parse_flags;
832 const int *saved_macro_ptr;
834 bf = tcc_malloc(sizeof(BufferedFile));
835 memset(bf, 0, sizeof(BufferedFile));
836 bf->fd = -1;
837 bf->buf_ptr = str;
838 bf->buf_end = str + len;
839 str[len] = CH_EOB;
840 /* same name as current file so that errors are correctly
841 reported */
842 pstrcpy(bf->filename, sizeof(bf->filename), file->filename);
843 bf->line_num = file->line_num;
844 saved_file = file;
845 file = bf;
846 saved_parse_flags = parse_flags;
847 saved_macro_ptr = macro_ptr;
848 macro_ptr = NULL;
850 tcc_assemble_internal(s1, 0);
852 parse_flags = saved_parse_flags;
853 macro_ptr = saved_macro_ptr;
854 file = saved_file;
855 tcc_free(bf);
858 /* find a constraint by its number or id (gcc 3 extended
859 syntax). return -1 if not found. Return in *pp in char after the
860 constraint */
861 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
862 const char *name, const char **pp)
864 int index;
865 TokenSym *ts;
866 const char *p;
868 if (isnum(*name)) {
869 index = 0;
870 while (isnum(*name)) {
871 index = (index * 10) + (*name) - '0';
872 name++;
874 if ((unsigned)index >= nb_operands)
875 index = -1;
876 } else if (*name == '[') {
877 name++;
878 p = strchr(name, ']');
879 if (p) {
880 ts = tok_alloc(name, p - name);
881 for(index = 0; index < nb_operands; index++) {
882 if (operands[index].id == ts->tok)
883 goto found;
885 index = -1;
886 found:
887 name = p + 1;
888 } else {
889 index = -1;
891 } else {
892 index = -1;
894 if (pp)
895 *pp = name;
896 return index;
899 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
900 int nb_outputs,
901 CString *out_str, CString *in_str)
903 int c, index, modifier;
904 const char *str;
905 ASMOperand *op;
906 SValue sv;
908 cstr_new(out_str);
909 str = in_str->data;
910 for(;;) {
911 c = *str++;
912 if (c == '%') {
913 if (*str == '%') {
914 str++;
915 goto add_char;
917 modifier = 0;
918 if (*str == 'c' || *str == 'n' ||
919 *str == 'b' || *str == 'w' || *str == 'h')
920 modifier = *str++;
921 index = find_constraint(operands, nb_operands, str, &str);
922 if (index < 0)
923 error("invalid operand reference after %%");
924 op = &operands[index];
925 sv = *op->vt;
926 if (op->reg >= 0) {
927 sv.r = op->reg;
928 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
929 sv.r |= VT_LVAL;
931 subst_asm_operand(out_str, &sv, modifier);
932 } else {
933 add_char:
934 cstr_ccat(out_str, c);
935 if (c == '\0')
936 break;
942 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
943 int is_output)
945 ASMOperand *op;
946 int nb_operands;
948 if (tok != ':') {
949 nb_operands = *nb_operands_ptr;
950 for(;;) {
951 if (nb_operands >= MAX_ASM_OPERANDS)
952 error("too many asm operands");
953 op = &operands[nb_operands++];
954 op->id = 0;
955 if (tok == '[') {
956 next();
957 if (tok < TOK_IDENT)
958 expect("identifier");
959 op->id = tok;
960 next();
961 skip(']');
963 if (tok != TOK_STR)
964 expect("string constant");
965 op->constraint = tcc_malloc(tokc.cstr->size);
966 strcpy(op->constraint, tokc.cstr->data);
967 next();
968 skip('(');
969 gexpr();
970 if (is_output) {
971 test_lvalue();
972 } else {
973 /* we want to avoid LLOCAL case, except when the 'm'
974 constraint is used. Note that it may come from
975 register storage, so we need to convert (reg)
976 case */
977 if ((vtop->r & VT_LVAL) &&
978 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
979 (vtop->r & VT_VALMASK) < VT_CONST) &&
980 !strchr(op->constraint, 'm')) {
981 gv(RC_INT);
984 op->vt = vtop;
985 skip(')');
986 if (tok == ',') {
987 next();
988 } else {
989 break;
992 *nb_operands_ptr = nb_operands;
996 #endif
998 static void parse_asm_str(CString *astr)
1000 skip('(');
1001 /* read the string */
1002 if (tok != TOK_STR)
1003 expect("string constant");
1004 cstr_new(astr);
1005 while (tok == TOK_STR) {
1006 /* XXX: add \0 handling too ? */
1007 cstr_cat(astr, tokc.cstr->data);
1008 next();
1010 cstr_ccat(astr, '\0');
1013 /* Parse an asm label and return the label
1014 * Don't forget to free the CString in the caller! */
1015 static void asm_label_instr(CString *astr)
1017 next();
1018 parse_asm_str(astr);
1019 skip(')');
1020 #ifdef ASM_DEBUG
1021 printf("asm_alias: \"%s\"\n", (char *)astr->data);
1022 #endif
1025 #ifdef CONFIG_TCC_ASM
1027 /* parse the GCC asm() instruction */
1028 ST_FUNC void asm_instr(void)
1030 CString astr, astr1;
1031 ASMOperand operands[MAX_ASM_OPERANDS];
1032 int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg;
1033 uint8_t clobber_regs[NB_ASM_REGS];
1035 next();
1036 /* since we always generate the asm() instruction, we can ignore
1037 volatile */
1038 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1039 next();
1041 parse_asm_str(&astr);
1042 nb_operands = 0;
1043 nb_outputs = 0;
1044 must_subst = 0;
1045 memset(clobber_regs, 0, sizeof(clobber_regs));
1046 if (tok == ':') {
1047 next();
1048 must_subst = 1;
1049 /* output args */
1050 parse_asm_operands(operands, &nb_operands, 1);
1051 nb_outputs = nb_operands;
1052 if (tok == ':') {
1053 next();
1054 if (tok != ')') {
1055 /* input args */
1056 parse_asm_operands(operands, &nb_operands, 0);
1057 if (tok == ':') {
1058 /* clobber list */
1059 /* XXX: handle registers */
1060 next();
1061 for(;;) {
1062 if (tok != TOK_STR)
1063 expect("string constant");
1064 asm_clobber(clobber_regs, tokc.cstr->data);
1065 next();
1066 if (tok == ',') {
1067 next();
1068 } else {
1069 break;
1076 skip(')');
1077 /* NOTE: we do not eat the ';' so that we can restore the current
1078 token after the assembler parsing */
1079 if (tok != ';')
1080 expect("';'");
1081 nb_inputs = nb_operands - nb_outputs;
1083 /* save all values in the memory */
1084 save_regs(0);
1086 /* compute constraints */
1087 asm_compute_constraints(operands, nb_operands, nb_outputs,
1088 clobber_regs, &out_reg);
1090 /* substitute the operands in the asm string. No substitution is
1091 done if no operands (GCC behaviour) */
1092 #ifdef ASM_DEBUG
1093 printf("asm: \"%s\"\n", (char *)astr.data);
1094 #endif
1095 if (must_subst) {
1096 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1097 cstr_free(&astr);
1098 } else {
1099 astr1 = astr;
1101 #ifdef ASM_DEBUG
1102 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1103 #endif
1105 /* generate loads */
1106 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1107 clobber_regs, out_reg);
1109 /* assemble the string with tcc internal assembler */
1110 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1112 /* restore the current C token */
1113 next();
1115 /* store the output values if needed */
1116 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1117 clobber_regs, out_reg);
1119 /* free everything */
1120 for(i=0;i<nb_operands;i++) {
1121 ASMOperand *op;
1122 op = &operands[i];
1123 tcc_free(op->constraint);
1124 vpop();
1126 cstr_free(&astr1);
1129 ST_FUNC void asm_global_instr(void)
1131 CString astr;
1133 next();
1134 parse_asm_str(&astr);
1135 skip(')');
1136 /* NOTE: we do not eat the ';' so that we can restore the current
1137 token after the assembler parsing */
1138 if (tok != ';')
1139 expect("';'");
1141 #ifdef ASM_DEBUG
1142 printf("asm_global: \"%s\"\n", (char *)astr.data);
1143 #endif
1144 cur_text_section = text_section;
1145 ind = cur_text_section->data_offset;
1147 /* assemble the string with tcc internal assembler */
1148 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1150 cur_text_section->data_offset = ind;
1152 /* restore the current C token */
1153 next();
1155 cstr_free(&astr);
1158 #endif