inline asm: Accept 'p' constraint and 'P' template mod
[tinycc.git] / tccasm.c
blobb3e9eb483c8082ca4fed024e2de47f6f24ff5486
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 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
264 asm_expr_sum(s1, pe);
267 ST_FUNC int asm_int_expr(TCCState *s1)
269 ExprValue e;
270 asm_expr(s1, &e);
271 if (e.sym)
272 expect("constant");
273 return e.v;
276 /* NOTE: the same name space as C labels is used to avoid using too
277 much memory when storing labels in TokenStrings */
278 static void asm_new_label1(TCCState *s1, int label, int is_local,
279 int sh_num, int value)
281 Sym *sym;
283 sym = label_find(label);
284 if (sym) {
285 if (sym->r) {
286 /* the label is already defined */
287 if (!is_local) {
288 tcc_error("assembler label '%s' already defined",
289 get_tok_str(label, NULL));
290 } else {
291 /* redefinition of local labels is possible */
292 goto new_label;
295 } else {
296 new_label:
297 sym = label_push(&s1->asm_labels, label, 0);
298 sym->type.t = VT_STATIC | VT_VOID;
300 sym->r = sh_num;
301 sym->jnext = value;
304 static void asm_new_label(TCCState *s1, int label, int is_local)
306 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
309 static void asm_free_labels(TCCState *st)
311 Sym *s, *s1;
312 Section *sec;
314 for(s = st->asm_labels; s != NULL; s = s1) {
315 s1 = s->prev;
316 /* define symbol value in object file */
317 if (s->r) {
318 if (s->r == SHN_ABS)
319 sec = SECTION_ABS;
320 else
321 sec = st->sections[s->r];
322 put_extern_sym2(s, sec, s->jnext, 0, 0);
324 /* remove label */
325 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
326 sym_free(s);
328 st->asm_labels = NULL;
331 static void use_section1(TCCState *s1, Section *sec)
333 cur_text_section->data_offset = ind;
334 cur_text_section = sec;
335 ind = cur_text_section->data_offset;
338 static void use_section(TCCState *s1, const char *name)
340 Section *sec;
341 sec = find_section(s1, name);
342 use_section1(s1, sec);
345 static void push_section(TCCState *s1, const char *name)
347 Section *sec = find_section(s1, name);
348 sec->prev = cur_text_section;
349 use_section1(s1, sec);
352 static void pop_section(TCCState *s1)
354 Section *prev = cur_text_section->prev;
355 if (!prev)
356 tcc_error(".popsection without .pushsection");
357 cur_text_section->prev = NULL;
358 use_section1(s1, prev);
361 static void asm_parse_directive(TCCState *s1)
363 int n, offset, v, size, tok1;
364 Section *sec;
365 uint8_t *ptr;
367 /* assembler directive */
368 sec = cur_text_section;
369 switch(tok) {
370 case TOK_ASMDIR_align:
371 case TOK_ASMDIR_balign:
372 case TOK_ASMDIR_p2align:
373 case TOK_ASMDIR_skip:
374 case TOK_ASMDIR_space:
375 tok1 = tok;
376 next();
377 n = asm_int_expr(s1);
378 if (tok1 == TOK_ASMDIR_p2align)
380 if (n < 0 || n > 30)
381 tcc_error("invalid p2align, must be between 0 and 30");
382 n = 1 << n;
383 tok1 = TOK_ASMDIR_align;
385 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
386 if (n < 0 || (n & (n-1)) != 0)
387 tcc_error("alignment must be a positive power of two");
388 offset = (ind + n - 1) & -n;
389 size = offset - ind;
390 /* the section must have a compatible alignment */
391 if (sec->sh_addralign < n)
392 sec->sh_addralign = n;
393 } else {
394 size = n;
396 v = 0;
397 if (tok == ',') {
398 next();
399 v = asm_int_expr(s1);
401 zero_pad:
402 if (sec->sh_type != SHT_NOBITS) {
403 sec->data_offset = ind;
404 ptr = section_ptr_add(sec, size);
405 memset(ptr, v, size);
407 ind += size;
408 break;
409 case TOK_ASMDIR_quad:
410 next();
411 for(;;) {
412 uint64_t vl;
413 const char *p;
415 p = tokc.str.data;
416 if (tok != TOK_PPNUM) {
417 error_constant:
418 tcc_error("64 bit constant");
420 vl = strtoll(p, (char **)&p, 0);
421 if (*p != '\0')
422 goto error_constant;
423 next();
424 if (sec->sh_type != SHT_NOBITS) {
425 /* XXX: endianness */
426 gen_le32(vl);
427 gen_le32(vl >> 32);
428 } else {
429 ind += 8;
431 if (tok != ',')
432 break;
433 next();
435 break;
436 case TOK_ASMDIR_byte:
437 size = 1;
438 goto asm_data;
439 case TOK_ASMDIR_word:
440 case TOK_ASMDIR_short:
441 size = 2;
442 goto asm_data;
443 case TOK_ASMDIR_long:
444 case TOK_ASMDIR_int:
445 size = 4;
446 asm_data:
447 next();
448 for(;;) {
449 ExprValue e;
450 asm_expr(s1, &e);
451 if (sec->sh_type != SHT_NOBITS) {
452 if (size == 4) {
453 gen_expr32(&e);
454 } else {
455 if (e.sym)
456 expect("constant");
457 if (size == 1)
458 g(e.v);
459 else
460 gen_le16(e.v);
462 } else {
463 ind += size;
465 if (tok != ',')
466 break;
467 next();
469 break;
470 case TOK_ASMDIR_fill:
472 int repeat, size, val, i, j;
473 uint8_t repeat_buf[8];
474 next();
475 repeat = asm_int_expr(s1);
476 if (repeat < 0) {
477 tcc_error("repeat < 0; .fill ignored");
478 break;
480 size = 1;
481 val = 0;
482 if (tok == ',') {
483 next();
484 size = asm_int_expr(s1);
485 if (size < 0) {
486 tcc_error("size < 0; .fill ignored");
487 break;
489 if (size > 8)
490 size = 8;
491 if (tok == ',') {
492 next();
493 val = asm_int_expr(s1);
496 /* XXX: endianness */
497 repeat_buf[0] = val;
498 repeat_buf[1] = val >> 8;
499 repeat_buf[2] = val >> 16;
500 repeat_buf[3] = val >> 24;
501 repeat_buf[4] = 0;
502 repeat_buf[5] = 0;
503 repeat_buf[6] = 0;
504 repeat_buf[7] = 0;
505 for(i = 0; i < repeat; i++) {
506 for(j = 0; j < size; j++) {
507 g(repeat_buf[j]);
511 break;
512 case TOK_ASMDIR_rept:
514 int repeat;
515 TokenString *init_str;
516 ParseState saved_parse_state = {0};
517 next();
518 repeat = asm_int_expr(s1);
519 init_str = tok_str_alloc();
520 next();
521 while ((tok != TOK_ASMDIR_endr) && (tok != CH_EOF)) {
522 tok_str_add_tok(init_str);
523 next();
525 if (tok == CH_EOF) tcc_error("we at end of file, .endr not found");
526 next();
527 tok_str_add(init_str, -1);
528 tok_str_add(init_str, 0);
529 save_parse_state(&saved_parse_state);
530 begin_macro(init_str, 1);
531 while (repeat-- > 0) {
532 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS));
533 macro_ptr = init_str->str;
535 end_macro();
536 restore_parse_state(&saved_parse_state);
537 break;
539 case TOK_ASMDIR_org:
541 unsigned long n;
542 ExprValue e;
543 next();
544 asm_expr(s1, &e);
545 n = e.v;
546 if (e.sym) {
547 if (e.sym->r != cur_text_section->sh_num)
548 expect("constant or same-section symbol");
549 n += e.sym->jnext;
551 if (n < ind)
552 tcc_error("attempt to .org backwards");
553 v = 0;
554 size = n - ind;
555 goto zero_pad;
557 break;
558 case TOK_ASMDIR_globl:
559 case TOK_ASMDIR_global:
560 case TOK_ASMDIR_weak:
561 case TOK_ASMDIR_hidden:
562 tok1 = tok;
563 do {
564 Sym *sym;
566 next();
567 sym = label_find(tok);
568 if (!sym) {
569 sym = label_push(&s1->asm_labels, tok, 0);
570 sym->type.t = VT_VOID;
572 if (tok1 != TOK_ASMDIR_hidden)
573 sym->type.t &= ~VT_STATIC;
574 if (tok1 == TOK_ASMDIR_weak)
575 sym->type.t |= VT_WEAK;
576 else if (tok1 == TOK_ASMDIR_hidden)
577 sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
578 next();
579 } while (tok == ',');
580 break;
581 case TOK_ASMDIR_string:
582 case TOK_ASMDIR_ascii:
583 case TOK_ASMDIR_asciz:
585 const uint8_t *p;
586 int i, size, t;
588 t = tok;
589 next();
590 for(;;) {
591 if (tok != TOK_STR)
592 expect("string constant");
593 p = tokc.str.data;
594 size = tokc.str.size;
595 if (t == TOK_ASMDIR_ascii && size > 0)
596 size--;
597 for(i = 0; i < size; i++)
598 g(p[i]);
599 next();
600 if (tok == ',') {
601 next();
602 } else if (tok != TOK_STR) {
603 break;
607 break;
608 case TOK_ASMDIR_text:
609 case TOK_ASMDIR_data:
610 case TOK_ASMDIR_bss:
612 char sname[64];
613 tok1 = tok;
614 n = 0;
615 next();
616 if (tok != ';' && tok != TOK_LINEFEED) {
617 n = asm_int_expr(s1);
618 next();
620 if (n)
621 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
622 else
623 sprintf(sname, "%s", get_tok_str(tok1, NULL));
624 use_section(s1, sname);
626 break;
627 case TOK_ASMDIR_file:
629 char filename[512];
631 filename[0] = '\0';
632 next();
634 if (tok == TOK_STR)
635 pstrcat(filename, sizeof(filename), tokc.str.data);
636 else
637 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
639 if (s1->warn_unsupported)
640 tcc_warning("ignoring .file %s", filename);
642 next();
644 break;
645 case TOK_ASMDIR_ident:
647 char ident[256];
649 ident[0] = '\0';
650 next();
652 if (tok == TOK_STR)
653 pstrcat(ident, sizeof(ident), tokc.str.data);
654 else
655 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
657 if (s1->warn_unsupported)
658 tcc_warning("ignoring .ident %s", ident);
660 next();
662 break;
663 case TOK_ASMDIR_size:
665 Sym *sym;
667 next();
668 sym = label_find(tok);
669 if (!sym) {
670 tcc_error("label not found: %s", get_tok_str(tok, NULL));
673 /* XXX .size name,label2-label1 */
674 if (s1->warn_unsupported)
675 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
677 next();
678 skip(',');
679 while (tok != '\n' && tok != CH_EOF) {
680 next();
683 break;
684 case TOK_ASMDIR_type:
686 Sym *sym;
687 const char *newtype;
689 next();
690 sym = label_find(tok);
691 if (!sym) {
692 sym = label_push(&s1->asm_labels, tok, 0);
693 sym->type.t = VT_VOID;
696 next();
697 skip(',');
698 if (tok == TOK_STR) {
699 newtype = tokc.str.data;
700 } else {
701 if (tok == '@' || tok == '%')
702 next();
703 newtype = get_tok_str(tok, NULL);
706 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
707 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
709 else if (s1->warn_unsupported)
710 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
711 get_tok_str(sym->v, NULL), sym->type.t, newtype);
713 next();
715 break;
716 case TOK_ASMDIR_pushsection:
717 case TOK_ASMDIR_section:
719 char sname[256];
720 int old_nb_section = s1->nb_sections;
722 tok1 = tok;
723 /* XXX: support more options */
724 next();
725 sname[0] = '\0';
726 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
727 if (tok == TOK_STR)
728 pstrcat(sname, sizeof(sname), tokc.str.data);
729 else
730 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
731 next();
733 if (tok == ',') {
734 /* skip section options */
735 next();
736 if (tok != TOK_STR)
737 expect("string constant");
738 next();
739 if (tok == ',') {
740 next();
741 if (tok == '@' || tok == '%')
742 next();
743 next();
746 last_text_section = cur_text_section;
747 if (tok1 == TOK_ASMDIR_section)
748 use_section(s1, sname);
749 else
750 push_section(s1, sname);
751 /* If we just allocated a new section reset its alignment to
752 1. new_section normally acts for GCC compatibility and
753 sets alignment to PTR_SIZE. The assembler behaves different. */
754 if (old_nb_section != s1->nb_sections)
755 cur_text_section->sh_addralign = 1;
757 break;
758 case TOK_ASMDIR_previous:
760 Section *sec;
761 next();
762 if (!last_text_section)
763 tcc_error("no previous section referenced");
764 sec = cur_text_section;
765 use_section1(s1, last_text_section);
766 last_text_section = sec;
768 break;
769 case TOK_ASMDIR_popsection:
770 next();
771 pop_section(s1);
772 break;
773 #ifdef TCC_TARGET_I386
774 case TOK_ASMDIR_code16:
776 next();
777 s1->seg_size = 16;
779 break;
780 case TOK_ASMDIR_code32:
782 next();
783 s1->seg_size = 32;
785 break;
786 #endif
787 #ifdef TCC_TARGET_X86_64
788 /* added for compatibility with GAS */
789 case TOK_ASMDIR_code64:
790 next();
791 break;
792 #endif
793 default:
794 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
795 break;
800 /* assemble a file */
801 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
803 int opcode;
805 /* XXX: undefine C labels */
807 ch = file->buf_ptr[0];
808 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
809 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
810 if (do_preprocess)
811 parse_flags |= PARSE_FLAG_PREPROCESS;
812 next();
813 for(;;) {
814 if (tok == TOK_EOF)
815 break;
816 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
817 redo:
818 if (tok == '#') {
819 /* horrible gas comment */
820 while (tok != TOK_LINEFEED)
821 next();
822 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
823 asm_parse_directive(s1);
824 } else if (tok == TOK_PPNUM) {
825 const char *p;
826 int n;
827 p = tokc.str.data;
828 n = strtoul(p, (char **)&p, 10);
829 if (*p != '\0')
830 expect("':'");
831 /* new local label */
832 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
833 next();
834 skip(':');
835 goto redo;
836 } else if (tok >= TOK_IDENT) {
837 /* instruction or label */
838 opcode = tok;
839 next();
840 if (tok == ':') {
841 /* handle "extern void vide(void); __asm__("vide: ret");" as
842 "__asm__("globl vide\nvide: ret");" */
843 Sym *sym = sym_find(opcode);
844 if (sym && (sym->type.t & VT_EXTERN) && nocode_wanted) {
845 sym = label_find(opcode);
846 if (!sym) {
847 sym = label_push(&s1->asm_labels, opcode, 0);
848 sym->type.t = VT_VOID;
851 /* new label */
852 asm_new_label(s1, opcode, 0);
853 next();
854 goto redo;
855 } else if (tok == '=') {
856 int n;
857 next();
858 n = asm_int_expr(s1);
859 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
860 goto redo;
861 } else {
862 asm_opcode(s1, opcode);
865 /* end of line */
866 if (tok != ';' && tok != TOK_LINEFEED){
867 expect("end of line");
869 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
870 next();
873 asm_free_labels(s1);
875 return 0;
878 /* Assemble the current file */
879 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
881 Sym *define_start;
882 int ret;
884 preprocess_start(s1);
886 /* default section is text */
887 cur_text_section = text_section;
888 ind = cur_text_section->data_offset;
890 define_start = define_stack;
892 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
893 symbols can be safely used */
894 put_elf_sym(symtab_section, 0, 0,
895 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
896 SHN_ABS, file->filename);
898 ret = tcc_assemble_internal(s1, do_preprocess);
900 cur_text_section->data_offset = ind;
902 free_defines(define_start);
904 return ret;
907 /********************************************************************/
908 /* GCC inline asm support */
910 /* assemble the string 'str' in the current C compilation unit without
911 C preprocessing. NOTE: str is modified by modifying the '\0' at the
912 end */
913 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
915 int saved_parse_flags;
916 const int *saved_macro_ptr;
918 saved_parse_flags = parse_flags;
919 saved_macro_ptr = macro_ptr;
921 tcc_open_bf(s1, ":asm:", len);
922 memcpy(file->buffer, str, len);
924 macro_ptr = NULL;
925 tcc_assemble_internal(s1, 0);
926 tcc_close();
928 parse_flags = saved_parse_flags;
929 macro_ptr = saved_macro_ptr;
932 /* find a constraint by its number or id (gcc 3 extended
933 syntax). return -1 if not found. Return in *pp in char after the
934 constraint */
935 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
936 const char *name, const char **pp)
938 int index;
939 TokenSym *ts;
940 const char *p;
942 if (isnum(*name)) {
943 index = 0;
944 while (isnum(*name)) {
945 index = (index * 10) + (*name) - '0';
946 name++;
948 if ((unsigned)index >= nb_operands)
949 index = -1;
950 } else if (*name == '[') {
951 name++;
952 p = strchr(name, ']');
953 if (p) {
954 ts = tok_alloc(name, p - name);
955 for(index = 0; index < nb_operands; index++) {
956 if (operands[index].id == ts->tok)
957 goto found;
959 index = -1;
960 found:
961 name = p + 1;
962 } else {
963 index = -1;
965 } else {
966 index = -1;
968 if (pp)
969 *pp = name;
970 return index;
973 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
974 int nb_outputs,
975 CString *out_str, CString *in_str)
977 int c, index, modifier;
978 const char *str;
979 ASMOperand *op;
980 SValue sv;
982 cstr_new(out_str);
983 str = in_str->data;
984 for(;;) {
985 c = *str++;
986 if (c == '%') {
987 if (*str == '%') {
988 str++;
989 goto add_char;
991 modifier = 0;
992 if (*str == 'c' || *str == 'n' ||
993 *str == 'b' || *str == 'w' ||
994 *str == 'h' || *str == 'k' || *str == 'q' ||
995 /* P in GCC would add "@PLT" to symbol refs in PIC mode
996 Ignore this in TCC. */
997 *str == 'P')
998 modifier = *str++;
999 index = find_constraint(operands, nb_operands, str, &str);
1000 if (index < 0)
1001 tcc_error("invalid operand reference after %%");
1002 op = &operands[index];
1003 sv = *op->vt;
1004 if (op->reg >= 0) {
1005 sv.r = op->reg;
1006 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1007 sv.r |= VT_LVAL;
1009 subst_asm_operand(out_str, &sv, modifier);
1010 } else {
1011 add_char:
1012 cstr_ccat(out_str, c);
1013 if (c == '\0')
1014 break;
1020 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1021 int is_output)
1023 ASMOperand *op;
1024 int nb_operands;
1026 if (tok != ':') {
1027 nb_operands = *nb_operands_ptr;
1028 for(;;) {
1029 if (nb_operands >= MAX_ASM_OPERANDS)
1030 tcc_error("too many asm operands");
1031 op = &operands[nb_operands++];
1032 op->id = 0;
1033 if (tok == '[') {
1034 next();
1035 if (tok < TOK_IDENT)
1036 expect("identifier");
1037 op->id = tok;
1038 next();
1039 skip(']');
1041 if (tok != TOK_STR)
1042 expect("string constant");
1043 op->constraint = tcc_malloc(tokc.str.size);
1044 strcpy(op->constraint, tokc.str.data);
1045 next();
1046 skip('(');
1047 gexpr();
1048 if (is_output) {
1049 if (!(vtop->type.t & VT_ARRAY))
1050 test_lvalue();
1051 } else {
1052 /* we want to avoid LLOCAL case, except when the 'm'
1053 constraint is used. Note that it may come from
1054 register storage, so we need to convert (reg)
1055 case */
1056 if ((vtop->r & VT_LVAL) &&
1057 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1058 (vtop->r & VT_VALMASK) < VT_CONST) &&
1059 !strchr(op->constraint, 'm')) {
1060 gv(RC_INT);
1063 op->vt = vtop;
1064 skip(')');
1065 if (tok == ',') {
1066 next();
1067 } else {
1068 break;
1071 *nb_operands_ptr = nb_operands;
1075 /* parse the GCC asm() instruction */
1076 ST_FUNC void asm_instr(void)
1078 CString astr, astr1;
1079 ASMOperand operands[MAX_ASM_OPERANDS];
1080 int nb_outputs, nb_operands, i, must_subst, out_reg;
1081 uint8_t clobber_regs[NB_ASM_REGS];
1083 next();
1084 /* since we always generate the asm() instruction, we can ignore
1085 volatile */
1086 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1087 next();
1089 parse_asm_str(&astr);
1090 nb_operands = 0;
1091 nb_outputs = 0;
1092 must_subst = 0;
1093 memset(clobber_regs, 0, sizeof(clobber_regs));
1094 if (tok == ':') {
1095 next();
1096 must_subst = 1;
1097 /* output args */
1098 parse_asm_operands(operands, &nb_operands, 1);
1099 nb_outputs = nb_operands;
1100 if (tok == ':') {
1101 next();
1102 if (tok != ')') {
1103 /* input args */
1104 parse_asm_operands(operands, &nb_operands, 0);
1105 if (tok == ':') {
1106 /* clobber list */
1107 /* XXX: handle registers */
1108 next();
1109 for(;;) {
1110 if (tok != TOK_STR)
1111 expect("string constant");
1112 asm_clobber(clobber_regs, tokc.str.data);
1113 next();
1114 if (tok == ',') {
1115 next();
1116 } else {
1117 break;
1124 skip(')');
1125 /* NOTE: we do not eat the ';' so that we can restore the current
1126 token after the assembler parsing */
1127 if (tok != ';')
1128 expect("';'");
1130 /* save all values in the memory */
1131 save_regs(0);
1133 /* compute constraints */
1134 asm_compute_constraints(operands, nb_operands, nb_outputs,
1135 clobber_regs, &out_reg);
1137 /* substitute the operands in the asm string. No substitution is
1138 done if no operands (GCC behaviour) */
1139 #ifdef ASM_DEBUG
1140 printf("asm: \"%s\"\n", (char *)astr.data);
1141 #endif
1142 if (must_subst) {
1143 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1144 cstr_free(&astr);
1145 } else {
1146 astr1 = astr;
1148 #ifdef ASM_DEBUG
1149 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1150 #endif
1152 /* generate loads */
1153 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1154 clobber_regs, out_reg);
1156 /* assemble the string with tcc internal assembler */
1157 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1159 /* restore the current C token */
1160 next();
1162 /* store the output values if needed */
1163 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1164 clobber_regs, out_reg);
1166 /* free everything */
1167 for(i=0;i<nb_operands;i++) {
1168 ASMOperand *op;
1169 op = &operands[i];
1170 tcc_free(op->constraint);
1171 vpop();
1173 cstr_free(&astr1);
1176 ST_FUNC void asm_global_instr(void)
1178 CString astr;
1180 next();
1181 parse_asm_str(&astr);
1182 skip(')');
1183 /* NOTE: we do not eat the ';' so that we can restore the current
1184 token after the assembler parsing */
1185 if (tok != ';')
1186 expect("';'");
1188 #ifdef ASM_DEBUG
1189 printf("asm_global: \"%s\"\n", (char *)astr.data);
1190 #endif
1191 cur_text_section = text_section;
1192 ind = cur_text_section->data_offset;
1194 /* assemble the string with tcc internal assembler */
1195 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1197 cur_text_section->data_offset = ind;
1199 /* restore the current C token */
1200 next();
1202 cstr_free(&astr);
1204 #endif /* CONFIG_TCC_ASM */