Add make testspp.all/testspp.20
[tinycc.git] / tccasm.c
blobc035c8b446cbe9796b59d9cb27ad2c569806ed4c
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* asm_new_label(TCCState *s1, int label, int is_local);
36 static Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, int value);
38 static Sym *asm_label_find(int v)
40 Sym *sym = sym_find(v);
41 while (sym && sym->sym_scope)
42 sym = sym->prev_tok;
43 return sym;
46 static Sym *asm_label_push(int v)
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 *sym = global_identifier_push(v, VT_ASM | VT_EXTERN | VT_STATIC, 0);
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);
71 if (csym)
72 sym->c = csym->c;
74 return sym;
77 static Sym* asm_section_sym(TCCState *s1, Section *sec)
79 char buf[100];
80 int label = tok_alloc(buf,
81 snprintf(buf, sizeof buf, "L.%s", sec->name)
82 )->tok;
83 Sym *sym = asm_label_find(label);
84 return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0);
87 /* We do not use the C expression parser to handle symbols. Maybe the
88 C expression parser could be tweaked to do so. */
90 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
92 Sym *sym;
93 int op, label;
94 uint64_t n;
95 const char *p;
97 switch(tok) {
98 case TOK_PPNUM:
99 p = tokc.str.data;
100 n = strtoull(p, (char **)&p, 0);
101 if (*p == 'b' || *p == 'f') {
102 /* backward or forward label */
103 label = asm_get_local_label_name(s1, n);
104 sym = asm_label_find(label);
105 if (*p == 'b') {
106 /* backward : find the last corresponding defined label */
107 if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))
108 sym = sym->prev_tok;
109 if (!sym)
110 tcc_error("local label '%d' not found backward", n);
111 } else {
112 /* forward */
113 if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) {
114 /* if the last label is defined, then define a new one */
115 sym = asm_label_push(label);
118 pe->v = 0;
119 pe->sym = sym;
120 pe->pcrel = 0;
121 } else if (*p == '\0') {
122 pe->v = n;
123 pe->sym = NULL;
124 pe->pcrel = 0;
125 } else {
126 tcc_error("invalid number syntax");
128 next();
129 break;
130 case '+':
131 next();
132 asm_expr_unary(s1, pe);
133 break;
134 case '-':
135 case '~':
136 op = tok;
137 next();
138 asm_expr_unary(s1, pe);
139 if (pe->sym)
140 tcc_error("invalid operation with label");
141 if (op == '-')
142 pe->v = -pe->v;
143 else
144 pe->v = ~pe->v;
145 break;
146 case TOK_CCHAR:
147 case TOK_LCHAR:
148 pe->v = tokc.i;
149 pe->sym = NULL;
150 pe->pcrel = 0;
151 next();
152 break;
153 case '(':
154 next();
155 asm_expr(s1, pe);
156 skip(')');
157 break;
158 case '.':
159 pe->v = ind;
160 pe->sym = asm_section_sym(s1, cur_text_section);
161 pe->pcrel = 0;
162 next();
163 break;
164 default:
165 if (tok >= TOK_IDENT) {
166 ElfSym *esym;
167 /* label case : if the label was not found, add one */
168 sym = get_asm_sym(tok, NULL);
169 esym = elfsym(sym);
170 if (esym && esym->st_shndx == SHN_ABS) {
171 /* if absolute symbol, no need to put a symbol value */
172 pe->v = esym->st_value;
173 pe->sym = NULL;
174 pe->pcrel = 0;
175 } else {
176 pe->v = 0;
177 pe->sym = sym;
178 pe->pcrel = 0;
180 next();
181 } else {
182 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
184 break;
188 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
190 int op;
191 ExprValue e2;
193 asm_expr_unary(s1, pe);
194 for(;;) {
195 op = tok;
196 if (op != '*' && op != '/' && op != '%' &&
197 op != TOK_SHL && op != TOK_SAR)
198 break;
199 next();
200 asm_expr_unary(s1, &e2);
201 if (pe->sym || e2.sym)
202 tcc_error("invalid operation with label");
203 switch(op) {
204 case '*':
205 pe->v *= e2.v;
206 break;
207 case '/':
208 if (e2.v == 0) {
209 div_error:
210 tcc_error("division by zero");
212 pe->v /= e2.v;
213 break;
214 case '%':
215 if (e2.v == 0)
216 goto div_error;
217 pe->v %= e2.v;
218 break;
219 case TOK_SHL:
220 pe->v <<= e2.v;
221 break;
222 default:
223 case TOK_SAR:
224 pe->v >>= e2.v;
225 break;
230 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
232 int op;
233 ExprValue e2;
235 asm_expr_prod(s1, pe);
236 for(;;) {
237 op = tok;
238 if (op != '&' && op != '|' && op != '^')
239 break;
240 next();
241 asm_expr_prod(s1, &e2);
242 if (pe->sym || e2.sym)
243 tcc_error("invalid operation with label");
244 switch(op) {
245 case '&':
246 pe->v &= e2.v;
247 break;
248 case '|':
249 pe->v |= e2.v;
250 break;
251 default:
252 case '^':
253 pe->v ^= e2.v;
254 break;
259 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
261 int op;
262 ExprValue e2;
264 asm_expr_logic(s1, pe);
265 for(;;) {
266 op = tok;
267 if (op != '+' && op != '-')
268 break;
269 next();
270 asm_expr_logic(s1, &e2);
271 if (op == '+') {
272 if (pe->sym != NULL && e2.sym != NULL)
273 goto cannot_relocate;
274 pe->v += e2.v;
275 if (pe->sym == NULL && e2.sym != NULL)
276 pe->sym = e2.sym;
277 } else {
278 pe->v -= e2.v;
279 /* NOTE: we are less powerful than gas in that case
280 because we store only one symbol in the expression */
281 if (!e2.sym) {
282 /* OK */
283 } else if (pe->sym == e2.sym) {
284 /* OK */
285 pe->sym = NULL; /* same symbols can be subtracted to NULL */
286 } else {
287 ElfSym *esym1, *esym2;
288 esym1 = elfsym(pe->sym);
289 esym2 = elfsym(e2.sym);
290 if (esym1 && esym1->st_shndx == esym2->st_shndx
291 && esym1->st_shndx != SHN_UNDEF) {
292 /* we also accept defined symbols in the same section */
293 pe->v += esym1->st_value - esym2->st_value;
294 pe->sym = NULL;
295 } else if (esym2->st_shndx == cur_text_section->sh_num) {
296 /* When subtracting a defined symbol in current section
297 this actually makes the value PC-relative. */
298 pe->v -= esym2->st_value - ind - 4;
299 pe->pcrel = 1;
300 e2.sym = NULL;
301 } else {
302 cannot_relocate:
303 tcc_error("invalid operation with label");
310 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
312 int op;
313 ExprValue e2;
315 asm_expr_sum(s1, pe);
316 for(;;) {
317 op = tok;
318 if (op != TOK_EQ && op != TOK_NE
319 && (op > TOK_GT || op < TOK_ULE))
320 break;
321 next();
322 asm_expr_sum(s1, &e2);
323 if (pe->sym || e2.sym)
324 tcc_error("invalid operation with label");
325 switch(op) {
326 case TOK_EQ:
327 pe->v = pe->v == e2.v;
328 break;
329 case TOK_NE:
330 pe->v = pe->v != e2.v;
331 break;
332 case TOK_LT:
333 pe->v = (int64_t)pe->v < (int64_t)e2.v;
334 break;
335 case TOK_GE:
336 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
337 break;
338 case TOK_LE:
339 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
340 break;
341 case TOK_GT:
342 pe->v = (int64_t)pe->v > (int64_t)e2.v;
343 break;
344 default:
345 break;
347 /* GAS compare results are -1/0 not 1/0. */
348 pe->v = -(int64_t)pe->v;
352 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
354 asm_expr_cmp(s1, pe);
357 ST_FUNC int asm_int_expr(TCCState *s1)
359 ExprValue e;
360 asm_expr(s1, &e);
361 if (e.sym)
362 expect("constant");
363 return e.v;
366 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
367 int sh_num, int value)
369 Sym *sym;
370 ElfSym *esym;
372 sym = asm_label_find(label);
373 if (sym) {
374 esym = elfsym(sym);
375 /* A VT_EXTERN symbol, even if it has a section is considered
376 overridable. This is how we "define" .set targets. Real
377 definitions won't have VT_EXTERN set. */
378 if (esym && esym->st_shndx != SHN_UNDEF) {
379 /* the label is already defined */
380 if (IS_ASM_SYM(sym)
381 && (is_local == 1 || (sym->type.t & VT_EXTERN)))
382 goto new_label;
383 if (!(sym->type.t & VT_EXTERN))
384 tcc_error("assembler label '%s' already defined",
385 get_tok_str(label, NULL));
387 } else {
388 new_label:
389 sym = asm_label_push(label);
391 if (!sym->c)
392 put_extern_sym2(sym, SHN_UNDEF, 0, 0, 0);
393 esym = elfsym(sym);
394 esym->st_shndx = sh_num;
395 esym->st_value = value;
396 if (is_local != 2)
397 sym->type.t &= ~VT_EXTERN;
398 return sym;
401 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
403 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
406 /* Set the value of LABEL to that of some expression (possibly
407 involving other symbols). LABEL can be overwritten later still. */
408 static Sym* set_symbol(TCCState *s1, int label)
410 long n;
411 ExprValue e;
412 Sym *sym;
413 ElfSym *esym;
414 next();
415 asm_expr(s1, &e);
416 n = e.v;
417 esym = elfsym(e.sym);
418 if (esym)
419 n += esym->st_value;
420 sym = asm_new_label1(s1, label, 2, esym ? esym->st_shndx : SHN_ABS, n);
421 elfsym(sym)->st_other |= ST_ASM_SET;
422 return sym;
425 static void use_section1(TCCState *s1, Section *sec)
427 cur_text_section->data_offset = ind;
428 cur_text_section = sec;
429 ind = cur_text_section->data_offset;
432 static void use_section(TCCState *s1, const char *name)
434 Section *sec;
435 sec = find_section(s1, name);
436 use_section1(s1, sec);
439 static void push_section(TCCState *s1, const char *name)
441 Section *sec = find_section(s1, name);
442 sec->prev = cur_text_section;
443 use_section1(s1, sec);
446 static void pop_section(TCCState *s1)
448 Section *prev = cur_text_section->prev;
449 if (!prev)
450 tcc_error(".popsection without .pushsection");
451 cur_text_section->prev = NULL;
452 use_section1(s1, prev);
455 static void asm_parse_directive(TCCState *s1, int global)
457 int n, offset, v, size, tok1;
458 Section *sec;
459 uint8_t *ptr;
461 /* assembler directive */
462 sec = cur_text_section;
463 switch(tok) {
464 case TOK_ASMDIR_align:
465 case TOK_ASMDIR_balign:
466 case TOK_ASMDIR_p2align:
467 case TOK_ASMDIR_skip:
468 case TOK_ASMDIR_space:
469 tok1 = tok;
470 next();
471 n = asm_int_expr(s1);
472 if (tok1 == TOK_ASMDIR_p2align)
474 if (n < 0 || n > 30)
475 tcc_error("invalid p2align, must be between 0 and 30");
476 n = 1 << n;
477 tok1 = TOK_ASMDIR_align;
479 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
480 if (n < 0 || (n & (n-1)) != 0)
481 tcc_error("alignment must be a positive power of two");
482 offset = (ind + n - 1) & -n;
483 size = offset - ind;
484 /* the section must have a compatible alignment */
485 if (sec->sh_addralign < n)
486 sec->sh_addralign = n;
487 } else {
488 if (n < 0)
489 n = 0;
490 size = n;
492 v = 0;
493 if (tok == ',') {
494 next();
495 v = asm_int_expr(s1);
497 zero_pad:
498 if (sec->sh_type != SHT_NOBITS) {
499 sec->data_offset = ind;
500 ptr = section_ptr_add(sec, size);
501 memset(ptr, v, size);
503 ind += size;
504 break;
505 case TOK_ASMDIR_quad:
506 #ifdef TCC_TARGET_X86_64
507 size = 8;
508 goto asm_data;
509 #else
510 next();
511 for(;;) {
512 uint64_t vl;
513 const char *p;
515 p = tokc.str.data;
516 if (tok != TOK_PPNUM) {
517 error_constant:
518 tcc_error("64 bit constant");
520 vl = strtoll(p, (char **)&p, 0);
521 if (*p != '\0')
522 goto error_constant;
523 next();
524 if (sec->sh_type != SHT_NOBITS) {
525 /* XXX: endianness */
526 gen_le32(vl);
527 gen_le32(vl >> 32);
528 } else {
529 ind += 8;
531 if (tok != ',')
532 break;
533 next();
535 break;
536 #endif
537 case TOK_ASMDIR_byte:
538 size = 1;
539 goto asm_data;
540 case TOK_ASMDIR_word:
541 case TOK_ASMDIR_short:
542 size = 2;
543 goto asm_data;
544 case TOK_ASMDIR_long:
545 case TOK_ASMDIR_int:
546 size = 4;
547 asm_data:
548 next();
549 for(;;) {
550 ExprValue e;
551 asm_expr(s1, &e);
552 if (sec->sh_type != SHT_NOBITS) {
553 if (size == 4) {
554 gen_expr32(&e);
555 #ifdef TCC_TARGET_X86_64
556 } else if (size == 8) {
557 gen_expr64(&e);
558 #endif
559 } else {
560 if (e.sym)
561 expect("constant");
562 if (size == 1)
563 g(e.v);
564 else
565 gen_le16(e.v);
567 } else {
568 ind += size;
570 if (tok != ',')
571 break;
572 next();
574 break;
575 case TOK_ASMDIR_fill:
577 int repeat, size, val, i, j;
578 uint8_t repeat_buf[8];
579 next();
580 repeat = asm_int_expr(s1);
581 if (repeat < 0) {
582 tcc_error("repeat < 0; .fill ignored");
583 break;
585 size = 1;
586 val = 0;
587 if (tok == ',') {
588 next();
589 size = asm_int_expr(s1);
590 if (size < 0) {
591 tcc_error("size < 0; .fill ignored");
592 break;
594 if (size > 8)
595 size = 8;
596 if (tok == ',') {
597 next();
598 val = asm_int_expr(s1);
601 /* XXX: endianness */
602 repeat_buf[0] = val;
603 repeat_buf[1] = val >> 8;
604 repeat_buf[2] = val >> 16;
605 repeat_buf[3] = val >> 24;
606 repeat_buf[4] = 0;
607 repeat_buf[5] = 0;
608 repeat_buf[6] = 0;
609 repeat_buf[7] = 0;
610 for(i = 0; i < repeat; i++) {
611 for(j = 0; j < size; j++) {
612 g(repeat_buf[j]);
616 break;
617 case TOK_ASMDIR_rept:
619 int repeat;
620 TokenString *init_str;
621 next();
622 repeat = asm_int_expr(s1);
623 init_str = tok_str_alloc();
624 while (next(), tok != TOK_ASMDIR_endr) {
625 if (tok == CH_EOF)
626 tcc_error("we at end of file, .endr not found");
627 tok_str_add_tok(init_str);
629 tok_str_add(init_str, -1);
630 tok_str_add(init_str, 0);
631 begin_macro(init_str, 1);
632 while (repeat-- > 0) {
633 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
634 global);
635 macro_ptr = init_str->str;
637 end_macro();
638 next();
639 break;
641 case TOK_ASMDIR_org:
643 unsigned long n;
644 ExprValue e;
645 ElfSym *esym;
646 next();
647 asm_expr(s1, &e);
648 n = e.v;
649 esym = elfsym(e.sym);
650 if (esym) {
651 if (esym->st_shndx != cur_text_section->sh_num)
652 expect("constant or same-section symbol");
653 n += esym->st_value;
655 if (n < ind)
656 tcc_error("attempt to .org backwards");
657 v = 0;
658 size = n - ind;
659 goto zero_pad;
661 break;
662 case TOK_ASMDIR_set:
663 next();
664 tok1 = tok;
665 next();
666 /* Also accept '.set stuff', but don't do anything with this.
667 It's used in GAS to set various features like '.set mips16'. */
668 if (tok == ',')
669 set_symbol(s1, tok1);
670 break;
671 case TOK_ASMDIR_globl:
672 case TOK_ASMDIR_global:
673 case TOK_ASMDIR_weak:
674 case TOK_ASMDIR_hidden:
675 tok1 = tok;
676 do {
677 Sym *sym;
678 next();
679 sym = get_asm_sym(tok, NULL);
680 if (tok1 != TOK_ASMDIR_hidden)
681 sym->type.t &= ~VT_STATIC;
682 if (tok1 == TOK_ASMDIR_weak)
683 sym->a.weak = 1;
684 else if (tok1 == TOK_ASMDIR_hidden)
685 sym->a.visibility = STV_HIDDEN;
686 update_storage(sym);
687 next();
688 } while (tok == ',');
689 break;
690 case TOK_ASMDIR_string:
691 case TOK_ASMDIR_ascii:
692 case TOK_ASMDIR_asciz:
694 const uint8_t *p;
695 int i, size, t;
697 t = tok;
698 next();
699 for(;;) {
700 if (tok != TOK_STR)
701 expect("string constant");
702 p = tokc.str.data;
703 size = tokc.str.size;
704 if (t == TOK_ASMDIR_ascii && size > 0)
705 size--;
706 for(i = 0; i < size; i++)
707 g(p[i]);
708 next();
709 if (tok == ',') {
710 next();
711 } else if (tok != TOK_STR) {
712 break;
716 break;
717 case TOK_ASMDIR_text:
718 case TOK_ASMDIR_data:
719 case TOK_ASMDIR_bss:
721 char sname[64];
722 tok1 = tok;
723 n = 0;
724 next();
725 if (tok != ';' && tok != TOK_LINEFEED) {
726 n = asm_int_expr(s1);
727 next();
729 if (n)
730 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
731 else
732 sprintf(sname, "%s", get_tok_str(tok1, NULL));
733 use_section(s1, sname);
735 break;
736 case TOK_ASMDIR_file:
738 char filename[512];
740 filename[0] = '\0';
741 next();
743 if (tok == TOK_STR)
744 pstrcat(filename, sizeof(filename), tokc.str.data);
745 else
746 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
748 if (s1->warn_unsupported)
749 tcc_warning("ignoring .file %s", filename);
751 next();
753 break;
754 case TOK_ASMDIR_ident:
756 char ident[256];
758 ident[0] = '\0';
759 next();
761 if (tok == TOK_STR)
762 pstrcat(ident, sizeof(ident), tokc.str.data);
763 else
764 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
766 if (s1->warn_unsupported)
767 tcc_warning("ignoring .ident %s", ident);
769 next();
771 break;
772 case TOK_ASMDIR_size:
774 Sym *sym;
776 next();
777 sym = asm_label_find(tok);
778 if (!sym) {
779 tcc_error("label not found: %s", get_tok_str(tok, NULL));
782 /* XXX .size name,label2-label1 */
783 if (s1->warn_unsupported)
784 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
786 next();
787 skip(',');
788 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
789 next();
792 break;
793 case TOK_ASMDIR_type:
795 Sym *sym;
796 const char *newtype;
798 next();
799 sym = get_asm_sym(tok, NULL);
800 next();
801 skip(',');
802 if (tok == TOK_STR) {
803 newtype = tokc.str.data;
804 } else {
805 if (tok == '@' || tok == '%')
806 next();
807 newtype = get_tok_str(tok, NULL);
810 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
811 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
813 else if (s1->warn_unsupported)
814 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
815 get_tok_str(sym->v, NULL), sym->type.t, newtype);
817 next();
819 break;
820 case TOK_ASMDIR_pushsection:
821 case TOK_ASMDIR_section:
823 char sname[256];
824 int old_nb_section = s1->nb_sections;
826 tok1 = tok;
827 /* XXX: support more options */
828 next();
829 sname[0] = '\0';
830 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
831 if (tok == TOK_STR)
832 pstrcat(sname, sizeof(sname), tokc.str.data);
833 else
834 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
835 next();
837 if (tok == ',') {
838 /* skip section options */
839 next();
840 if (tok != TOK_STR)
841 expect("string constant");
842 next();
843 if (tok == ',') {
844 next();
845 if (tok == '@' || tok == '%')
846 next();
847 next();
850 last_text_section = cur_text_section;
851 if (tok1 == TOK_ASMDIR_section)
852 use_section(s1, sname);
853 else
854 push_section(s1, sname);
855 /* If we just allocated a new section reset its alignment to
856 1. new_section normally acts for GCC compatibility and
857 sets alignment to PTR_SIZE. The assembler behaves different. */
858 if (old_nb_section != s1->nb_sections)
859 cur_text_section->sh_addralign = 1;
861 break;
862 case TOK_ASMDIR_previous:
864 Section *sec;
865 next();
866 if (!last_text_section)
867 tcc_error("no previous section referenced");
868 sec = cur_text_section;
869 use_section1(s1, last_text_section);
870 last_text_section = sec;
872 break;
873 case TOK_ASMDIR_popsection:
874 next();
875 pop_section(s1);
876 break;
877 #ifdef TCC_TARGET_I386
878 case TOK_ASMDIR_code16:
880 next();
881 s1->seg_size = 16;
883 break;
884 case TOK_ASMDIR_code32:
886 next();
887 s1->seg_size = 32;
889 break;
890 #endif
891 #ifdef TCC_TARGET_X86_64
892 /* added for compatibility with GAS */
893 case TOK_ASMDIR_code64:
894 next();
895 break;
896 #endif
897 default:
898 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
899 break;
904 /* assemble a file */
905 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
907 int opcode;
908 int saved_parse_flags = parse_flags;
910 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
911 if (do_preprocess)
912 parse_flags |= PARSE_FLAG_PREPROCESS;
913 for(;;) {
914 next();
915 if (tok == TOK_EOF)
916 break;
917 /* generate line number info */
918 if (global && s1->do_debug)
919 tcc_debug_line(s1);
920 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
921 redo:
922 if (tok == '#') {
923 /* horrible gas comment */
924 while (tok != TOK_LINEFEED)
925 next();
926 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
927 asm_parse_directive(s1, global);
928 } else if (tok == TOK_PPNUM) {
929 const char *p;
930 int n;
931 p = tokc.str.data;
932 n = strtoul(p, (char **)&p, 10);
933 if (*p != '\0')
934 expect("':'");
935 /* new local label */
936 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
937 next();
938 skip(':');
939 goto redo;
940 } else if (tok >= TOK_IDENT) {
941 /* instruction or label */
942 opcode = tok;
943 next();
944 if (tok == ':') {
945 /* new label */
946 asm_new_label(s1, opcode, 0);
947 next();
948 goto redo;
949 } else if (tok == '=') {
950 set_symbol(s1, opcode);
951 goto redo;
952 } else {
953 asm_opcode(s1, opcode);
956 /* end of line */
957 if (tok != ';' && tok != TOK_LINEFEED)
958 expect("end of line");
959 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
962 parse_flags = saved_parse_flags;
963 return 0;
966 /* Assemble the current file */
967 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
969 int ret;
970 tcc_debug_start(s1);
971 /* default section is text */
972 cur_text_section = text_section;
973 ind = cur_text_section->data_offset;
974 nocode_wanted = 0;
975 ret = tcc_assemble_internal(s1, do_preprocess, 1);
976 cur_text_section->data_offset = ind;
977 tcc_debug_end(s1);
978 return ret;
981 /********************************************************************/
982 /* GCC inline asm support */
984 /* assemble the string 'str' in the current C compilation unit without
985 C preprocessing. NOTE: str is modified by modifying the '\0' at the
986 end */
987 static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
989 const int *saved_macro_ptr = macro_ptr;
990 int dotid = set_idnum('.', IS_ID);
992 tcc_open_bf(s1, ":asm:", len);
993 memcpy(file->buffer, str, len);
994 macro_ptr = NULL;
995 tcc_assemble_internal(s1, 0, global);
996 tcc_close();
998 set_idnum('.', dotid);
999 macro_ptr = saved_macro_ptr;
1002 /* find a constraint by its number or id (gcc 3 extended
1003 syntax). return -1 if not found. Return in *pp in char after the
1004 constraint */
1005 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1006 const char *name, const char **pp)
1008 int index;
1009 TokenSym *ts;
1010 const char *p;
1012 if (isnum(*name)) {
1013 index = 0;
1014 while (isnum(*name)) {
1015 index = (index * 10) + (*name) - '0';
1016 name++;
1018 if ((unsigned)index >= nb_operands)
1019 index = -1;
1020 } else if (*name == '[') {
1021 name++;
1022 p = strchr(name, ']');
1023 if (p) {
1024 ts = tok_alloc(name, p - name);
1025 for(index = 0; index < nb_operands; index++) {
1026 if (operands[index].id == ts->tok)
1027 goto found;
1029 index = -1;
1030 found:
1031 name = p + 1;
1032 } else {
1033 index = -1;
1035 } else {
1036 index = -1;
1038 if (pp)
1039 *pp = name;
1040 return index;
1043 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1044 CString *out_str, CString *in_str)
1046 int c, index, modifier;
1047 const char *str;
1048 ASMOperand *op;
1049 SValue sv;
1051 cstr_new(out_str);
1052 str = in_str->data;
1053 for(;;) {
1054 c = *str++;
1055 if (c == '%') {
1056 if (*str == '%') {
1057 str++;
1058 goto add_char;
1060 modifier = 0;
1061 if (*str == 'c' || *str == 'n' ||
1062 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1063 *str == 'q' ||
1064 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1065 and make literal operands not be decorated with '$'. */
1066 *str == 'P')
1067 modifier = *str++;
1068 index = find_constraint(operands, nb_operands, str, &str);
1069 if (index < 0)
1070 tcc_error("invalid operand reference after %%");
1071 op = &operands[index];
1072 sv = *op->vt;
1073 if (op->reg >= 0) {
1074 sv.r = op->reg;
1075 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1076 sv.r |= VT_LVAL;
1078 subst_asm_operand(out_str, &sv, modifier);
1079 } else {
1080 add_char:
1081 cstr_ccat(out_str, c);
1082 if (c == '\0')
1083 break;
1089 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1090 int is_output)
1092 ASMOperand *op;
1093 int nb_operands;
1095 if (tok != ':') {
1096 nb_operands = *nb_operands_ptr;
1097 for(;;) {
1098 CString astr;
1099 if (nb_operands >= MAX_ASM_OPERANDS)
1100 tcc_error("too many asm operands");
1101 op = &operands[nb_operands++];
1102 op->id = 0;
1103 if (tok == '[') {
1104 next();
1105 if (tok < TOK_IDENT)
1106 expect("identifier");
1107 op->id = tok;
1108 next();
1109 skip(']');
1111 parse_mult_str(&astr, "string constant");
1112 op->constraint = tcc_malloc(astr.size);
1113 strcpy(op->constraint, astr.data);
1114 cstr_free(&astr);
1115 skip('(');
1116 gexpr();
1117 if (is_output) {
1118 if (!(vtop->type.t & VT_ARRAY))
1119 test_lvalue();
1120 } else {
1121 /* we want to avoid LLOCAL case, except when the 'm'
1122 constraint is used. Note that it may come from
1123 register storage, so we need to convert (reg)
1124 case */
1125 if ((vtop->r & VT_LVAL) &&
1126 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1127 (vtop->r & VT_VALMASK) < VT_CONST) &&
1128 !strchr(op->constraint, 'm')) {
1129 gv(RC_INT);
1132 op->vt = vtop;
1133 skip(')');
1134 if (tok == ',') {
1135 next();
1136 } else {
1137 break;
1140 *nb_operands_ptr = nb_operands;
1144 /* parse the GCC asm() instruction */
1145 ST_FUNC void asm_instr(void)
1147 CString astr, astr1;
1148 ASMOperand operands[MAX_ASM_OPERANDS];
1149 int nb_outputs, nb_operands, i, must_subst, out_reg;
1150 uint8_t clobber_regs[NB_ASM_REGS];
1152 next();
1153 /* since we always generate the asm() instruction, we can ignore
1154 volatile */
1155 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1156 next();
1158 parse_asm_str(&astr);
1159 nb_operands = 0;
1160 nb_outputs = 0;
1161 must_subst = 0;
1162 memset(clobber_regs, 0, sizeof(clobber_regs));
1163 if (tok == ':') {
1164 next();
1165 must_subst = 1;
1166 /* output args */
1167 parse_asm_operands(operands, &nb_operands, 1);
1168 nb_outputs = nb_operands;
1169 if (tok == ':') {
1170 next();
1171 if (tok != ')') {
1172 /* input args */
1173 parse_asm_operands(operands, &nb_operands, 0);
1174 if (tok == ':') {
1175 /* clobber list */
1176 /* XXX: handle registers */
1177 next();
1178 for(;;) {
1179 if (tok != TOK_STR)
1180 expect("string constant");
1181 asm_clobber(clobber_regs, tokc.str.data);
1182 next();
1183 if (tok == ',') {
1184 next();
1185 } else {
1186 break;
1193 skip(')');
1194 /* NOTE: we do not eat the ';' so that we can restore the current
1195 token after the assembler parsing */
1196 if (tok != ';')
1197 expect("';'");
1199 /* save all values in the memory */
1200 save_regs(0);
1202 /* compute constraints */
1203 asm_compute_constraints(operands, nb_operands, nb_outputs,
1204 clobber_regs, &out_reg);
1206 /* substitute the operands in the asm string. No substitution is
1207 done if no operands (GCC behaviour) */
1208 #ifdef ASM_DEBUG
1209 printf("asm: \"%s\"\n", (char *)astr.data);
1210 #endif
1211 if (must_subst) {
1212 subst_asm_operands(operands, nb_operands, &astr1, &astr);
1213 cstr_free(&astr);
1214 } else {
1215 astr1 = astr;
1217 #ifdef ASM_DEBUG
1218 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1219 #endif
1221 /* generate loads */
1222 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1223 clobber_regs, out_reg);
1225 /* assemble the string with tcc internal assembler */
1226 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1228 /* restore the current C token */
1229 next();
1231 /* store the output values if needed */
1232 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1233 clobber_regs, out_reg);
1235 /* free everything */
1236 for(i=0;i<nb_operands;i++) {
1237 ASMOperand *op;
1238 op = &operands[i];
1239 tcc_free(op->constraint);
1240 vpop();
1242 cstr_free(&astr1);
1245 ST_FUNC void asm_global_instr(void)
1247 CString astr;
1248 int saved_nocode_wanted = nocode_wanted;
1250 /* Global asm blocks are always emitted. */
1251 nocode_wanted = 0;
1252 next();
1253 parse_asm_str(&astr);
1254 skip(')');
1255 /* NOTE: we do not eat the ';' so that we can restore the current
1256 token after the assembler parsing */
1257 if (tok != ';')
1258 expect("';'");
1260 #ifdef ASM_DEBUG
1261 printf("asm_global: \"%s\"\n", (char *)astr.data);
1262 #endif
1263 cur_text_section = text_section;
1264 ind = cur_text_section->data_offset;
1266 /* assemble the string with tcc internal assembler */
1267 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1269 cur_text_section->data_offset = ind;
1271 /* restore the current C token */
1272 next();
1274 cstr_free(&astr);
1275 nocode_wanted = saved_nocode_wanted;
1277 #endif /* CONFIG_TCC_ASM */