struct-init: Correctly parse unnamed member initializers
[tinycc.git] / tccasm.c
blobea65171306b4c6731475c8f5a409ed8f6f3c2b91
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"
22 #ifdef CONFIG_TCC_ASM
24 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
26 char buf[64];
27 TokenSym *ts;
29 snprintf(buf, sizeof(buf), "L..%u", n);
30 ts = tok_alloc(buf, strlen(buf));
31 return ts->tok;
34 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
35 static int tcc_assemble_internal(TCCState *s1, int do_preprocess);
36 static Sym sym_dot;
38 /* We do not use the C expression parser to handle symbols. Maybe the
39 C expression parser could be tweaked to do so. */
41 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
43 Sym *sym;
44 int op, label;
45 unsigned long n;
46 const char *p;
48 switch(tok) {
49 case TOK_PPNUM:
50 p = tokc.str.data;
51 n = strtoul(p, (char **)&p, 0);
52 if (*p == 'b' || *p == 'f') {
53 /* backward or forward label */
54 label = asm_get_local_label_name(s1, n);
55 sym = label_find(label);
56 if (*p == 'b') {
57 /* backward : find the last corresponding defined label */
58 if (sym && sym->r == 0)
59 sym = sym->prev_tok;
60 if (!sym)
61 tcc_error("local label '%d' not found backward", n);
62 } else {
63 /* forward */
64 if (!sym || sym->r) {
65 /* if the last label is defined, then define a new one */
66 sym = label_push(&s1->asm_labels, label, 0);
67 sym->type.t = VT_STATIC | VT_VOID;
70 pe->v = 0;
71 pe->sym = sym;
72 pe->pcrel = 0;
73 } else if (*p == '\0') {
74 pe->v = n;
75 pe->sym = NULL;
76 pe->pcrel = 0;
77 } else {
78 tcc_error("invalid number syntax");
80 next();
81 break;
82 case '+':
83 next();
84 asm_expr_unary(s1, pe);
85 break;
86 case '-':
87 case '~':
88 op = tok;
89 next();
90 asm_expr_unary(s1, pe);
91 if (pe->sym)
92 tcc_error("invalid operation with label");
93 if (op == '-')
94 pe->v = -pe->v;
95 else
96 pe->v = ~pe->v;
97 break;
98 case TOK_CCHAR:
99 case TOK_LCHAR:
100 pe->v = tokc.i;
101 pe->sym = NULL;
102 pe->pcrel = 0;
103 next();
104 break;
105 case '(':
106 next();
107 asm_expr(s1, pe);
108 skip(')');
109 break;
110 case '.':
111 pe->v = 0;
112 pe->sym = &sym_dot;
113 pe->pcrel = 0;
114 sym_dot.type.t = VT_VOID | VT_STATIC;
115 sym_dot.r = cur_text_section->sh_num;
116 sym_dot.jnext = ind;
117 next();
118 break;
119 default:
120 if (tok >= TOK_IDENT) {
121 /* label case : if the label was not found, add one */
122 sym = label_find(tok);
123 if (!sym) {
124 sym = label_push(&s1->asm_labels, tok, 0);
125 /* NOTE: by default, the symbol is global */
126 sym->type.t = VT_VOID;
128 if (sym->r == SHN_ABS) {
129 /* if absolute symbol, no need to put a symbol value */
130 pe->v = sym->jnext;
131 pe->sym = NULL;
132 pe->pcrel = 0;
133 } else {
134 pe->v = 0;
135 pe->sym = sym;
136 pe->pcrel = 0;
138 next();
139 } else {
140 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
142 break;
146 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
148 int op;
149 ExprValue e2;
151 asm_expr_unary(s1, pe);
152 for(;;) {
153 op = tok;
154 if (op != '*' && op != '/' && op != '%' &&
155 op != TOK_SHL && op != TOK_SAR)
156 break;
157 next();
158 asm_expr_unary(s1, &e2);
159 if (pe->sym || e2.sym)
160 tcc_error("invalid operation with label");
161 switch(op) {
162 case '*':
163 pe->v *= e2.v;
164 break;
165 case '/':
166 if (e2.v == 0) {
167 div_error:
168 tcc_error("division by zero");
170 pe->v /= e2.v;
171 break;
172 case '%':
173 if (e2.v == 0)
174 goto div_error;
175 pe->v %= e2.v;
176 break;
177 case TOK_SHL:
178 pe->v <<= e2.v;
179 break;
180 default:
181 case TOK_SAR:
182 pe->v >>= e2.v;
183 break;
188 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
190 int op;
191 ExprValue e2;
193 asm_expr_prod(s1, pe);
194 for(;;) {
195 op = tok;
196 if (op != '&' && op != '|' && op != '^')
197 break;
198 next();
199 asm_expr_prod(s1, &e2);
200 if (pe->sym || e2.sym)
201 tcc_error("invalid operation with label");
202 switch(op) {
203 case '&':
204 pe->v &= e2.v;
205 break;
206 case '|':
207 pe->v |= e2.v;
208 break;
209 default:
210 case '^':
211 pe->v ^= e2.v;
212 break;
217 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
219 int op;
220 ExprValue e2;
222 asm_expr_logic(s1, pe);
223 for(;;) {
224 op = tok;
225 if (op != '+' && op != '-')
226 break;
227 next();
228 asm_expr_logic(s1, &e2);
229 if (op == '+') {
230 if (pe->sym != NULL && e2.sym != NULL)
231 goto cannot_relocate;
232 pe->v += e2.v;
233 if (pe->sym == NULL && e2.sym != NULL)
234 pe->sym = e2.sym;
235 } else {
236 pe->v -= e2.v;
237 /* NOTE: we are less powerful than gas in that case
238 because we store only one symbol in the expression */
239 if (!e2.sym) {
240 /* OK */
241 } else if (pe->sym == e2.sym) {
242 /* OK */
243 pe->sym = NULL; /* same symbols can be subtracted to NULL */
244 } else if (pe->sym && pe->sym->r == e2.sym->r && pe->sym->r != 0) {
245 /* we also accept defined symbols in the same section */
246 pe->v += pe->sym->jnext - e2.sym->jnext;
247 pe->sym = NULL;
248 } else if (e2.sym->r == cur_text_section->sh_num) {
249 /* When subtracting a defined symbol in current section
250 this actually makes the value PC-relative. */
251 pe->v -= e2.sym->jnext - ind - 4;
252 pe->pcrel = 1;
253 e2.sym = NULL;
254 } else {
255 cannot_relocate:
256 tcc_error("invalid operation with label");
262 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
264 int op;
265 ExprValue e2;
267 asm_expr_sum(s1, pe);
268 for(;;) {
269 op = tok;
270 if (op != TOK_EQ && op != TOK_NE
271 && (op > TOK_GT || op < TOK_ULE))
272 break;
273 next();
274 asm_expr_sum(s1, &e2);
275 if (pe->sym || e2.sym)
276 tcc_error("invalid operation with label");
277 switch(op) {
278 case TOK_EQ:
279 pe->v = pe->v == e2.v;
280 break;
281 case TOK_NE:
282 pe->v = pe->v != e2.v;
283 break;
284 case TOK_LT:
285 pe->v = (int64_t)pe->v < (int64_t)e2.v;
286 break;
287 case TOK_GE:
288 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
289 break;
290 case TOK_LE:
291 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
292 break;
293 case TOK_GT:
294 pe->v = (int64_t)pe->v > (int64_t)e2.v;
295 break;
296 default:
297 break;
299 /* GAS compare results are -1/0 not 1/0. */
300 pe->v = -(int64_t)pe->v;
304 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
306 asm_expr_cmp(s1, pe);
309 ST_FUNC int asm_int_expr(TCCState *s1)
311 ExprValue e;
312 asm_expr(s1, &e);
313 if (e.sym)
314 expect("constant");
315 return e.v;
318 /* NOTE: the same name space as C labels is used to avoid using too
319 much memory when storing labels in TokenStrings */
320 static void asm_new_label1(TCCState *s1, int label, int is_local,
321 int sh_num, int value)
323 Sym *sym;
325 sym = label_find(label);
326 if (sym) {
327 if (sym->r) {
328 /* the label is already defined */
329 if (!is_local) {
330 tcc_error("assembler label '%s' already defined",
331 get_tok_str(label, NULL));
332 } else {
333 /* redefinition of local labels is possible */
334 goto new_label;
337 } else {
338 new_label:
339 sym = label_push(&s1->asm_labels, label, 0);
340 sym->type.t = VT_STATIC | VT_VOID;
342 sym->r = sh_num;
343 sym->jnext = value;
346 static void asm_new_label(TCCState *s1, int label, int is_local)
348 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
351 static void asm_free_labels(TCCState *st)
353 Sym *s, *s1;
354 Section *sec;
356 for(s = st->asm_labels; s != NULL; s = s1) {
357 s1 = s->prev;
358 /* define symbol value in object file */
359 if (s->r) {
360 if (s->r == SHN_ABS)
361 sec = SECTION_ABS;
362 else
363 sec = st->sections[s->r];
364 put_extern_sym2(s, sec, s->jnext, 0, 0);
366 /* remove label */
367 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
368 sym_free(s);
370 st->asm_labels = NULL;
373 static void use_section1(TCCState *s1, Section *sec)
375 cur_text_section->data_offset = ind;
376 cur_text_section = sec;
377 ind = cur_text_section->data_offset;
380 static void use_section(TCCState *s1, const char *name)
382 Section *sec;
383 sec = find_section(s1, name);
384 use_section1(s1, sec);
387 static void push_section(TCCState *s1, const char *name)
389 Section *sec = find_section(s1, name);
390 sec->prev = cur_text_section;
391 use_section1(s1, sec);
394 static void pop_section(TCCState *s1)
396 Section *prev = cur_text_section->prev;
397 if (!prev)
398 tcc_error(".popsection without .pushsection");
399 cur_text_section->prev = NULL;
400 use_section1(s1, prev);
403 static void asm_parse_directive(TCCState *s1)
405 int n, offset, v, size, tok1;
406 Section *sec;
407 uint8_t *ptr;
409 /* assembler directive */
410 sec = cur_text_section;
411 switch(tok) {
412 case TOK_ASMDIR_align:
413 case TOK_ASMDIR_balign:
414 case TOK_ASMDIR_p2align:
415 case TOK_ASMDIR_skip:
416 case TOK_ASMDIR_space:
417 tok1 = tok;
418 next();
419 n = asm_int_expr(s1);
420 if (tok1 == TOK_ASMDIR_p2align)
422 if (n < 0 || n > 30)
423 tcc_error("invalid p2align, must be between 0 and 30");
424 n = 1 << n;
425 tok1 = TOK_ASMDIR_align;
427 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
428 if (n < 0 || (n & (n-1)) != 0)
429 tcc_error("alignment must be a positive power of two");
430 offset = (ind + n - 1) & -n;
431 size = offset - ind;
432 /* the section must have a compatible alignment */
433 if (sec->sh_addralign < n)
434 sec->sh_addralign = n;
435 } else {
436 if (n < 0)
437 n = 0;
438 size = n;
440 v = 0;
441 if (tok == ',') {
442 next();
443 v = asm_int_expr(s1);
445 zero_pad:
446 if (sec->sh_type != SHT_NOBITS) {
447 sec->data_offset = ind;
448 ptr = section_ptr_add(sec, size);
449 memset(ptr, v, size);
451 ind += size;
452 break;
453 case TOK_ASMDIR_quad:
454 #ifdef TCC_TARGET_X86_64
455 size = 8;
456 goto asm_data;
457 #else
458 next();
459 for(;;) {
460 uint64_t vl;
461 const char *p;
463 p = tokc.str.data;
464 if (tok != TOK_PPNUM) {
465 error_constant:
466 tcc_error("64 bit constant");
468 vl = strtoll(p, (char **)&p, 0);
469 if (*p != '\0')
470 goto error_constant;
471 next();
472 if (sec->sh_type != SHT_NOBITS) {
473 /* XXX: endianness */
474 gen_le32(vl);
475 gen_le32(vl >> 32);
476 } else {
477 ind += 8;
479 if (tok != ',')
480 break;
481 next();
483 break;
484 #endif
485 case TOK_ASMDIR_byte:
486 size = 1;
487 goto asm_data;
488 case TOK_ASMDIR_word:
489 case TOK_ASMDIR_short:
490 size = 2;
491 goto asm_data;
492 case TOK_ASMDIR_long:
493 case TOK_ASMDIR_int:
494 size = 4;
495 asm_data:
496 next();
497 for(;;) {
498 ExprValue e;
499 asm_expr(s1, &e);
500 if (sec->sh_type != SHT_NOBITS) {
501 if (size == 4) {
502 gen_expr32(&e);
503 #ifdef TCC_TARGET_X86_64
504 } else if (size == 8) {
505 gen_expr64(&e);
506 #endif
507 } else {
508 if (e.sym)
509 expect("constant");
510 if (size == 1)
511 g(e.v);
512 else
513 gen_le16(e.v);
515 } else {
516 ind += size;
518 if (tok != ',')
519 break;
520 next();
522 break;
523 case TOK_ASMDIR_fill:
525 int repeat, size, val, i, j;
526 uint8_t repeat_buf[8];
527 next();
528 repeat = asm_int_expr(s1);
529 if (repeat < 0) {
530 tcc_error("repeat < 0; .fill ignored");
531 break;
533 size = 1;
534 val = 0;
535 if (tok == ',') {
536 next();
537 size = asm_int_expr(s1);
538 if (size < 0) {
539 tcc_error("size < 0; .fill ignored");
540 break;
542 if (size > 8)
543 size = 8;
544 if (tok == ',') {
545 next();
546 val = asm_int_expr(s1);
549 /* XXX: endianness */
550 repeat_buf[0] = val;
551 repeat_buf[1] = val >> 8;
552 repeat_buf[2] = val >> 16;
553 repeat_buf[3] = val >> 24;
554 repeat_buf[4] = 0;
555 repeat_buf[5] = 0;
556 repeat_buf[6] = 0;
557 repeat_buf[7] = 0;
558 for(i = 0; i < repeat; i++) {
559 for(j = 0; j < size; j++) {
560 g(repeat_buf[j]);
564 break;
565 case TOK_ASMDIR_rept:
567 int repeat;
568 TokenString *init_str;
569 ParseState saved_parse_state = {0};
570 next();
571 repeat = asm_int_expr(s1);
572 init_str = tok_str_alloc();
573 next();
574 while ((tok != TOK_ASMDIR_endr) && (tok != CH_EOF)) {
575 tok_str_add_tok(init_str);
576 next();
578 if (tok == CH_EOF) tcc_error("we at end of file, .endr not found");
579 next();
580 tok_str_add(init_str, -1);
581 tok_str_add(init_str, 0);
582 save_parse_state(&saved_parse_state);
583 begin_macro(init_str, 1);
584 while (repeat-- > 0) {
585 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS));
586 macro_ptr = init_str->str;
588 end_macro();
589 restore_parse_state(&saved_parse_state);
590 break;
592 case TOK_ASMDIR_org:
594 unsigned long n;
595 ExprValue e;
596 next();
597 asm_expr(s1, &e);
598 n = e.v;
599 if (e.sym) {
600 if (e.sym->r != cur_text_section->sh_num)
601 expect("constant or same-section symbol");
602 n += e.sym->jnext;
604 if (n < ind)
605 tcc_error("attempt to .org backwards");
606 v = 0;
607 size = n - ind;
608 goto zero_pad;
610 break;
611 case TOK_ASMDIR_globl:
612 case TOK_ASMDIR_global:
613 case TOK_ASMDIR_weak:
614 case TOK_ASMDIR_hidden:
615 tok1 = tok;
616 do {
617 Sym *sym;
619 next();
620 sym = label_find(tok);
621 if (!sym) {
622 sym = label_push(&s1->asm_labels, tok, 0);
623 sym->type.t = VT_VOID;
625 if (tok1 != TOK_ASMDIR_hidden)
626 sym->type.t &= ~VT_STATIC;
627 if (tok1 == TOK_ASMDIR_weak)
628 sym->type.t |= VT_WEAK;
629 else if (tok1 == TOK_ASMDIR_hidden)
630 sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
631 next();
632 } while (tok == ',');
633 break;
634 case TOK_ASMDIR_string:
635 case TOK_ASMDIR_ascii:
636 case TOK_ASMDIR_asciz:
638 const uint8_t *p;
639 int i, size, t;
641 t = tok;
642 next();
643 for(;;) {
644 if (tok != TOK_STR)
645 expect("string constant");
646 p = tokc.str.data;
647 size = tokc.str.size;
648 if (t == TOK_ASMDIR_ascii && size > 0)
649 size--;
650 for(i = 0; i < size; i++)
651 g(p[i]);
652 next();
653 if (tok == ',') {
654 next();
655 } else if (tok != TOK_STR) {
656 break;
660 break;
661 case TOK_ASMDIR_text:
662 case TOK_ASMDIR_data:
663 case TOK_ASMDIR_bss:
665 char sname[64];
666 tok1 = tok;
667 n = 0;
668 next();
669 if (tok != ';' && tok != TOK_LINEFEED) {
670 n = asm_int_expr(s1);
671 next();
673 if (n)
674 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
675 else
676 sprintf(sname, "%s", get_tok_str(tok1, NULL));
677 use_section(s1, sname);
679 break;
680 case TOK_ASMDIR_file:
682 char filename[512];
684 filename[0] = '\0';
685 next();
687 if (tok == TOK_STR)
688 pstrcat(filename, sizeof(filename), tokc.str.data);
689 else
690 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
692 if (s1->warn_unsupported)
693 tcc_warning("ignoring .file %s", filename);
695 next();
697 break;
698 case TOK_ASMDIR_ident:
700 char ident[256];
702 ident[0] = '\0';
703 next();
705 if (tok == TOK_STR)
706 pstrcat(ident, sizeof(ident), tokc.str.data);
707 else
708 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
710 if (s1->warn_unsupported)
711 tcc_warning("ignoring .ident %s", ident);
713 next();
715 break;
716 case TOK_ASMDIR_size:
718 Sym *sym;
720 next();
721 sym = label_find(tok);
722 if (!sym) {
723 tcc_error("label not found: %s", get_tok_str(tok, NULL));
726 /* XXX .size name,label2-label1 */
727 if (s1->warn_unsupported)
728 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
730 next();
731 skip(',');
732 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
733 next();
736 break;
737 case TOK_ASMDIR_type:
739 Sym *sym;
740 const char *newtype;
742 next();
743 sym = label_find(tok);
744 if (!sym) {
745 sym = label_push(&s1->asm_labels, tok, 0);
746 sym->type.t = VT_VOID;
749 next();
750 skip(',');
751 if (tok == TOK_STR) {
752 newtype = tokc.str.data;
753 } else {
754 if (tok == '@' || tok == '%')
755 next();
756 newtype = get_tok_str(tok, NULL);
759 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
760 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
762 else if (s1->warn_unsupported)
763 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
764 get_tok_str(sym->v, NULL), sym->type.t, newtype);
766 next();
768 break;
769 case TOK_ASMDIR_pushsection:
770 case TOK_ASMDIR_section:
772 char sname[256];
773 int old_nb_section = s1->nb_sections;
775 tok1 = tok;
776 /* XXX: support more options */
777 next();
778 sname[0] = '\0';
779 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
780 if (tok == TOK_STR)
781 pstrcat(sname, sizeof(sname), tokc.str.data);
782 else
783 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
784 next();
786 if (tok == ',') {
787 /* skip section options */
788 next();
789 if (tok != TOK_STR)
790 expect("string constant");
791 next();
792 if (tok == ',') {
793 next();
794 if (tok == '@' || tok == '%')
795 next();
796 next();
799 last_text_section = cur_text_section;
800 if (tok1 == TOK_ASMDIR_section)
801 use_section(s1, sname);
802 else
803 push_section(s1, sname);
804 /* If we just allocated a new section reset its alignment to
805 1. new_section normally acts for GCC compatibility and
806 sets alignment to PTR_SIZE. The assembler behaves different. */
807 if (old_nb_section != s1->nb_sections)
808 cur_text_section->sh_addralign = 1;
810 break;
811 case TOK_ASMDIR_previous:
813 Section *sec;
814 next();
815 if (!last_text_section)
816 tcc_error("no previous section referenced");
817 sec = cur_text_section;
818 use_section1(s1, last_text_section);
819 last_text_section = sec;
821 break;
822 case TOK_ASMDIR_popsection:
823 next();
824 pop_section(s1);
825 break;
826 #ifdef TCC_TARGET_I386
827 case TOK_ASMDIR_code16:
829 next();
830 s1->seg_size = 16;
832 break;
833 case TOK_ASMDIR_code32:
835 next();
836 s1->seg_size = 32;
838 break;
839 #endif
840 #ifdef TCC_TARGET_X86_64
841 /* added for compatibility with GAS */
842 case TOK_ASMDIR_code64:
843 next();
844 break;
845 #endif
846 default:
847 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
848 break;
853 /* assemble a file */
854 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
856 int opcode;
858 /* XXX: undefine C labels */
860 ch = file->buf_ptr[0];
861 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
862 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
863 if (do_preprocess)
864 parse_flags |= PARSE_FLAG_PREPROCESS;
865 next();
866 for(;;) {
867 if (tok == TOK_EOF)
868 break;
869 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
870 redo:
871 if (tok == '#') {
872 /* horrible gas comment */
873 while (tok != TOK_LINEFEED)
874 next();
875 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
876 asm_parse_directive(s1);
877 } else if (tok == TOK_PPNUM) {
878 const char *p;
879 int n;
880 p = tokc.str.data;
881 n = strtoul(p, (char **)&p, 10);
882 if (*p != '\0')
883 expect("':'");
884 /* new local label */
885 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
886 next();
887 skip(':');
888 goto redo;
889 } else if (tok >= TOK_IDENT) {
890 /* instruction or label */
891 opcode = tok;
892 next();
893 if (tok == ':') {
894 /* handle "extern void vide(void); __asm__("vide: ret");" as
895 "__asm__("globl vide\nvide: ret");" */
896 Sym *sym = sym_find(opcode);
897 if (sym && (sym->type.t & VT_EXTERN) && nocode_wanted) {
898 sym = label_find(opcode);
899 if (!sym) {
900 sym = label_push(&s1->asm_labels, opcode, 0);
901 sym->type.t = VT_VOID;
904 /* new label */
905 asm_new_label(s1, opcode, 0);
906 next();
907 goto redo;
908 } else if (tok == '=') {
909 int n;
910 next();
911 n = asm_int_expr(s1);
912 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
913 goto redo;
914 } else {
915 asm_opcode(s1, opcode);
918 /* end of line */
919 if (tok != ';' && tok != TOK_LINEFEED){
920 expect("end of line");
922 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
923 next();
926 asm_free_labels(s1);
928 return 0;
931 /* Assemble the current file */
932 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
934 Sym *define_start;
935 int ret;
937 preprocess_start(s1);
939 /* default section is text */
940 cur_text_section = text_section;
941 ind = cur_text_section->data_offset;
943 define_start = define_stack;
945 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
946 symbols can be safely used */
947 put_elf_sym(symtab_section, 0, 0,
948 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
949 SHN_ABS, file->filename);
951 ret = tcc_assemble_internal(s1, do_preprocess);
953 cur_text_section->data_offset = ind;
955 free_defines(define_start);
957 return ret;
960 /********************************************************************/
961 /* GCC inline asm support */
963 /* assemble the string 'str' in the current C compilation unit without
964 C preprocessing. NOTE: str is modified by modifying the '\0' at the
965 end */
966 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
968 int saved_parse_flags;
969 const int *saved_macro_ptr;
971 saved_parse_flags = parse_flags;
972 saved_macro_ptr = macro_ptr;
974 tcc_open_bf(s1, ":asm:", len);
975 memcpy(file->buffer, str, len);
977 macro_ptr = NULL;
978 tcc_assemble_internal(s1, 0);
979 tcc_close();
981 parse_flags = saved_parse_flags;
982 macro_ptr = saved_macro_ptr;
985 /* find a constraint by its number or id (gcc 3 extended
986 syntax). return -1 if not found. Return in *pp in char after the
987 constraint */
988 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
989 const char *name, const char **pp)
991 int index;
992 TokenSym *ts;
993 const char *p;
995 if (isnum(*name)) {
996 index = 0;
997 while (isnum(*name)) {
998 index = (index * 10) + (*name) - '0';
999 name++;
1001 if ((unsigned)index >= nb_operands)
1002 index = -1;
1003 } else if (*name == '[') {
1004 name++;
1005 p = strchr(name, ']');
1006 if (p) {
1007 ts = tok_alloc(name, p - name);
1008 for(index = 0; index < nb_operands; index++) {
1009 if (operands[index].id == ts->tok)
1010 goto found;
1012 index = -1;
1013 found:
1014 name = p + 1;
1015 } else {
1016 index = -1;
1018 } else {
1019 index = -1;
1021 if (pp)
1022 *pp = name;
1023 return index;
1026 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1027 int nb_outputs,
1028 CString *out_str, CString *in_str)
1030 int c, index, modifier;
1031 const char *str;
1032 ASMOperand *op;
1033 SValue sv;
1035 cstr_new(out_str);
1036 str = in_str->data;
1037 for(;;) {
1038 c = *str++;
1039 if (c == '%') {
1040 if (*str == '%') {
1041 str++;
1042 goto add_char;
1044 modifier = 0;
1045 if (*str == 'c' || *str == 'n' ||
1046 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1047 *str == 'q' ||
1048 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1049 and make literal operands not be decorated with '$'. */
1050 *str == 'P')
1051 modifier = *str++;
1052 index = find_constraint(operands, nb_operands, str, &str);
1053 if (index < 0)
1054 tcc_error("invalid operand reference after %%");
1055 op = &operands[index];
1056 sv = *op->vt;
1057 if (op->reg >= 0) {
1058 sv.r = op->reg;
1059 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1060 sv.r |= VT_LVAL;
1062 subst_asm_operand(out_str, &sv, modifier);
1063 } else {
1064 add_char:
1065 cstr_ccat(out_str, c);
1066 if (c == '\0')
1067 break;
1073 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1074 int is_output)
1076 ASMOperand *op;
1077 int nb_operands;
1079 if (tok != ':') {
1080 nb_operands = *nb_operands_ptr;
1081 for(;;) {
1082 CString astr;
1083 if (nb_operands >= MAX_ASM_OPERANDS)
1084 tcc_error("too many asm operands");
1085 op = &operands[nb_operands++];
1086 op->id = 0;
1087 if (tok == '[') {
1088 next();
1089 if (tok < TOK_IDENT)
1090 expect("identifier");
1091 op->id = tok;
1092 next();
1093 skip(']');
1095 parse_mult_str(&astr, "string constant");
1096 op->constraint = tcc_malloc(astr.size);
1097 strcpy(op->constraint, astr.data);
1098 cstr_free(&astr);
1099 skip('(');
1100 gexpr();
1101 if (is_output) {
1102 if (!(vtop->type.t & VT_ARRAY))
1103 test_lvalue();
1104 } else {
1105 /* we want to avoid LLOCAL case, except when the 'm'
1106 constraint is used. Note that it may come from
1107 register storage, so we need to convert (reg)
1108 case */
1109 if ((vtop->r & VT_LVAL) &&
1110 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1111 (vtop->r & VT_VALMASK) < VT_CONST) &&
1112 !strchr(op->constraint, 'm')) {
1113 gv(RC_INT);
1116 op->vt = vtop;
1117 skip(')');
1118 if (tok == ',') {
1119 next();
1120 } else {
1121 break;
1124 *nb_operands_ptr = nb_operands;
1128 /* parse the GCC asm() instruction */
1129 ST_FUNC void asm_instr(void)
1131 CString astr, astr1;
1132 ASMOperand operands[MAX_ASM_OPERANDS];
1133 int nb_outputs, nb_operands, i, must_subst, out_reg;
1134 uint8_t clobber_regs[NB_ASM_REGS];
1136 next();
1137 /* since we always generate the asm() instruction, we can ignore
1138 volatile */
1139 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1140 next();
1142 parse_asm_str(&astr);
1143 nb_operands = 0;
1144 nb_outputs = 0;
1145 must_subst = 0;
1146 memset(clobber_regs, 0, sizeof(clobber_regs));
1147 if (tok == ':') {
1148 next();
1149 must_subst = 1;
1150 /* output args */
1151 parse_asm_operands(operands, &nb_operands, 1);
1152 nb_outputs = nb_operands;
1153 if (tok == ':') {
1154 next();
1155 if (tok != ')') {
1156 /* input args */
1157 parse_asm_operands(operands, &nb_operands, 0);
1158 if (tok == ':') {
1159 /* clobber list */
1160 /* XXX: handle registers */
1161 next();
1162 for(;;) {
1163 if (tok != TOK_STR)
1164 expect("string constant");
1165 asm_clobber(clobber_regs, tokc.str.data);
1166 next();
1167 if (tok == ',') {
1168 next();
1169 } else {
1170 break;
1177 skip(')');
1178 /* NOTE: we do not eat the ';' so that we can restore the current
1179 token after the assembler parsing */
1180 if (tok != ';')
1181 expect("';'");
1183 /* save all values in the memory */
1184 save_regs(0);
1186 /* compute constraints */
1187 asm_compute_constraints(operands, nb_operands, nb_outputs,
1188 clobber_regs, &out_reg);
1190 /* substitute the operands in the asm string. No substitution is
1191 done if no operands (GCC behaviour) */
1192 #ifdef ASM_DEBUG
1193 printf("asm: \"%s\"\n", (char *)astr.data);
1194 #endif
1195 if (must_subst) {
1196 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1197 cstr_free(&astr);
1198 } else {
1199 astr1 = astr;
1201 #ifdef ASM_DEBUG
1202 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1203 #endif
1205 /* generate loads */
1206 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1207 clobber_regs, out_reg);
1209 /* assemble the string with tcc internal assembler */
1210 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1212 /* restore the current C token */
1213 next();
1215 /* store the output values if needed */
1216 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1217 clobber_regs, out_reg);
1219 /* free everything */
1220 for(i=0;i<nb_operands;i++) {
1221 ASMOperand *op;
1222 op = &operands[i];
1223 tcc_free(op->constraint);
1224 vpop();
1226 cstr_free(&astr1);
1229 ST_FUNC void asm_global_instr(void)
1231 CString astr;
1233 next();
1234 parse_asm_str(&astr);
1235 skip(')');
1236 /* NOTE: we do not eat the ';' so that we can restore the current
1237 token after the assembler parsing */
1238 if (tok != ';')
1239 expect("';'");
1241 #ifdef ASM_DEBUG
1242 printf("asm_global: \"%s\"\n", (char *)astr.data);
1243 #endif
1244 cur_text_section = text_section;
1245 ind = cur_text_section->data_offset;
1247 /* assemble the string with tcc internal assembler */
1248 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1250 cur_text_section->data_offset = ind;
1252 /* restore the current C token */
1253 next();
1255 cstr_free(&astr);
1257 #endif /* CONFIG_TCC_ASM */