tccasm: Detect (but ignore) .ident directive
[tinycc.git] / tccasm.c
blobb5b2fff3717d4714da01bba491696bbee8ff82a5
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 #include "tcc.h"
23 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
25 char buf[64];
26 TokenSym *ts;
28 snprintf(buf, sizeof(buf), "L..%u", n);
29 ts = tok_alloc(buf, strlen(buf));
30 return ts->tok;
33 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
35 /* We do not use the C expression parser to handle symbols. Maybe the
36 C expression parser could be tweaked to do so. */
38 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
40 Sym *sym;
41 int op, n, label;
42 const char *p;
44 switch(tok) {
45 case TOK_PPNUM:
46 p = tokc.cstr->data;
47 n = strtoul(p, (char **)&p, 0);
48 if (*p == 'b' || *p == 'f') {
49 /* backward or forward label */
50 label = asm_get_local_label_name(s1, n);
51 sym = label_find(label);
52 if (*p == 'b') {
53 /* backward : find the last corresponding defined label */
54 if (sym && sym->r == 0)
55 sym = sym->prev_tok;
56 if (!sym)
57 error("local label '%d' not found backward", n);
58 } else {
59 /* forward */
60 if (!sym || sym->r) {
61 /* if the last label is defined, then define a new one */
62 sym = label_push(&s1->asm_labels, label, 0);
63 sym->type.t = VT_STATIC | VT_VOID;
66 pe->v = 0;
67 pe->sym = sym;
68 } else if (*p == '\0') {
69 pe->v = n;
70 pe->sym = NULL;
71 } else {
72 error("invalid number syntax");
74 next();
75 break;
76 case '+':
77 next();
78 asm_expr_unary(s1, pe);
79 break;
80 case '-':
81 case '~':
82 op = tok;
83 next();
84 asm_expr_unary(s1, pe);
85 if (pe->sym)
86 error("invalid operation with label");
87 if (op == '-')
88 pe->v = -pe->v;
89 else
90 pe->v = ~pe->v;
91 break;
92 case TOK_CCHAR:
93 case TOK_LCHAR:
94 pe->v = tokc.i;
95 pe->sym = NULL;
96 next();
97 break;
98 case '(':
99 next();
100 asm_expr(s1, pe);
101 skip(')');
102 break;
103 default:
104 if (tok >= TOK_IDENT) {
105 /* label case : if the label was not found, add one */
106 sym = label_find(tok);
107 if (!sym) {
108 sym = label_push(&s1->asm_labels, tok, 0);
109 /* NOTE: by default, the symbol is global */
110 sym->type.t = VT_VOID;
112 if (sym->r == SHN_ABS) {
113 /* if absolute symbol, no need to put a symbol value */
114 pe->v = sym->jnext;
115 pe->sym = NULL;
116 } else {
117 pe->v = 0;
118 pe->sym = sym;
120 next();
121 } else {
122 error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
124 break;
128 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
130 int op;
131 ExprValue e2;
133 asm_expr_unary(s1, pe);
134 for(;;) {
135 op = tok;
136 if (op != '*' && op != '/' && op != '%' &&
137 op != TOK_SHL && op != TOK_SAR)
138 break;
139 next();
140 asm_expr_unary(s1, &e2);
141 if (pe->sym || e2.sym)
142 error("invalid operation with label");
143 switch(op) {
144 case '*':
145 pe->v *= e2.v;
146 break;
147 case '/':
148 if (e2.v == 0) {
149 div_error:
150 error("division by zero");
152 pe->v /= e2.v;
153 break;
154 case '%':
155 if (e2.v == 0)
156 goto div_error;
157 pe->v %= e2.v;
158 break;
159 case TOK_SHL:
160 pe->v <<= e2.v;
161 break;
162 default:
163 case TOK_SAR:
164 pe->v >>= e2.v;
165 break;
170 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
172 int op;
173 ExprValue e2;
175 asm_expr_prod(s1, pe);
176 for(;;) {
177 op = tok;
178 if (op != '&' && op != '|' && op != '^')
179 break;
180 next();
181 asm_expr_prod(s1, &e2);
182 if (pe->sym || e2.sym)
183 error("invalid operation with label");
184 switch(op) {
185 case '&':
186 pe->v &= e2.v;
187 break;
188 case '|':
189 pe->v |= e2.v;
190 break;
191 default:
192 case '^':
193 pe->v ^= e2.v;
194 break;
199 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
201 int op;
202 ExprValue e2;
204 asm_expr_logic(s1, pe);
205 for(;;) {
206 op = tok;
207 if (op != '+' && op != '-')
208 break;
209 next();
210 asm_expr_logic(s1, &e2);
211 if (op == '+') {
212 if (pe->sym != NULL && e2.sym != NULL)
213 goto cannot_relocate;
214 pe->v += e2.v;
215 if (pe->sym == NULL && e2.sym != NULL)
216 pe->sym = e2.sym;
217 } else {
218 pe->v -= e2.v;
219 /* NOTE: we are less powerful than gas in that case
220 because we store only one symbol in the expression */
221 if (!pe->sym && !e2.sym) {
222 /* OK */
223 } else if (pe->sym && !e2.sym) {
224 /* OK */
225 } else if (pe->sym && e2.sym) {
226 if (pe->sym == e2.sym) {
227 /* OK */
228 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
229 /* we also accept defined symbols in the same section */
230 pe->v += pe->sym->jnext - e2.sym->jnext;
231 } else {
232 goto cannot_relocate;
234 pe->sym = NULL; /* same symbols can be substracted to NULL */
235 } else {
236 cannot_relocate:
237 error("invalid operation with label");
243 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
245 asm_expr_sum(s1, pe);
248 ST_FUNC int asm_int_expr(TCCState *s1)
250 ExprValue e;
251 asm_expr(s1, &e);
252 if (e.sym)
253 expect("constant");
254 return e.v;
257 /* NOTE: the same name space as C labels is used to avoid using too
258 much memory when storing labels in TokenStrings */
259 static void asm_new_label1(TCCState *s1, int label, int is_local,
260 int sh_num, int value)
262 Sym *sym;
264 sym = label_find(label);
265 if (sym) {
266 if (sym->r) {
267 /* the label is already defined */
268 if (!is_local) {
269 error("assembler label '%s' already defined",
270 get_tok_str(label, NULL));
271 } else {
272 /* redefinition of local labels is possible */
273 goto new_label;
276 } else {
277 new_label:
278 sym = label_push(&s1->asm_labels, label, 0);
279 sym->type.t = VT_STATIC | VT_VOID;
281 sym->r = sh_num;
282 sym->jnext = value;
285 static void asm_new_label(TCCState *s1, int label, int is_local)
287 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
290 static void asm_free_labels(TCCState *st)
292 Sym *s, *s1;
293 Section *sec;
295 for(s = st->asm_labels; s != NULL; s = s1) {
296 s1 = s->prev;
297 /* define symbol value in object file */
298 if (s->r) {
299 if (s->r == SHN_ABS)
300 sec = SECTION_ABS;
301 else
302 sec = st->sections[s->r];
303 put_extern_sym2(s, sec, s->jnext, 0, 0);
305 /* remove label */
306 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
307 sym_free(s);
309 st->asm_labels = NULL;
312 static void use_section1(TCCState *s1, Section *sec)
314 cur_text_section->data_offset = ind;
315 cur_text_section = sec;
316 ind = cur_text_section->data_offset;
319 static void use_section(TCCState *s1, const char *name)
321 Section *sec;
322 sec = find_section(s1, name);
323 use_section1(s1, sec);
326 static void asm_parse_directive(TCCState *s1)
328 int n, offset, v, size, tok1;
329 Section *sec;
330 uint8_t *ptr;
332 /* assembler directive */
333 next();
334 sec = cur_text_section;
335 switch(tok) {
336 case TOK_ASM_align:
337 case TOK_ASM_skip:
338 case TOK_ASM_space:
339 tok1 = tok;
340 next();
341 n = asm_int_expr(s1);
342 if (tok1 == TOK_ASM_align) {
343 if (n < 0 || (n & (n-1)) != 0)
344 error("alignment must be a positive power of two");
345 offset = (ind + n - 1) & -n;
346 size = offset - ind;
347 /* the section must have a compatible alignment */
348 if (sec->sh_addralign < n)
349 sec->sh_addralign = n;
350 } else {
351 size = n;
353 v = 0;
354 if (tok == ',') {
355 next();
356 v = asm_int_expr(s1);
358 zero_pad:
359 if (sec->sh_type != SHT_NOBITS) {
360 sec->data_offset = ind;
361 ptr = section_ptr_add(sec, size);
362 memset(ptr, v, size);
364 ind += size;
365 break;
366 case TOK_ASM_quad:
367 next();
368 for(;;) {
369 uint64_t vl;
370 const char *p;
372 p = tokc.cstr->data;
373 if (tok != TOK_PPNUM) {
374 error_constant:
375 error("64 bit constant");
377 vl = strtoll(p, (char **)&p, 0);
378 if (*p != '\0')
379 goto error_constant;
380 next();
381 if (sec->sh_type != SHT_NOBITS) {
382 /* XXX: endianness */
383 gen_le32(vl);
384 gen_le32(vl >> 32);
385 } else {
386 ind += 8;
388 if (tok != ',')
389 break;
390 next();
392 break;
393 case TOK_ASM_byte:
394 size = 1;
395 goto asm_data;
396 case TOK_ASM_word:
397 case TOK_SHORT:
398 size = 2;
399 goto asm_data;
400 case TOK_LONG:
401 case TOK_INT:
402 size = 4;
403 asm_data:
404 next();
405 for(;;) {
406 ExprValue e;
407 asm_expr(s1, &e);
408 if (sec->sh_type != SHT_NOBITS) {
409 if (size == 4) {
410 gen_expr32(&e);
411 } else {
412 if (e.sym)
413 expect("constant");
414 if (size == 1)
415 g(e.v);
416 else
417 gen_le16(e.v);
419 } else {
420 ind += size;
422 if (tok != ',')
423 break;
424 next();
426 break;
427 case TOK_ASM_fill:
429 int repeat, size, val, i, j;
430 uint8_t repeat_buf[8];
431 next();
432 repeat = asm_int_expr(s1);
433 if (repeat < 0) {
434 error("repeat < 0; .fill ignored");
435 break;
437 size = 1;
438 val = 0;
439 if (tok == ',') {
440 next();
441 size = asm_int_expr(s1);
442 if (size < 0) {
443 error("size < 0; .fill ignored");
444 break;
446 if (size > 8)
447 size = 8;
448 if (tok == ',') {
449 next();
450 val = asm_int_expr(s1);
453 /* XXX: endianness */
454 repeat_buf[0] = val;
455 repeat_buf[1] = val >> 8;
456 repeat_buf[2] = val >> 16;
457 repeat_buf[3] = val >> 24;
458 repeat_buf[4] = 0;
459 repeat_buf[5] = 0;
460 repeat_buf[6] = 0;
461 repeat_buf[7] = 0;
462 for(i = 0; i < repeat; i++) {
463 for(j = 0; j < size; j++) {
464 g(repeat_buf[j]);
468 break;
469 case TOK_ASM_org:
471 unsigned long n;
472 next();
473 /* XXX: handle section symbols too */
474 n = asm_int_expr(s1);
475 if (n < ind)
476 error("attempt to .org backwards");
477 v = 0;
478 size = n - ind;
479 goto zero_pad;
481 break;
482 case TOK_ASM_globl:
483 case TOK_ASM_global:
485 Sym *sym;
487 next();
488 sym = label_find(tok);
489 if (!sym) {
490 sym = label_push(&s1->asm_labels, tok, 0);
491 sym->type.t = VT_VOID;
493 sym->type.t &= ~VT_STATIC;
494 next();
496 break;
497 case TOK_ASM_string:
498 case TOK_ASM_ascii:
499 case TOK_ASM_asciz:
501 const uint8_t *p;
502 int i, size, t;
504 t = tok;
505 next();
506 for(;;) {
507 if (tok != TOK_STR)
508 expect("string constant");
509 p = tokc.cstr->data;
510 size = tokc.cstr->size;
511 if (t == TOK_ASM_ascii && size > 0)
512 size--;
513 for(i = 0; i < size; i++)
514 g(p[i]);
515 next();
516 if (tok == ',') {
517 next();
518 } else if (tok != TOK_STR) {
519 break;
523 break;
524 case TOK_ASM_text:
525 case TOK_ASM_data:
526 case TOK_ASM_bss:
528 char sname[64];
529 tok1 = tok;
530 n = 0;
531 next();
532 if (tok != ';' && tok != TOK_LINEFEED) {
533 n = asm_int_expr(s1);
534 next();
536 sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
537 use_section(s1, sname);
539 break;
540 case TOK_ASM_file:
542 char filename[512];
544 filename[0] = '\0';
545 next();
547 if (tok == TOK_STR)
548 pstrcat(filename, sizeof(filename), tokc.cstr->data);
549 else
550 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
552 if (s1->warn_unsupported)
553 warning("ignoring .file %s", filename);
555 next();
557 break;
558 case TOK_ASM_ident:
560 char ident[256];
562 ident[0] = '\0';
563 next();
565 if (tok == TOK_STR)
566 pstrcat(ident, sizeof(ident), tokc.cstr->data);
567 else
568 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
570 if (s1->warn_unsupported)
571 warning("ignoring .ident %s", ident);
573 next();
575 break;
576 case TOK_ASM_size:
578 Sym *sym;
580 next();
581 sym = label_find(tok);
582 if (!sym) {
583 error("label not found: %s", get_tok_str(tok, NULL));
586 next();
587 skip(',');
588 /* XXX .size name,label2-label1 */
589 if (s1->warn_unsupported)
590 warning("ignoring .size %s,*", get_tok_str(tok, NULL));
592 while (tok != '\n' && tok != CH_EOF) {
593 next();
596 break;
597 case TOK_ASM_type:
599 Sym *sym;
600 char newtype[64];
601 newtype[0] = 0;
603 next();
604 sym = label_find(tok);
605 if (!sym) {
606 sym = label_push(&s1->asm_labels, tok, 0);
607 sym->type.t = VT_VOID;
610 next();
611 skip(',');
612 skip('@');
613 if (tok == TOK_STR)
614 pstrcat(newtype, sizeof(newtype), tokc.cstr->data);
615 else
616 pstrcat(newtype, sizeof(newtype), get_tok_str(tok, NULL));
618 if (!strcmp(newtype, "function")) {
619 sym->type.t = VT_FUNC;
621 else if (s1->warn_unsupported)
622 warning("change type of '%s' from 0x%x to '%s' ignored",
623 get_tok_str(sym->v, NULL), sym->type.t, newtype);
625 next();
627 break;
628 case TOK_SECTION1:
630 char sname[256];
632 /* XXX: support more options */
633 next();
634 sname[0] = '\0';
635 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
636 if (tok == TOK_STR)
637 pstrcat(sname, sizeof(sname), tokc.cstr->data);
638 else
639 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
640 next();
642 if (tok == ',') {
643 /* skip section options */
644 next();
645 if (tok != TOK_STR)
646 expect("string constant");
647 next();
649 last_text_section = cur_text_section;
650 use_section(s1, sname);
652 break;
653 case TOK_ASM_previous:
655 Section *sec;
656 next();
657 if (!last_text_section)
658 error("no previous section referenced");
659 sec = cur_text_section;
660 use_section1(s1, last_text_section);
661 last_text_section = sec;
663 break;
664 #ifdef TCC_TARGET_I386
665 case TOK_ASM_code16:
667 next();
668 s1->seg_size = 16;
670 break;
671 case TOK_ASM_code32:
673 next();
674 s1->seg_size = 32;
676 break;
677 #endif
678 #ifdef TCC_TARGET_X86_64
679 /* added for compatibility with GAS */
680 case TOK_ASM_code64:
681 next();
682 break;
683 #endif
684 default:
685 error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
686 break;
691 /* assemble a file */
692 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
694 int opcode;
696 #if 0
697 /* print stats about opcodes */
699 const ASMInstr *pa;
700 int freq[4];
701 int op_vals[500];
702 int nb_op_vals, i, j;
704 nb_op_vals = 0;
705 memset(freq, 0, sizeof(freq));
706 for(pa = asm_instrs; pa->sym != 0; pa++) {
707 freq[pa->nb_ops]++;
708 for(i=0;i<pa->nb_ops;i++) {
709 for(j=0;j<nb_op_vals;j++) {
710 if (pa->op_type[i] == op_vals[j])
711 goto found;
713 op_vals[nb_op_vals++] = pa->op_type[i];
714 found: ;
717 for(i=0;i<nb_op_vals;i++) {
718 int v = op_vals[i];
719 if ((v & (v - 1)) != 0)
720 printf("%3d: %08x\n", i, v);
722 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
723 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
724 freq[0], freq[1], freq[2], freq[3]);
726 #endif
728 /* XXX: undefine C labels */
730 ch = file->buf_ptr[0];
731 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
732 parse_flags = PARSE_FLAG_ASM_COMMENTS;
733 if (do_preprocess)
734 parse_flags |= PARSE_FLAG_PREPROCESS;
735 next();
736 for(;;) {
737 if (tok == TOK_EOF)
738 break;
739 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
740 redo:
741 if (tok == '#') {
742 /* horrible gas comment */
743 while (tok != TOK_LINEFEED)
744 next();
745 } else if (tok == '.') {
746 asm_parse_directive(s1);
747 } else if (tok == TOK_PPNUM) {
748 const char *p;
749 int n;
750 p = tokc.cstr->data;
751 n = strtoul(p, (char **)&p, 10);
752 if (*p != '\0')
753 expect("':'");
754 /* new local label */
755 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
756 next();
757 skip(':');
758 goto redo;
759 } else if (tok >= TOK_IDENT) {
760 /* instruction or label */
761 opcode = tok;
762 next();
763 if (tok == ':') {
764 char * label = get_tok_str(opcode, NULL);
766 /* new label */
767 asm_new_label(s1, opcode,
768 (label && label[0] == '.' && label[1] == 'L') ? 1 : 0);
769 next();
770 goto redo;
771 } else if (tok == '=') {
772 int n;
773 next();
774 n = asm_int_expr(s1);
775 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
776 goto redo;
777 } else {
778 asm_opcode(s1, opcode);
781 /* end of line */
782 if (tok != ';' && tok != TOK_LINEFEED){
783 expect("end of line");
785 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
786 next();
789 asm_free_labels(s1);
791 return 0;
794 /* Assemble the current file */
795 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
797 Sym *define_start;
798 int ret;
800 preprocess_init(s1);
802 /* default section is text */
803 cur_text_section = text_section;
804 ind = cur_text_section->data_offset;
806 define_start = define_stack;
808 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
809 symbols can be safely used */
810 put_elf_sym(symtab_section, 0, 0,
811 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
812 SHN_ABS, file->filename);
814 ret = tcc_assemble_internal(s1, do_preprocess);
816 cur_text_section->data_offset = ind;
818 free_defines(define_start);
820 return ret;
823 /********************************************************************/
824 /* GCC inline asm support */
826 /* assemble the string 'str' in the current C compilation unit without
827 C preprocessing. NOTE: str is modified by modifying the '\0' at the
828 end */
829 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
831 BufferedFile *bf, *saved_file;
832 int saved_parse_flags;
833 const int *saved_macro_ptr;
835 bf = tcc_malloc(sizeof(BufferedFile));
836 memset(bf, 0, sizeof(BufferedFile));
837 bf->fd = -1;
838 bf->buf_ptr = str;
839 bf->buf_end = str + len;
840 str[len] = CH_EOB;
841 /* same name as current file so that errors are correctly
842 reported */
843 pstrcpy(bf->filename, sizeof(bf->filename), file->filename);
844 bf->line_num = file->line_num;
845 saved_file = file;
846 file = bf;
847 saved_parse_flags = parse_flags;
848 saved_macro_ptr = macro_ptr;
849 macro_ptr = NULL;
851 tcc_assemble_internal(s1, 0);
853 parse_flags = saved_parse_flags;
854 macro_ptr = saved_macro_ptr;
855 file = saved_file;
856 tcc_free(bf);
859 /* find a constraint by its number or id (gcc 3 extended
860 syntax). return -1 if not found. Return in *pp in char after the
861 constraint */
862 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
863 const char *name, const char **pp)
865 int index;
866 TokenSym *ts;
867 const char *p;
869 if (isnum(*name)) {
870 index = 0;
871 while (isnum(*name)) {
872 index = (index * 10) + (*name) - '0';
873 name++;
875 if ((unsigned)index >= nb_operands)
876 index = -1;
877 } else if (*name == '[') {
878 name++;
879 p = strchr(name, ']');
880 if (p) {
881 ts = tok_alloc(name, p - name);
882 for(index = 0; index < nb_operands; index++) {
883 if (operands[index].id == ts->tok)
884 goto found;
886 index = -1;
887 found:
888 name = p + 1;
889 } else {
890 index = -1;
892 } else {
893 index = -1;
895 if (pp)
896 *pp = name;
897 return index;
900 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
901 int nb_outputs,
902 CString *out_str, CString *in_str)
904 int c, index, modifier;
905 const char *str;
906 ASMOperand *op;
907 SValue sv;
909 cstr_new(out_str);
910 str = in_str->data;
911 for(;;) {
912 c = *str++;
913 if (c == '%') {
914 if (*str == '%') {
915 str++;
916 goto add_char;
918 modifier = 0;
919 if (*str == 'c' || *str == 'n' ||
920 *str == 'b' || *str == 'w' || *str == 'h')
921 modifier = *str++;
922 index = find_constraint(operands, nb_operands, str, &str);
923 if (index < 0)
924 error("invalid operand reference after %%");
925 op = &operands[index];
926 sv = *op->vt;
927 if (op->reg >= 0) {
928 sv.r = op->reg;
929 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
930 sv.r |= VT_LVAL;
932 subst_asm_operand(out_str, &sv, modifier);
933 } else {
934 add_char:
935 cstr_ccat(out_str, c);
936 if (c == '\0')
937 break;
943 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
944 int is_output)
946 ASMOperand *op;
947 int nb_operands;
949 if (tok != ':') {
950 nb_operands = *nb_operands_ptr;
951 for(;;) {
952 if (nb_operands >= MAX_ASM_OPERANDS)
953 error("too many asm operands");
954 op = &operands[nb_operands++];
955 op->id = 0;
956 if (tok == '[') {
957 next();
958 if (tok < TOK_IDENT)
959 expect("identifier");
960 op->id = tok;
961 next();
962 skip(']');
964 if (tok != TOK_STR)
965 expect("string constant");
966 op->constraint = tcc_malloc(tokc.cstr->size);
967 strcpy(op->constraint, tokc.cstr->data);
968 next();
969 skip('(');
970 gexpr();
971 if (is_output) {
972 test_lvalue();
973 } else {
974 /* we want to avoid LLOCAL case, except when the 'm'
975 constraint is used. Note that it may come from
976 register storage, so we need to convert (reg)
977 case */
978 if ((vtop->r & VT_LVAL) &&
979 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
980 (vtop->r & VT_VALMASK) < VT_CONST) &&
981 !strchr(op->constraint, 'm')) {
982 gv(RC_INT);
985 op->vt = vtop;
986 skip(')');
987 if (tok == ',') {
988 next();
989 } else {
990 break;
993 *nb_operands_ptr = nb_operands;
997 static void parse_asm_str(CString *astr)
999 skip('(');
1000 /* read the string */
1001 if (tok != TOK_STR)
1002 expect("string constant");
1003 cstr_new(astr);
1004 while (tok == TOK_STR) {
1005 /* XXX: add \0 handling too ? */
1006 cstr_cat(astr, tokc.cstr->data);
1007 next();
1009 cstr_ccat(astr, '\0');
1012 /* parse the GCC asm() instruction */
1013 ST_FUNC void asm_instr(void)
1015 CString astr, astr1;
1016 ASMOperand operands[MAX_ASM_OPERANDS];
1017 int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg;
1018 uint8_t clobber_regs[NB_ASM_REGS];
1020 next();
1021 /* since we always generate the asm() instruction, we can ignore
1022 volatile */
1023 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1024 next();
1026 parse_asm_str(&astr);
1027 nb_operands = 0;
1028 nb_outputs = 0;
1029 must_subst = 0;
1030 memset(clobber_regs, 0, sizeof(clobber_regs));
1031 if (tok == ':') {
1032 next();
1033 must_subst = 1;
1034 /* output args */
1035 parse_asm_operands(operands, &nb_operands, 1);
1036 nb_outputs = nb_operands;
1037 if (tok == ':') {
1038 next();
1039 if (tok != ')') {
1040 /* input args */
1041 parse_asm_operands(operands, &nb_operands, 0);
1042 if (tok == ':') {
1043 /* clobber list */
1044 /* XXX: handle registers */
1045 next();
1046 for(;;) {
1047 if (tok != TOK_STR)
1048 expect("string constant");
1049 asm_clobber(clobber_regs, tokc.cstr->data);
1050 next();
1051 if (tok == ',') {
1052 next();
1053 } else {
1054 break;
1061 skip(')');
1062 /* NOTE: we do not eat the ';' so that we can restore the current
1063 token after the assembler parsing */
1064 if (tok != ';')
1065 expect("';'");
1066 nb_inputs = nb_operands - nb_outputs;
1068 /* save all values in the memory */
1069 save_regs(0);
1071 /* compute constraints */
1072 asm_compute_constraints(operands, nb_operands, nb_outputs,
1073 clobber_regs, &out_reg);
1075 /* substitute the operands in the asm string. No substitution is
1076 done if no operands (GCC behaviour) */
1077 #ifdef ASM_DEBUG
1078 printf("asm: \"%s\"\n", (char *)astr.data);
1079 #endif
1080 if (must_subst) {
1081 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1082 cstr_free(&astr);
1083 } else {
1084 astr1 = astr;
1086 #ifdef ASM_DEBUG
1087 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1088 #endif
1090 /* generate loads */
1091 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1092 clobber_regs, out_reg);
1094 /* assemble the string with tcc internal assembler */
1095 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1097 /* restore the current C token */
1098 next();
1100 /* store the output values if needed */
1101 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1102 clobber_regs, out_reg);
1104 /* free everything */
1105 for(i=0;i<nb_operands;i++) {
1106 ASMOperand *op;
1107 op = &operands[i];
1108 tcc_free(op->constraint);
1109 vpop();
1111 cstr_free(&astr1);
1114 ST_FUNC void asm_global_instr(void)
1116 CString astr;
1118 next();
1119 parse_asm_str(&astr);
1120 skip(')');
1121 /* NOTE: we do not eat the ';' so that we can restore the current
1122 token after the assembler parsing */
1123 if (tok != ';')
1124 expect("';'");
1126 #ifdef ASM_DEBUG
1127 printf("asm_global: \"%s\"\n", (char *)astr.data);
1128 #endif
1129 cur_text_section = text_section;
1130 ind = cur_text_section->data_offset;
1132 /* assemble the string with tcc internal assembler */
1133 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1135 cur_text_section->data_offset = ind;
1137 /* restore the current C token */
1138 next();
1140 cstr_free(&astr);