Redo "fix line number in macro redefined message"
[tinycc.git] / tccasm.c
blob9dcab1ce94a02b1d75fb0ec9264dab67b798b829
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 } else if (*p == '\0') {
73 pe->v = n;
74 pe->sym = NULL;
75 } else {
76 tcc_error("invalid number syntax");
78 next();
79 break;
80 case '+':
81 next();
82 asm_expr_unary(s1, pe);
83 break;
84 case '-':
85 case '~':
86 op = tok;
87 next();
88 asm_expr_unary(s1, pe);
89 if (pe->sym)
90 tcc_error("invalid operation with label");
91 if (op == '-')
92 pe->v = -pe->v;
93 else
94 pe->v = ~pe->v;
95 break;
96 case TOK_CCHAR:
97 case TOK_LCHAR:
98 pe->v = tokc.i;
99 pe->sym = NULL;
100 next();
101 break;
102 case '(':
103 next();
104 asm_expr(s1, pe);
105 skip(')');
106 break;
107 case '.':
108 pe->v = 0;
109 pe->sym = &sym_dot;
110 sym_dot.type.t = VT_VOID | VT_STATIC;
111 sym_dot.r = cur_text_section->sh_num;
112 sym_dot.jnext = ind;
113 next();
114 break;
115 default:
116 if (tok >= TOK_IDENT) {
117 /* label case : if the label was not found, add one */
118 sym = label_find(tok);
119 if (!sym) {
120 sym = label_push(&s1->asm_labels, tok, 0);
121 /* NOTE: by default, the symbol is global */
122 sym->type.t = VT_VOID;
124 if (sym->r == SHN_ABS) {
125 /* if absolute symbol, no need to put a symbol value */
126 pe->v = sym->jnext;
127 pe->sym = NULL;
128 } else {
129 pe->v = 0;
130 pe->sym = sym;
132 next();
133 } else {
134 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
136 break;
140 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
142 int op;
143 ExprValue e2;
145 asm_expr_unary(s1, pe);
146 for(;;) {
147 op = tok;
148 if (op != '*' && op != '/' && op != '%' &&
149 op != TOK_SHL && op != TOK_SAR)
150 break;
151 next();
152 asm_expr_unary(s1, &e2);
153 if (pe->sym || e2.sym)
154 tcc_error("invalid operation with label");
155 switch(op) {
156 case '*':
157 pe->v *= e2.v;
158 break;
159 case '/':
160 if (e2.v == 0) {
161 div_error:
162 tcc_error("division by zero");
164 pe->v /= e2.v;
165 break;
166 case '%':
167 if (e2.v == 0)
168 goto div_error;
169 pe->v %= e2.v;
170 break;
171 case TOK_SHL:
172 pe->v <<= e2.v;
173 break;
174 default:
175 case TOK_SAR:
176 pe->v >>= e2.v;
177 break;
182 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
184 int op;
185 ExprValue e2;
187 asm_expr_prod(s1, pe);
188 for(;;) {
189 op = tok;
190 if (op != '&' && op != '|' && op != '^')
191 break;
192 next();
193 asm_expr_prod(s1, &e2);
194 if (pe->sym || e2.sym)
195 tcc_error("invalid operation with label");
196 switch(op) {
197 case '&':
198 pe->v &= e2.v;
199 break;
200 case '|':
201 pe->v |= e2.v;
202 break;
203 default:
204 case '^':
205 pe->v ^= e2.v;
206 break;
211 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
213 int op;
214 ExprValue e2;
216 asm_expr_logic(s1, pe);
217 for(;;) {
218 op = tok;
219 if (op != '+' && op != '-')
220 break;
221 next();
222 asm_expr_logic(s1, &e2);
223 if (op == '+') {
224 if (pe->sym != NULL && e2.sym != NULL)
225 goto cannot_relocate;
226 pe->v += e2.v;
227 if (pe->sym == NULL && e2.sym != NULL)
228 pe->sym = e2.sym;
229 } else {
230 pe->v -= e2.v;
231 /* NOTE: we are less powerful than gas in that case
232 because we store only one symbol in the expression */
233 if (!pe->sym && !e2.sym) {
234 /* OK */
235 } else if (pe->sym && !e2.sym) {
236 /* OK */
237 } else if (pe->sym && e2.sym) {
238 if (pe->sym == e2.sym) {
239 /* OK */
240 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
241 /* we also accept defined symbols in the same section */
242 pe->v += pe->sym->jnext - e2.sym->jnext;
243 } else {
244 goto cannot_relocate;
246 pe->sym = NULL; /* same symbols can be subtracted to NULL */
247 } else {
248 cannot_relocate:
249 tcc_error("invalid operation with label");
255 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
257 asm_expr_sum(s1, pe);
260 ST_FUNC int asm_int_expr(TCCState *s1)
262 ExprValue e;
263 asm_expr(s1, &e);
264 if (e.sym)
265 expect("constant");
266 return e.v;
269 /* NOTE: the same name space as C labels is used to avoid using too
270 much memory when storing labels in TokenStrings */
271 static void asm_new_label1(TCCState *s1, int label, int is_local,
272 int sh_num, int value)
274 Sym *sym;
276 sym = label_find(label);
277 if (sym) {
278 if (sym->r) {
279 /* the label is already defined */
280 if (!is_local) {
281 tcc_error("assembler label '%s' already defined",
282 get_tok_str(label, NULL));
283 } else {
284 /* redefinition of local labels is possible */
285 goto new_label;
288 } else {
289 new_label:
290 sym = label_push(&s1->asm_labels, label, 0);
291 sym->type.t = VT_STATIC | VT_VOID;
293 sym->r = sh_num;
294 sym->jnext = value;
297 static void asm_new_label(TCCState *s1, int label, int is_local)
299 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
302 static void asm_free_labels(TCCState *st)
304 Sym *s, *s1;
305 Section *sec;
307 for(s = st->asm_labels; s != NULL; s = s1) {
308 s1 = s->prev;
309 /* define symbol value in object file */
310 if (s->r) {
311 if (s->r == SHN_ABS)
312 sec = SECTION_ABS;
313 else
314 sec = st->sections[s->r];
315 put_extern_sym2(s, sec, s->jnext, 0, 0);
317 /* remove label */
318 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
319 sym_free(s);
321 st->asm_labels = NULL;
324 static void use_section1(TCCState *s1, Section *sec)
326 cur_text_section->data_offset = ind;
327 cur_text_section = sec;
328 ind = cur_text_section->data_offset;
331 static void use_section(TCCState *s1, const char *name)
333 Section *sec;
334 sec = find_section(s1, name);
335 use_section1(s1, sec);
338 static void asm_parse_directive(TCCState *s1)
340 int n, offset, v, size, tok1;
341 Section *sec;
342 uint8_t *ptr;
344 /* assembler directive */
345 sec = cur_text_section;
346 switch(tok) {
347 case TOK_ASMDIR_align:
348 case TOK_ASMDIR_p2align:
349 case TOK_ASMDIR_skip:
350 case TOK_ASMDIR_space:
351 tok1 = tok;
352 next();
353 n = asm_int_expr(s1);
354 if (tok1 == TOK_ASMDIR_p2align)
356 if (n < 0 || n > 30)
357 tcc_error("invalid p2align, must be between 0 and 30");
358 n = 1 << n;
359 tok1 = TOK_ASMDIR_align;
361 if (tok1 == TOK_ASMDIR_align) {
362 if (n < 0 || (n & (n-1)) != 0)
363 tcc_error("alignment must be a positive power of two");
364 offset = (ind + n - 1) & -n;
365 size = offset - ind;
366 /* the section must have a compatible alignment */
367 if (sec->sh_addralign < n)
368 sec->sh_addralign = n;
369 } else {
370 size = n;
372 v = 0;
373 if (tok == ',') {
374 next();
375 v = asm_int_expr(s1);
377 zero_pad:
378 if (sec->sh_type != SHT_NOBITS) {
379 sec->data_offset = ind;
380 ptr = section_ptr_add(sec, size);
381 memset(ptr, v, size);
383 ind += size;
384 break;
385 case TOK_ASMDIR_quad:
386 next();
387 for(;;) {
388 uint64_t vl;
389 const char *p;
391 p = tokc.str.data;
392 if (tok != TOK_PPNUM) {
393 error_constant:
394 tcc_error("64 bit constant");
396 vl = strtoll(p, (char **)&p, 0);
397 if (*p != '\0')
398 goto error_constant;
399 next();
400 if (sec->sh_type != SHT_NOBITS) {
401 /* XXX: endianness */
402 gen_le32(vl);
403 gen_le32(vl >> 32);
404 } else {
405 ind += 8;
407 if (tok != ',')
408 break;
409 next();
411 break;
412 case TOK_ASMDIR_byte:
413 size = 1;
414 goto asm_data;
415 case TOK_ASMDIR_word:
416 case TOK_ASMDIR_short:
417 size = 2;
418 goto asm_data;
419 case TOK_ASMDIR_long:
420 case TOK_ASMDIR_int:
421 size = 4;
422 asm_data:
423 next();
424 for(;;) {
425 ExprValue e;
426 asm_expr(s1, &e);
427 if (sec->sh_type != SHT_NOBITS) {
428 if (size == 4) {
429 gen_expr32(&e);
430 } else {
431 if (e.sym)
432 expect("constant");
433 if (size == 1)
434 g(e.v);
435 else
436 gen_le16(e.v);
438 } else {
439 ind += size;
441 if (tok != ',')
442 break;
443 next();
445 break;
446 case TOK_ASMDIR_fill:
448 int repeat, size, val, i, j;
449 uint8_t repeat_buf[8];
450 next();
451 repeat = asm_int_expr(s1);
452 if (repeat < 0) {
453 tcc_error("repeat < 0; .fill ignored");
454 break;
456 size = 1;
457 val = 0;
458 if (tok == ',') {
459 next();
460 size = asm_int_expr(s1);
461 if (size < 0) {
462 tcc_error("size < 0; .fill ignored");
463 break;
465 if (size > 8)
466 size = 8;
467 if (tok == ',') {
468 next();
469 val = asm_int_expr(s1);
472 /* XXX: endianness */
473 repeat_buf[0] = val;
474 repeat_buf[1] = val >> 8;
475 repeat_buf[2] = val >> 16;
476 repeat_buf[3] = val >> 24;
477 repeat_buf[4] = 0;
478 repeat_buf[5] = 0;
479 repeat_buf[6] = 0;
480 repeat_buf[7] = 0;
481 for(i = 0; i < repeat; i++) {
482 for(j = 0; j < size; j++) {
483 g(repeat_buf[j]);
487 break;
488 case TOK_ASMDIR_rept:
490 int repeat;
491 TokenString init_str;
492 ParseState saved_parse_state = {0};
493 next();
494 repeat = asm_int_expr(s1);
495 tok_str_new(&init_str);
496 next();
497 while ((tok != TOK_ASMDIR_endr) && (tok != CH_EOF)) {
498 tok_str_add_tok(&init_str);
499 next();
501 if (tok == CH_EOF) tcc_error("we at end of file, .endr not found");
502 next();
503 tok_str_add(&init_str, -1);
504 tok_str_add(&init_str, 0);
505 save_parse_state(&saved_parse_state);
506 begin_macro(&init_str, 0);
507 while (repeat-- > 0) {
508 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS));
509 macro_ptr = init_str.str;
511 end_macro();
512 restore_parse_state(&saved_parse_state);
513 break;
515 case TOK_ASMDIR_org:
517 unsigned long n;
518 next();
519 /* XXX: handle section symbols too */
520 n = asm_int_expr(s1);
521 if (n < ind)
522 tcc_error("attempt to .org backwards");
523 v = 0;
524 size = n - ind;
525 goto zero_pad;
527 break;
528 case TOK_ASMDIR_globl:
529 case TOK_ASMDIR_global:
530 case TOK_ASMDIR_weak:
531 case TOK_ASMDIR_hidden:
532 tok1 = tok;
533 do {
534 Sym *sym;
536 next();
537 sym = label_find(tok);
538 if (!sym) {
539 sym = label_push(&s1->asm_labels, tok, 0);
540 sym->type.t = VT_VOID;
542 if (tok1 != TOK_ASMDIR_hidden)
543 sym->type.t &= ~VT_STATIC;
544 if (tok1 == TOK_ASMDIR_weak)
545 sym->type.t |= VT_WEAK;
546 else if (tok1 == TOK_ASMDIR_hidden)
547 sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
548 next();
549 } while (tok == ',');
550 break;
551 case TOK_ASMDIR_string:
552 case TOK_ASMDIR_ascii:
553 case TOK_ASMDIR_asciz:
555 const uint8_t *p;
556 int i, size, t;
558 t = tok;
559 next();
560 for(;;) {
561 if (tok != TOK_STR)
562 expect("string constant");
563 p = tokc.str.data;
564 size = tokc.str.size;
565 if (t == TOK_ASMDIR_ascii && size > 0)
566 size--;
567 for(i = 0; i < size; i++)
568 g(p[i]);
569 next();
570 if (tok == ',') {
571 next();
572 } else if (tok != TOK_STR) {
573 break;
577 break;
578 case TOK_ASMDIR_text:
579 case TOK_ASMDIR_data:
580 case TOK_ASMDIR_bss:
582 char sname[64];
583 tok1 = tok;
584 n = 0;
585 next();
586 if (tok != ';' && tok != TOK_LINEFEED) {
587 n = asm_int_expr(s1);
588 next();
590 if (n)
591 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
592 else
593 sprintf(sname, "%s", get_tok_str(tok1, NULL));
594 use_section(s1, sname);
596 break;
597 case TOK_ASMDIR_file:
599 char filename[512];
601 filename[0] = '\0';
602 next();
604 if (tok == TOK_STR)
605 pstrcat(filename, sizeof(filename), tokc.str.data);
606 else
607 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
609 if (s1->warn_unsupported)
610 tcc_warning("ignoring .file %s", filename);
612 next();
614 break;
615 case TOK_ASMDIR_ident:
617 char ident[256];
619 ident[0] = '\0';
620 next();
622 if (tok == TOK_STR)
623 pstrcat(ident, sizeof(ident), tokc.str.data);
624 else
625 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
627 if (s1->warn_unsupported)
628 tcc_warning("ignoring .ident %s", ident);
630 next();
632 break;
633 case TOK_ASMDIR_size:
635 Sym *sym;
637 next();
638 sym = label_find(tok);
639 if (!sym) {
640 tcc_error("label not found: %s", get_tok_str(tok, NULL));
643 /* XXX .size name,label2-label1 */
644 if (s1->warn_unsupported)
645 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
647 next();
648 skip(',');
649 while (tok != '\n' && tok != CH_EOF) {
650 next();
653 break;
654 case TOK_ASMDIR_type:
656 Sym *sym;
657 const char *newtype;
659 next();
660 sym = label_find(tok);
661 if (!sym) {
662 sym = label_push(&s1->asm_labels, tok, 0);
663 sym->type.t = VT_VOID;
666 next();
667 skip(',');
668 if (tok == TOK_STR) {
669 newtype = tokc.str.data;
670 } else {
671 if (tok == '@' || tok == '%')
672 next();
673 newtype = get_tok_str(tok, NULL);
676 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
677 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
679 else if (s1->warn_unsupported)
680 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
681 get_tok_str(sym->v, NULL), sym->type.t, newtype);
683 next();
685 break;
686 case TOK_ASMDIR_section:
688 char sname[256];
690 /* XXX: support more options */
691 next();
692 sname[0] = '\0';
693 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
694 if (tok == TOK_STR)
695 pstrcat(sname, sizeof(sname), tokc.str.data);
696 else
697 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
698 next();
700 if (tok == ',') {
701 /* skip section options */
702 next();
703 if (tok != TOK_STR)
704 expect("string constant");
705 next();
706 if (tok == ',') {
707 next();
708 if (tok == '@' || tok == '%')
709 next();
710 next();
713 last_text_section = cur_text_section;
714 use_section(s1, sname);
716 break;
717 case TOK_ASMDIR_previous:
719 Section *sec;
720 next();
721 if (!last_text_section)
722 tcc_error("no previous section referenced");
723 sec = cur_text_section;
724 use_section1(s1, last_text_section);
725 last_text_section = sec;
727 break;
728 #ifdef TCC_TARGET_I386
729 case TOK_ASMDIR_code16:
731 next();
732 s1->seg_size = 16;
734 break;
735 case TOK_ASMDIR_code32:
737 next();
738 s1->seg_size = 32;
740 break;
741 #endif
742 #ifdef TCC_TARGET_X86_64
743 /* added for compatibility with GAS */
744 case TOK_ASMDIR_code64:
745 next();
746 break;
747 #endif
748 default:
749 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
750 break;
755 /* assemble a file */
756 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
758 int opcode;
760 #if 0
761 /* print stats about opcodes */
763 const ASMInstr *pa;
764 int freq[4];
765 int op_vals[500];
766 int nb_op_vals, i, j;
768 nb_op_vals = 0;
769 memset(freq, 0, sizeof(freq));
770 for(pa = asm_instrs; pa->sym != 0; pa++) {
771 freq[pa->nb_ops]++;
772 for(i=0;i<pa->nb_ops;i++) {
773 for(j=0;j<nb_op_vals;j++) {
774 if (pa->op_type[i] == op_vals[j])
775 goto found;
777 op_vals[nb_op_vals++] = pa->op_type[i];
778 found: ;
781 for(i=0;i<nb_op_vals;i++) {
782 int v = op_vals[i];
783 if ((v & (v - 1)) != 0)
784 printf("%3d: %08x\n", i, v);
786 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
787 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
788 freq[0], freq[1], freq[2], freq[3]);
790 #endif
792 /* XXX: undefine C labels */
794 ch = file->buf_ptr[0];
795 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
796 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
797 if (do_preprocess)
798 parse_flags |= PARSE_FLAG_PREPROCESS;
799 next();
800 for(;;) {
801 if (tok == TOK_EOF)
802 break;
803 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
804 redo:
805 if (tok == '#') {
806 /* horrible gas comment */
807 while (tok != TOK_LINEFEED)
808 next();
809 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
810 asm_parse_directive(s1);
811 } else if (tok == TOK_PPNUM) {
812 const char *p;
813 int n;
814 p = tokc.str.data;
815 n = strtoul(p, (char **)&p, 10);
816 if (*p != '\0')
817 expect("':'");
818 /* new local label */
819 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
820 next();
821 skip(':');
822 goto redo;
823 } else if (tok >= TOK_IDENT) {
824 /* instruction or label */
825 opcode = tok;
826 next();
827 if (tok == ':') {
828 /* handle "extern void vide(void); __asm__("vide: ret");" as
829 "__asm__("globl vide\nvide: ret");" */
830 Sym *sym = sym_find(opcode);
831 if (sym && (sym->type.t & VT_EXTERN) && nocode_wanted) {
832 sym = label_find(opcode);
833 if (!sym) {
834 sym = label_push(&s1->asm_labels, opcode, 0);
835 sym->type.t = VT_VOID;
838 /* new label */
839 asm_new_label(s1, opcode, 0);
840 next();
841 goto redo;
842 } else if (tok == '=') {
843 int n;
844 next();
845 n = asm_int_expr(s1);
846 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
847 goto redo;
848 } else {
849 asm_opcode(s1, opcode);
852 /* end of line */
853 if (tok != ';' && tok != TOK_LINEFEED){
854 expect("end of line");
856 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
857 next();
860 asm_free_labels(s1);
862 return 0;
865 /* Assemble the current file */
866 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
868 Sym *define_start;
869 int ret;
871 preprocess_init(s1);
873 /* default section is text */
874 cur_text_section = text_section;
875 ind = cur_text_section->data_offset;
877 define_start = define_stack;
879 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
880 symbols can be safely used */
881 put_elf_sym(symtab_section, 0, 0,
882 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
883 SHN_ABS, file->filename);
885 ret = tcc_assemble_internal(s1, do_preprocess);
887 cur_text_section->data_offset = ind;
889 free_defines(define_start);
891 return ret;
894 /********************************************************************/
895 /* GCC inline asm support */
897 /* assemble the string 'str' in the current C compilation unit without
898 C preprocessing. NOTE: str is modified by modifying the '\0' at the
899 end */
900 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
902 int saved_parse_flags;
903 const int *saved_macro_ptr;
905 saved_parse_flags = parse_flags;
906 saved_macro_ptr = macro_ptr;
908 tcc_open_bf(s1, ":asm:", len);
909 memcpy(file->buffer, str, len);
911 macro_ptr = NULL;
912 tcc_assemble_internal(s1, 0);
913 tcc_close();
915 parse_flags = saved_parse_flags;
916 macro_ptr = saved_macro_ptr;
919 /* find a constraint by its number or id (gcc 3 extended
920 syntax). return -1 if not found. Return in *pp in char after the
921 constraint */
922 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
923 const char *name, const char **pp)
925 int index;
926 TokenSym *ts;
927 const char *p;
929 if (isnum(*name)) {
930 index = 0;
931 while (isnum(*name)) {
932 index = (index * 10) + (*name) - '0';
933 name++;
935 if ((unsigned)index >= nb_operands)
936 index = -1;
937 } else if (*name == '[') {
938 name++;
939 p = strchr(name, ']');
940 if (p) {
941 ts = tok_alloc(name, p - name);
942 for(index = 0; index < nb_operands; index++) {
943 if (operands[index].id == ts->tok)
944 goto found;
946 index = -1;
947 found:
948 name = p + 1;
949 } else {
950 index = -1;
952 } else {
953 index = -1;
955 if (pp)
956 *pp = name;
957 return index;
960 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
961 int nb_outputs,
962 CString *out_str, CString *in_str)
964 int c, index, modifier;
965 const char *str;
966 ASMOperand *op;
967 SValue sv;
969 cstr_new(out_str);
970 str = in_str->data;
971 for(;;) {
972 c = *str++;
973 if (c == '%') {
974 if (*str == '%') {
975 str++;
976 goto add_char;
978 modifier = 0;
979 if (*str == 'c' || *str == 'n' ||
980 *str == 'b' || *str == 'w' || *str == 'h')
981 modifier = *str++;
982 index = find_constraint(operands, nb_operands, str, &str);
983 if (index < 0)
984 tcc_error("invalid operand reference after %%");
985 op = &operands[index];
986 sv = *op->vt;
987 if (op->reg >= 0) {
988 sv.r = op->reg;
989 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
990 sv.r |= VT_LVAL;
992 subst_asm_operand(out_str, &sv, modifier);
993 } else {
994 add_char:
995 cstr_ccat(out_str, c);
996 if (c == '\0')
997 break;
1003 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1004 int is_output)
1006 ASMOperand *op;
1007 int nb_operands;
1009 if (tok != ':') {
1010 nb_operands = *nb_operands_ptr;
1011 for(;;) {
1012 if (nb_operands >= MAX_ASM_OPERANDS)
1013 tcc_error("too many asm operands");
1014 op = &operands[nb_operands++];
1015 op->id = 0;
1016 if (tok == '[') {
1017 next();
1018 if (tok < TOK_IDENT)
1019 expect("identifier");
1020 op->id = tok;
1021 next();
1022 skip(']');
1024 if (tok != TOK_STR)
1025 expect("string constant");
1026 op->constraint = tcc_malloc(tokc.str.size);
1027 strcpy(op->constraint, tokc.str.data);
1028 next();
1029 skip('(');
1030 gexpr();
1031 if (is_output) {
1032 test_lvalue();
1033 } else {
1034 /* we want to avoid LLOCAL case, except when the 'm'
1035 constraint is used. Note that it may come from
1036 register storage, so we need to convert (reg)
1037 case */
1038 if ((vtop->r & VT_LVAL) &&
1039 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1040 (vtop->r & VT_VALMASK) < VT_CONST) &&
1041 !strchr(op->constraint, 'm')) {
1042 gv(RC_INT);
1045 op->vt = vtop;
1046 skip(')');
1047 if (tok == ',') {
1048 next();
1049 } else {
1050 break;
1053 *nb_operands_ptr = nb_operands;
1057 /* parse the GCC asm() instruction */
1058 ST_FUNC void asm_instr(void)
1060 CString astr, astr1;
1061 ASMOperand operands[MAX_ASM_OPERANDS];
1062 int nb_outputs, nb_operands, i, must_subst, out_reg;
1063 uint8_t clobber_regs[NB_ASM_REGS];
1065 next();
1066 /* since we always generate the asm() instruction, we can ignore
1067 volatile */
1068 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1069 next();
1071 parse_asm_str(&astr);
1072 nb_operands = 0;
1073 nb_outputs = 0;
1074 must_subst = 0;
1075 memset(clobber_regs, 0, sizeof(clobber_regs));
1076 if (tok == ':') {
1077 next();
1078 must_subst = 1;
1079 /* output args */
1080 parse_asm_operands(operands, &nb_operands, 1);
1081 nb_outputs = nb_operands;
1082 if (tok == ':') {
1083 next();
1084 if (tok != ')') {
1085 /* input args */
1086 parse_asm_operands(operands, &nb_operands, 0);
1087 if (tok == ':') {
1088 /* clobber list */
1089 /* XXX: handle registers */
1090 next();
1091 for(;;) {
1092 if (tok != TOK_STR)
1093 expect("string constant");
1094 asm_clobber(clobber_regs, tokc.str.data);
1095 next();
1096 if (tok == ',') {
1097 next();
1098 } else {
1099 break;
1106 skip(')');
1107 /* NOTE: we do not eat the ';' so that we can restore the current
1108 token after the assembler parsing */
1109 if (tok != ';')
1110 expect("';'");
1112 /* save all values in the memory */
1113 save_regs(0);
1115 /* compute constraints */
1116 asm_compute_constraints(operands, nb_operands, nb_outputs,
1117 clobber_regs, &out_reg);
1119 /* substitute the operands in the asm string. No substitution is
1120 done if no operands (GCC behaviour) */
1121 #ifdef ASM_DEBUG
1122 printf("asm: \"%s\"\n", (char *)astr.data);
1123 #endif
1124 if (must_subst) {
1125 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1126 cstr_free(&astr);
1127 } else {
1128 astr1 = astr;
1130 #ifdef ASM_DEBUG
1131 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1132 #endif
1134 /* generate loads */
1135 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1136 clobber_regs, out_reg);
1138 /* assemble the string with tcc internal assembler */
1139 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1141 /* restore the current C token */
1142 next();
1144 /* store the output values if needed */
1145 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1146 clobber_regs, out_reg);
1148 /* free everything */
1149 for(i=0;i<nb_operands;i++) {
1150 ASMOperand *op;
1151 op = &operands[i];
1152 tcc_free(op->constraint);
1153 vpop();
1155 cstr_free(&astr1);
1158 ST_FUNC void asm_global_instr(void)
1160 CString astr;
1162 next();
1163 parse_asm_str(&astr);
1164 skip(')');
1165 /* NOTE: we do not eat the ';' so that we can restore the current
1166 token after the assembler parsing */
1167 if (tok != ';')
1168 expect("';'");
1170 #ifdef ASM_DEBUG
1171 printf("asm_global: \"%s\"\n", (char *)astr.data);
1172 #endif
1173 cur_text_section = text_section;
1174 ind = cur_text_section->data_offset;
1176 /* assemble the string with tcc internal assembler */
1177 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1179 cur_text_section->data_offset = ind;
1181 /* restore the current C token */
1182 next();
1184 cstr_free(&astr);
1186 #endif /* CONFIG_TCC_ASM */