Fix another corner case with C/asm symtable
[tinycc.git] / tccasm.c
blob15f1e43c1019fbfb054ece4b958e4e0476f35ba9
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 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
35 static Sym sym_dot;
37 static Sym *asm_label_find(int v)
39 Sym *sym = sym_find(v);
40 while (sym && sym->sym_scope)
41 sym = sym->prev_tok;
42 return sym;
45 static Sym *asm_label_push(int v, int t)
47 Sym *sym = global_identifier_push_1(&tcc_state->asm_labels, v, t, 0);
48 /* We always add VT_EXTERN, for sym definition that's tentative
49 (for .set, removed for real defs), for mere references it's correct
50 as is. */
51 sym->type.t |= VT_VOID | VT_EXTERN;
52 sym->r = VT_CONST | VT_SYM;
53 return sym;
56 /* Return a symbol we can use inside the assembler, having name NAME.
57 Symbols from asm and C source share a namespace. If we generate
58 an asm symbol it's also a (file-global) C symbol, but it's
59 either not accessible by name (like "L.123"), or its type information
60 is such that it's not usable without a proper C declaration.
62 Sometimes we need symbols accessible by name from asm, which
63 are anonymous in C, in this case CSYM can be used to transfer
64 all information from that symbol to the (possibly newly created)
65 asm symbol. */
66 ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
68 Sym *sym = asm_label_find(name);
69 if (!sym) {
70 sym = asm_label_push(name, 0);
71 if (csym)
72 sym->c = csym->c;
74 return sym;
77 /* We do not use the C expression parser to handle symbols. Maybe the
78 C expression parser could be tweaked to do so. */
80 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
82 Sym *sym;
83 int op, label;
84 uint64_t n;
85 const char *p;
87 switch(tok) {
88 case TOK_PPNUM:
89 p = tokc.str.data;
90 n = strtoull(p, (char **)&p, 0);
91 if (*p == 'b' || *p == 'f') {
92 /* backward or forward label */
93 label = asm_get_local_label_name(s1, n);
94 sym = asm_label_find(label);
95 if (*p == 'b') {
96 /* backward : find the last corresponding defined label */
97 if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))
98 sym = sym->prev_tok;
99 if (!sym)
100 tcc_error("local label '%d' not found backward", n);
101 } else {
102 /* forward */
103 if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) {
104 /* if the last label is defined, then define a new one */
105 sym = asm_label_push(label, VT_STATIC);
108 pe->v = 0;
109 pe->sym = sym;
110 pe->pcrel = 0;
111 } else if (*p == '\0') {
112 pe->v = n;
113 pe->sym = NULL;
114 pe->pcrel = 0;
115 } else {
116 tcc_error("invalid number syntax");
118 next();
119 break;
120 case '+':
121 next();
122 asm_expr_unary(s1, pe);
123 break;
124 case '-':
125 case '~':
126 op = tok;
127 next();
128 asm_expr_unary(s1, pe);
129 if (pe->sym)
130 tcc_error("invalid operation with label");
131 if (op == '-')
132 pe->v = -pe->v;
133 else
134 pe->v = ~pe->v;
135 break;
136 case TOK_CCHAR:
137 case TOK_LCHAR:
138 pe->v = tokc.i;
139 pe->sym = NULL;
140 pe->pcrel = 0;
141 next();
142 break;
143 case '(':
144 next();
145 asm_expr(s1, pe);
146 skip(')');
147 break;
148 case '.':
149 pe->v = 0;
150 pe->sym = &sym_dot;
151 pe->pcrel = 0;
152 sym_dot.type.t = VT_VOID | VT_STATIC;
153 sym_dot.c = -1;
154 tcc_state->esym_dot.st_shndx = cur_text_section->sh_num;
155 tcc_state->esym_dot.st_value = ind;
156 next();
157 break;
158 default:
159 if (tok >= TOK_IDENT) {
160 ElfSym *esym;
161 /* label case : if the label was not found, add one */
162 sym = get_asm_sym(tok, NULL);
163 esym = elfsym(sym);
164 if (esym && esym->st_shndx == SHN_ABS) {
165 /* if absolute symbol, no need to put a symbol value */
166 pe->v = esym->st_value;
167 pe->sym = NULL;
168 pe->pcrel = 0;
169 } else {
170 pe->v = 0;
171 pe->sym = sym;
172 pe->pcrel = 0;
174 next();
175 } else {
176 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
178 break;
182 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
184 int op;
185 ExprValue e2;
187 asm_expr_unary(s1, pe);
188 for(;;) {
189 op = tok;
190 if (op != '*' && op != '/' && op != '%' &&
191 op != TOK_SHL && op != TOK_SAR)
192 break;
193 next();
194 asm_expr_unary(s1, &e2);
195 if (pe->sym || e2.sym)
196 tcc_error("invalid operation with label");
197 switch(op) {
198 case '*':
199 pe->v *= e2.v;
200 break;
201 case '/':
202 if (e2.v == 0) {
203 div_error:
204 tcc_error("division by zero");
206 pe->v /= e2.v;
207 break;
208 case '%':
209 if (e2.v == 0)
210 goto div_error;
211 pe->v %= e2.v;
212 break;
213 case TOK_SHL:
214 pe->v <<= e2.v;
215 break;
216 default:
217 case TOK_SAR:
218 pe->v >>= e2.v;
219 break;
224 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
226 int op;
227 ExprValue e2;
229 asm_expr_prod(s1, pe);
230 for(;;) {
231 op = tok;
232 if (op != '&' && op != '|' && op != '^')
233 break;
234 next();
235 asm_expr_prod(s1, &e2);
236 if (pe->sym || e2.sym)
237 tcc_error("invalid operation with label");
238 switch(op) {
239 case '&':
240 pe->v &= e2.v;
241 break;
242 case '|':
243 pe->v |= e2.v;
244 break;
245 default:
246 case '^':
247 pe->v ^= e2.v;
248 break;
253 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
255 int op;
256 ExprValue e2;
258 asm_expr_logic(s1, pe);
259 for(;;) {
260 op = tok;
261 if (op != '+' && op != '-')
262 break;
263 next();
264 asm_expr_logic(s1, &e2);
265 if (op == '+') {
266 if (pe->sym != NULL && e2.sym != NULL)
267 goto cannot_relocate;
268 pe->v += e2.v;
269 if (pe->sym == NULL && e2.sym != NULL)
270 pe->sym = e2.sym;
271 } else {
272 pe->v -= e2.v;
273 /* NOTE: we are less powerful than gas in that case
274 because we store only one symbol in the expression */
275 if (!e2.sym) {
276 /* OK */
277 } else if (pe->sym == e2.sym) {
278 /* OK */
279 pe->sym = NULL; /* same symbols can be subtracted to NULL */
280 } else {
281 ElfSym *esym1, *esym2;
282 esym1 = elfsym(pe->sym);
283 esym2 = elfsym(e2.sym);
284 if (esym1 && esym1->st_shndx == esym2->st_shndx
285 && esym1->st_shndx != SHN_UNDEF) {
286 /* we also accept defined symbols in the same section */
287 pe->v += esym1->st_value - esym2->st_value;
288 pe->sym = NULL;
289 } else if (esym2->st_shndx == cur_text_section->sh_num) {
290 /* When subtracting a defined symbol in current section
291 this actually makes the value PC-relative. */
292 pe->v -= esym2->st_value - ind - 4;
293 pe->pcrel = 1;
294 e2.sym = NULL;
295 } else {
296 cannot_relocate:
297 tcc_error("invalid operation with label");
304 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
306 int op;
307 ExprValue e2;
309 asm_expr_sum(s1, pe);
310 for(;;) {
311 op = tok;
312 if (op != TOK_EQ && op != TOK_NE
313 && (op > TOK_GT || op < TOK_ULE))
314 break;
315 next();
316 asm_expr_sum(s1, &e2);
317 if (pe->sym || e2.sym)
318 tcc_error("invalid operation with label");
319 switch(op) {
320 case TOK_EQ:
321 pe->v = pe->v == e2.v;
322 break;
323 case TOK_NE:
324 pe->v = pe->v != e2.v;
325 break;
326 case TOK_LT:
327 pe->v = (int64_t)pe->v < (int64_t)e2.v;
328 break;
329 case TOK_GE:
330 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
331 break;
332 case TOK_LE:
333 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
334 break;
335 case TOK_GT:
336 pe->v = (int64_t)pe->v > (int64_t)e2.v;
337 break;
338 default:
339 break;
341 /* GAS compare results are -1/0 not 1/0. */
342 pe->v = -(int64_t)pe->v;
346 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
348 asm_expr_cmp(s1, pe);
351 ST_FUNC int asm_int_expr(TCCState *s1)
353 ExprValue e;
354 asm_expr(s1, &e);
355 if (e.sym)
356 expect("constant");
357 return e.v;
360 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
361 int sh_num, int value)
363 Sym *sym;
364 ElfSym *esym;
366 sym = asm_label_find(label);
367 if (sym) {
368 esym = elfsym(sym);
369 /* A VT_EXTERN symbol, even if it has a section is considered
370 overridable. This is how we "define" .set targets. Real
371 definitions won't have VT_EXTERN set. */
372 if (esym && esym->st_shndx != SHN_UNDEF && !(sym->type.t & VT_EXTERN)) {
373 /* the label is already defined */
374 if (!is_local) {
375 tcc_error("assembler label '%s' already defined",
376 get_tok_str(label, NULL));
377 } else {
378 /* redefinition of local labels is possible */
379 goto new_label;
382 } else {
383 new_label:
384 sym = asm_label_push(label, is_local ? VT_STATIC : 0);
386 if (!sym->c)
387 put_extern_sym2(sym, NULL, 0, 0, 0);
388 esym = elfsym(sym);
389 esym->st_shndx = sh_num;
390 esym->st_value = value;
391 return sym;
394 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
396 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
399 /* Set the value of LABEL to that of some expression (possibly
400 involving other symbols). LABEL can be overwritten later still. */
401 static Sym* set_symbol(TCCState *s1, int label)
403 long n;
404 ExprValue e;
405 Sym *sym;
406 ElfSym *esym;
407 next();
408 asm_expr(s1, &e);
409 n = e.v;
410 esym = elfsym(e.sym);
411 if (esym)
412 n += esym->st_value;
413 sym = asm_new_label1(s1, label, 0, esym ? esym->st_shndx : SHN_ABS, n);
414 elfsym(sym)->st_other |= ST_ASM_SET;
415 return sym;
418 ST_FUNC void asm_free_labels(TCCState *st)
420 Sym *s, *s1;
422 for(s = st->asm_labels; s != NULL; s = s1) {
423 ElfSym *esym = elfsym(s);
424 s1 = s->prev;
425 /* Possibly update binding and visibility from asm directives
426 if the symbol has no C decl (type is VT_VOID).*/
427 s->type.t &= ~VT_EXTERN;
428 if (esym && s->type.t == VT_VOID) {
429 if (!s->a.asmexport && esym->st_shndx != SHN_UNDEF)
430 s->type.t |= VT_STATIC;
431 if (s->a.visibility)
432 esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
433 | s->a.visibility;
434 esym->st_info = ELFW(ST_INFO)(s->a.weak ? STB_WEAK
435 : (s->type.t & VT_STATIC) ? STB_LOCAL
436 : STB_GLOBAL,
437 ELFW(ST_TYPE)(esym->st_info));
439 /* remove label */
440 table_ident[s->v - TOK_IDENT]->sym_identifier = s->prev_tok;
441 sym_free(s);
443 st->asm_labels = NULL;
446 static void use_section1(TCCState *s1, Section *sec)
448 cur_text_section->data_offset = ind;
449 cur_text_section = sec;
450 ind = cur_text_section->data_offset;
453 static void use_section(TCCState *s1, const char *name)
455 Section *sec;
456 sec = find_section(s1, name);
457 use_section1(s1, sec);
460 static void push_section(TCCState *s1, const char *name)
462 Section *sec = find_section(s1, name);
463 sec->prev = cur_text_section;
464 use_section1(s1, sec);
467 static void pop_section(TCCState *s1)
469 Section *prev = cur_text_section->prev;
470 if (!prev)
471 tcc_error(".popsection without .pushsection");
472 cur_text_section->prev = NULL;
473 use_section1(s1, prev);
476 static void asm_parse_directive(TCCState *s1, int global)
478 int n, offset, v, size, tok1;
479 Section *sec;
480 uint8_t *ptr;
482 /* assembler directive */
483 sec = cur_text_section;
484 switch(tok) {
485 case TOK_ASMDIR_align:
486 case TOK_ASMDIR_balign:
487 case TOK_ASMDIR_p2align:
488 case TOK_ASMDIR_skip:
489 case TOK_ASMDIR_space:
490 tok1 = tok;
491 next();
492 n = asm_int_expr(s1);
493 if (tok1 == TOK_ASMDIR_p2align)
495 if (n < 0 || n > 30)
496 tcc_error("invalid p2align, must be between 0 and 30");
497 n = 1 << n;
498 tok1 = TOK_ASMDIR_align;
500 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
501 if (n < 0 || (n & (n-1)) != 0)
502 tcc_error("alignment must be a positive power of two");
503 offset = (ind + n - 1) & -n;
504 size = offset - ind;
505 /* the section must have a compatible alignment */
506 if (sec->sh_addralign < n)
507 sec->sh_addralign = n;
508 } else {
509 if (n < 0)
510 n = 0;
511 size = n;
513 v = 0;
514 if (tok == ',') {
515 next();
516 v = asm_int_expr(s1);
518 zero_pad:
519 if (sec->sh_type != SHT_NOBITS) {
520 sec->data_offset = ind;
521 ptr = section_ptr_add(sec, size);
522 memset(ptr, v, size);
524 ind += size;
525 break;
526 case TOK_ASMDIR_quad:
527 #ifdef TCC_TARGET_X86_64
528 size = 8;
529 goto asm_data;
530 #else
531 next();
532 for(;;) {
533 uint64_t vl;
534 const char *p;
536 p = tokc.str.data;
537 if (tok != TOK_PPNUM) {
538 error_constant:
539 tcc_error("64 bit constant");
541 vl = strtoll(p, (char **)&p, 0);
542 if (*p != '\0')
543 goto error_constant;
544 next();
545 if (sec->sh_type != SHT_NOBITS) {
546 /* XXX: endianness */
547 gen_le32(vl);
548 gen_le32(vl >> 32);
549 } else {
550 ind += 8;
552 if (tok != ',')
553 break;
554 next();
556 break;
557 #endif
558 case TOK_ASMDIR_byte:
559 size = 1;
560 goto asm_data;
561 case TOK_ASMDIR_word:
562 case TOK_ASMDIR_short:
563 size = 2;
564 goto asm_data;
565 case TOK_ASMDIR_long:
566 case TOK_ASMDIR_int:
567 size = 4;
568 asm_data:
569 next();
570 for(;;) {
571 ExprValue e;
572 asm_expr(s1, &e);
573 if (sec->sh_type != SHT_NOBITS) {
574 if (size == 4) {
575 gen_expr32(&e);
576 #ifdef TCC_TARGET_X86_64
577 } else if (size == 8) {
578 gen_expr64(&e);
579 #endif
580 } else {
581 if (e.sym)
582 expect("constant");
583 if (size == 1)
584 g(e.v);
585 else
586 gen_le16(e.v);
588 } else {
589 ind += size;
591 if (tok != ',')
592 break;
593 next();
595 break;
596 case TOK_ASMDIR_fill:
598 int repeat, size, val, i, j;
599 uint8_t repeat_buf[8];
600 next();
601 repeat = asm_int_expr(s1);
602 if (repeat < 0) {
603 tcc_error("repeat < 0; .fill ignored");
604 break;
606 size = 1;
607 val = 0;
608 if (tok == ',') {
609 next();
610 size = asm_int_expr(s1);
611 if (size < 0) {
612 tcc_error("size < 0; .fill ignored");
613 break;
615 if (size > 8)
616 size = 8;
617 if (tok == ',') {
618 next();
619 val = asm_int_expr(s1);
622 /* XXX: endianness */
623 repeat_buf[0] = val;
624 repeat_buf[1] = val >> 8;
625 repeat_buf[2] = val >> 16;
626 repeat_buf[3] = val >> 24;
627 repeat_buf[4] = 0;
628 repeat_buf[5] = 0;
629 repeat_buf[6] = 0;
630 repeat_buf[7] = 0;
631 for(i = 0; i < repeat; i++) {
632 for(j = 0; j < size; j++) {
633 g(repeat_buf[j]);
637 break;
638 case TOK_ASMDIR_rept:
640 int repeat;
641 TokenString *init_str;
642 next();
643 repeat = asm_int_expr(s1);
644 init_str = tok_str_alloc();
645 while (next(), tok != TOK_ASMDIR_endr) {
646 if (tok == CH_EOF)
647 tcc_error("we at end of file, .endr not found");
648 tok_str_add_tok(init_str);
650 tok_str_add(init_str, -1);
651 tok_str_add(init_str, 0);
652 begin_macro(init_str, 1);
653 while (repeat-- > 0) {
654 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
655 global);
656 macro_ptr = init_str->str;
658 end_macro();
659 next();
660 break;
662 case TOK_ASMDIR_org:
664 unsigned long n;
665 ExprValue e;
666 ElfSym *esym;
667 next();
668 asm_expr(s1, &e);
669 n = e.v;
670 esym = elfsym(e.sym);
671 if (esym) {
672 if (esym->st_shndx != cur_text_section->sh_num)
673 expect("constant or same-section symbol");
674 n += esym->st_value;
676 if (n < ind)
677 tcc_error("attempt to .org backwards");
678 v = 0;
679 size = n - ind;
680 goto zero_pad;
682 break;
683 case TOK_ASMDIR_set:
684 next();
685 tok1 = tok;
686 next();
687 /* Also accept '.set stuff', but don't do anything with this.
688 It's used in GAS to set various features like '.set mips16'. */
689 if (tok == ',')
690 set_symbol(s1, tok1);
691 break;
692 case TOK_ASMDIR_globl:
693 case TOK_ASMDIR_global:
694 case TOK_ASMDIR_weak:
695 case TOK_ASMDIR_hidden:
696 tok1 = tok;
697 do {
698 Sym *sym;
700 next();
701 sym = get_asm_sym(tok, NULL);
702 if (tok1 != TOK_ASMDIR_hidden)
703 sym->type.t &= ~VT_STATIC, sym->a.asmexport = 1;
704 if (tok1 == TOK_ASMDIR_weak)
705 sym->a.weak = 1;
706 else if (tok1 == TOK_ASMDIR_hidden)
707 sym->a.visibility = STV_HIDDEN;
708 next();
709 } while (tok == ',');
710 break;
711 case TOK_ASMDIR_string:
712 case TOK_ASMDIR_ascii:
713 case TOK_ASMDIR_asciz:
715 const uint8_t *p;
716 int i, size, t;
718 t = tok;
719 next();
720 for(;;) {
721 if (tok != TOK_STR)
722 expect("string constant");
723 p = tokc.str.data;
724 size = tokc.str.size;
725 if (t == TOK_ASMDIR_ascii && size > 0)
726 size--;
727 for(i = 0; i < size; i++)
728 g(p[i]);
729 next();
730 if (tok == ',') {
731 next();
732 } else if (tok != TOK_STR) {
733 break;
737 break;
738 case TOK_ASMDIR_text:
739 case TOK_ASMDIR_data:
740 case TOK_ASMDIR_bss:
742 char sname[64];
743 tok1 = tok;
744 n = 0;
745 next();
746 if (tok != ';' && tok != TOK_LINEFEED) {
747 n = asm_int_expr(s1);
748 next();
750 if (n)
751 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
752 else
753 sprintf(sname, "%s", get_tok_str(tok1, NULL));
754 use_section(s1, sname);
756 break;
757 case TOK_ASMDIR_file:
759 char filename[512];
761 filename[0] = '\0';
762 next();
764 if (tok == TOK_STR)
765 pstrcat(filename, sizeof(filename), tokc.str.data);
766 else
767 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
769 if (s1->warn_unsupported)
770 tcc_warning("ignoring .file %s", filename);
772 next();
774 break;
775 case TOK_ASMDIR_ident:
777 char ident[256];
779 ident[0] = '\0';
780 next();
782 if (tok == TOK_STR)
783 pstrcat(ident, sizeof(ident), tokc.str.data);
784 else
785 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
787 if (s1->warn_unsupported)
788 tcc_warning("ignoring .ident %s", ident);
790 next();
792 break;
793 case TOK_ASMDIR_size:
795 Sym *sym;
797 next();
798 sym = asm_label_find(tok);
799 if (!sym) {
800 tcc_error("label not found: %s", get_tok_str(tok, NULL));
803 /* XXX .size name,label2-label1 */
804 if (s1->warn_unsupported)
805 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
807 next();
808 skip(',');
809 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
810 next();
813 break;
814 case TOK_ASMDIR_type:
816 Sym *sym;
817 const char *newtype;
819 next();
820 sym = get_asm_sym(tok, NULL);
821 next();
822 skip(',');
823 if (tok == TOK_STR) {
824 newtype = tokc.str.data;
825 } else {
826 if (tok == '@' || tok == '%')
827 next();
828 newtype = get_tok_str(tok, NULL);
831 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
832 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
834 else if (s1->warn_unsupported)
835 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
836 get_tok_str(sym->v, NULL), sym->type.t, newtype);
838 next();
840 break;
841 case TOK_ASMDIR_pushsection:
842 case TOK_ASMDIR_section:
844 char sname[256];
845 int old_nb_section = s1->nb_sections;
847 tok1 = tok;
848 /* XXX: support more options */
849 next();
850 sname[0] = '\0';
851 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
852 if (tok == TOK_STR)
853 pstrcat(sname, sizeof(sname), tokc.str.data);
854 else
855 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
856 next();
858 if (tok == ',') {
859 /* skip section options */
860 next();
861 if (tok != TOK_STR)
862 expect("string constant");
863 next();
864 if (tok == ',') {
865 next();
866 if (tok == '@' || tok == '%')
867 next();
868 next();
871 last_text_section = cur_text_section;
872 if (tok1 == TOK_ASMDIR_section)
873 use_section(s1, sname);
874 else
875 push_section(s1, sname);
876 /* If we just allocated a new section reset its alignment to
877 1. new_section normally acts for GCC compatibility and
878 sets alignment to PTR_SIZE. The assembler behaves different. */
879 if (old_nb_section != s1->nb_sections)
880 cur_text_section->sh_addralign = 1;
882 break;
883 case TOK_ASMDIR_previous:
885 Section *sec;
886 next();
887 if (!last_text_section)
888 tcc_error("no previous section referenced");
889 sec = cur_text_section;
890 use_section1(s1, last_text_section);
891 last_text_section = sec;
893 break;
894 case TOK_ASMDIR_popsection:
895 next();
896 pop_section(s1);
897 break;
898 #ifdef TCC_TARGET_I386
899 case TOK_ASMDIR_code16:
901 next();
902 s1->seg_size = 16;
904 break;
905 case TOK_ASMDIR_code32:
907 next();
908 s1->seg_size = 32;
910 break;
911 #endif
912 #ifdef TCC_TARGET_X86_64
913 /* added for compatibility with GAS */
914 case TOK_ASMDIR_code64:
915 next();
916 break;
917 #endif
918 default:
919 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
920 break;
925 /* assemble a file */
926 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
928 int opcode;
929 int saved_parse_flags = parse_flags;
931 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
932 if (do_preprocess)
933 parse_flags |= PARSE_FLAG_PREPROCESS;
934 for(;;) {
935 next();
936 if (tok == TOK_EOF)
937 break;
938 /* generate line number info */
939 if (global && s1->do_debug)
940 tcc_debug_line(s1);
941 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
942 redo:
943 if (tok == '#') {
944 /* horrible gas comment */
945 while (tok != TOK_LINEFEED)
946 next();
947 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
948 asm_parse_directive(s1, global);
949 } else if (tok == TOK_PPNUM) {
950 Sym *sym;
951 const char *p;
952 int n;
953 p = tokc.str.data;
954 n = strtoul(p, (char **)&p, 10);
955 if (*p != '\0')
956 expect("':'");
957 /* new local label */
958 sym = asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
959 /* Remove the marker for tentative definitions. */
960 sym->type.t &= ~VT_EXTERN;
961 next();
962 skip(':');
963 goto redo;
964 } else if (tok >= TOK_IDENT) {
965 /* instruction or label */
966 opcode = tok;
967 next();
968 if (tok == ':') {
969 /* new label */
970 Sym *sym = asm_new_label(s1, opcode, 0);
971 sym->type.t &= ~VT_EXTERN;
972 next();
973 goto redo;
974 } else if (tok == '=') {
975 set_symbol(s1, opcode);
976 goto redo;
977 } else {
978 asm_opcode(s1, opcode);
981 /* end of line */
982 if (tok != ';' && tok != TOK_LINEFEED)
983 expect("end of line");
984 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
987 parse_flags = saved_parse_flags;
988 return 0;
991 /* Assemble the current file */
992 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
994 int ret;
995 tcc_debug_start(s1);
996 /* default section is text */
997 cur_text_section = text_section;
998 ind = cur_text_section->data_offset;
999 nocode_wanted = 0;
1000 ret = tcc_assemble_internal(s1, do_preprocess, 1);
1001 asm_free_labels(s1);
1002 cur_text_section->data_offset = ind;
1003 tcc_debug_end(s1);
1004 return ret;
1007 /********************************************************************/
1008 /* GCC inline asm support */
1010 /* assemble the string 'str' in the current C compilation unit without
1011 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1012 end */
1013 static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
1015 const int *saved_macro_ptr = macro_ptr;
1016 int dotid = set_idnum('.', IS_ID);
1018 tcc_open_bf(s1, ":asm:", len);
1019 memcpy(file->buffer, str, len);
1020 macro_ptr = NULL;
1021 tcc_assemble_internal(s1, 0, global);
1022 tcc_close();
1024 set_idnum('.', dotid);
1025 macro_ptr = saved_macro_ptr;
1028 /* find a constraint by its number or id (gcc 3 extended
1029 syntax). return -1 if not found. Return in *pp in char after the
1030 constraint */
1031 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1032 const char *name, const char **pp)
1034 int index;
1035 TokenSym *ts;
1036 const char *p;
1038 if (isnum(*name)) {
1039 index = 0;
1040 while (isnum(*name)) {
1041 index = (index * 10) + (*name) - '0';
1042 name++;
1044 if ((unsigned)index >= nb_operands)
1045 index = -1;
1046 } else if (*name == '[') {
1047 name++;
1048 p = strchr(name, ']');
1049 if (p) {
1050 ts = tok_alloc(name, p - name);
1051 for(index = 0; index < nb_operands; index++) {
1052 if (operands[index].id == ts->tok)
1053 goto found;
1055 index = -1;
1056 found:
1057 name = p + 1;
1058 } else {
1059 index = -1;
1061 } else {
1062 index = -1;
1064 if (pp)
1065 *pp = name;
1066 return index;
1069 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1070 CString *out_str, CString *in_str)
1072 int c, index, modifier;
1073 const char *str;
1074 ASMOperand *op;
1075 SValue sv;
1077 cstr_new(out_str);
1078 str = in_str->data;
1079 for(;;) {
1080 c = *str++;
1081 if (c == '%') {
1082 if (*str == '%') {
1083 str++;
1084 goto add_char;
1086 modifier = 0;
1087 if (*str == 'c' || *str == 'n' ||
1088 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1089 *str == 'q' ||
1090 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1091 and make literal operands not be decorated with '$'. */
1092 *str == 'P')
1093 modifier = *str++;
1094 index = find_constraint(operands, nb_operands, str, &str);
1095 if (index < 0)
1096 tcc_error("invalid operand reference after %%");
1097 op = &operands[index];
1098 sv = *op->vt;
1099 if (op->reg >= 0) {
1100 sv.r = op->reg;
1101 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1102 sv.r |= VT_LVAL;
1104 subst_asm_operand(out_str, &sv, modifier);
1105 } else {
1106 add_char:
1107 cstr_ccat(out_str, c);
1108 if (c == '\0')
1109 break;
1115 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1116 int is_output)
1118 ASMOperand *op;
1119 int nb_operands;
1121 if (tok != ':') {
1122 nb_operands = *nb_operands_ptr;
1123 for(;;) {
1124 CString astr;
1125 if (nb_operands >= MAX_ASM_OPERANDS)
1126 tcc_error("too many asm operands");
1127 op = &operands[nb_operands++];
1128 op->id = 0;
1129 if (tok == '[') {
1130 next();
1131 if (tok < TOK_IDENT)
1132 expect("identifier");
1133 op->id = tok;
1134 next();
1135 skip(']');
1137 parse_mult_str(&astr, "string constant");
1138 op->constraint = tcc_malloc(astr.size);
1139 strcpy(op->constraint, astr.data);
1140 cstr_free(&astr);
1141 skip('(');
1142 gexpr();
1143 if (is_output) {
1144 if (!(vtop->type.t & VT_ARRAY))
1145 test_lvalue();
1146 } else {
1147 /* we want to avoid LLOCAL case, except when the 'm'
1148 constraint is used. Note that it may come from
1149 register storage, so we need to convert (reg)
1150 case */
1151 if ((vtop->r & VT_LVAL) &&
1152 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1153 (vtop->r & VT_VALMASK) < VT_CONST) &&
1154 !strchr(op->constraint, 'm')) {
1155 gv(RC_INT);
1158 op->vt = vtop;
1159 skip(')');
1160 if (tok == ',') {
1161 next();
1162 } else {
1163 break;
1166 *nb_operands_ptr = nb_operands;
1170 /* parse the GCC asm() instruction */
1171 ST_FUNC void asm_instr(void)
1173 CString astr, astr1;
1174 ASMOperand operands[MAX_ASM_OPERANDS];
1175 int nb_outputs, nb_operands, i, must_subst, out_reg;
1176 uint8_t clobber_regs[NB_ASM_REGS];
1178 next();
1179 /* since we always generate the asm() instruction, we can ignore
1180 volatile */
1181 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1182 next();
1184 parse_asm_str(&astr);
1185 nb_operands = 0;
1186 nb_outputs = 0;
1187 must_subst = 0;
1188 memset(clobber_regs, 0, sizeof(clobber_regs));
1189 if (tok == ':') {
1190 next();
1191 must_subst = 1;
1192 /* output args */
1193 parse_asm_operands(operands, &nb_operands, 1);
1194 nb_outputs = nb_operands;
1195 if (tok == ':') {
1196 next();
1197 if (tok != ')') {
1198 /* input args */
1199 parse_asm_operands(operands, &nb_operands, 0);
1200 if (tok == ':') {
1201 /* clobber list */
1202 /* XXX: handle registers */
1203 next();
1204 for(;;) {
1205 if (tok != TOK_STR)
1206 expect("string constant");
1207 asm_clobber(clobber_regs, tokc.str.data);
1208 next();
1209 if (tok == ',') {
1210 next();
1211 } else {
1212 break;
1219 skip(')');
1220 /* NOTE: we do not eat the ';' so that we can restore the current
1221 token after the assembler parsing */
1222 if (tok != ';')
1223 expect("';'");
1225 /* save all values in the memory */
1226 save_regs(0);
1228 /* compute constraints */
1229 asm_compute_constraints(operands, nb_operands, nb_outputs,
1230 clobber_regs, &out_reg);
1232 /* substitute the operands in the asm string. No substitution is
1233 done if no operands (GCC behaviour) */
1234 #ifdef ASM_DEBUG
1235 printf("asm: \"%s\"\n", (char *)astr.data);
1236 #endif
1237 if (must_subst) {
1238 subst_asm_operands(operands, nb_operands, &astr1, &astr);
1239 cstr_free(&astr);
1240 } else {
1241 astr1 = astr;
1243 #ifdef ASM_DEBUG
1244 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1245 #endif
1247 /* generate loads */
1248 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1249 clobber_regs, out_reg);
1251 /* assemble the string with tcc internal assembler */
1252 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1254 /* restore the current C token */
1255 next();
1257 /* store the output values if needed */
1258 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1259 clobber_regs, out_reg);
1261 /* free everything */
1262 for(i=0;i<nb_operands;i++) {
1263 ASMOperand *op;
1264 op = &operands[i];
1265 tcc_free(op->constraint);
1266 vpop();
1268 cstr_free(&astr1);
1271 ST_FUNC void asm_global_instr(void)
1273 CString astr;
1274 int saved_nocode_wanted = nocode_wanted;
1276 /* Global asm blocks are always emitted. */
1277 nocode_wanted = 0;
1278 next();
1279 parse_asm_str(&astr);
1280 skip(')');
1281 /* NOTE: we do not eat the ';' so that we can restore the current
1282 token after the assembler parsing */
1283 if (tok != ';')
1284 expect("';'");
1286 #ifdef ASM_DEBUG
1287 printf("asm_global: \"%s\"\n", (char *)astr.data);
1288 #endif
1289 cur_text_section = text_section;
1290 ind = cur_text_section->data_offset;
1292 /* assemble the string with tcc internal assembler */
1293 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1295 cur_text_section->data_offset = ind;
1297 /* restore the current C token */
1298 next();
1300 cstr_free(&astr);
1301 nocode_wanted = saved_nocode_wanted;
1303 #endif /* CONFIG_TCC_ASM */