tccpp: faster next()
[tinycc.git] / tccasm.c
blobc89cdc67eefc0be41fb2bd8cf2d231b5e8a72a77
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 #define USING_GLOBALS
22 #include "tcc.h"
23 #ifdef CONFIG_TCC_ASM
25 static Section *last_text_section; /* to handle .previous asm directive */
27 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
29 char buf[64];
30 TokenSym *ts;
32 snprintf(buf, sizeof(buf), "L..%u", n);
33 ts = tok_alloc(buf, strlen(buf));
34 return ts->tok;
37 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
38 static Sym* asm_new_label(TCCState *s1, int label, int is_local);
39 static Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, int value);
41 static Sym *asm_label_find(int v)
43 Sym *sym = sym_find(v);
44 while (sym && sym->sym_scope && !(sym->type.t & VT_STATIC))
45 sym = sym->prev_tok;
46 return sym;
49 static Sym *asm_label_push(int v)
51 /* We always add VT_EXTERN, for sym definition that's tentative
52 (for .set, removed for real defs), for mere references it's correct
53 as is. */
54 return global_identifier_push(v, VT_ASM | VT_EXTERN | VT_STATIC, 0);
57 /* Return a symbol we can use inside the assembler, having name NAME.
58 Symbols from asm and C source share a namespace. If we generate
59 an asm symbol it's also a (file-global) C symbol, but it's
60 either not accessible by name (like "L.123"), or its type information
61 is such that it's not usable without a proper C declaration.
63 Sometimes we need symbols accessible by name from asm, which
64 are anonymous in C, in this case CSYM can be used to transfer
65 all information from that symbol to the (possibly newly created)
66 asm symbol. */
67 ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
69 Sym *sym = asm_label_find(name);
70 if (!sym) {
71 sym = asm_label_push(name);
72 if (csym)
73 sym->c = csym->c;
75 return sym;
78 static Sym* asm_section_sym(TCCState *s1, Section *sec)
80 char buf[100];
81 int label = tok_alloc(buf,
82 snprintf(buf, sizeof buf, "L.%s", sec->name)
83 )->tok;
84 Sym *sym = asm_label_find(label);
85 return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0);
88 /* We do not use the C expression parser to handle symbols. Maybe the
89 C expression parser could be tweaked to do so. */
91 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
93 Sym *sym;
94 int op, label;
95 uint64_t n;
96 const char *p;
98 switch(tok) {
99 case TOK_PPNUM:
100 p = tokc.str.data;
101 n = strtoull(p, (char **)&p, 0);
102 if (*p == 'b' || *p == 'f') {
103 /* backward or forward label */
104 label = asm_get_local_label_name(s1, n);
105 sym = asm_label_find(label);
106 if (*p == 'b') {
107 /* backward : find the last corresponding defined label */
108 if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))
109 sym = sym->prev_tok;
110 if (!sym)
111 tcc_error("local label '%d' not found backward", (int)n);
112 } else {
113 /* forward */
114 if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) {
115 /* if the last label is defined, then define a new one */
116 sym = asm_label_push(label);
119 pe->v = 0;
120 pe->sym = sym;
121 pe->pcrel = 0;
122 } else if (*p == '\0') {
123 pe->v = n;
124 pe->sym = NULL;
125 pe->pcrel = 0;
126 } else {
127 tcc_error("invalid number syntax");
129 next();
130 break;
131 case '+':
132 next();
133 asm_expr_unary(s1, pe);
134 break;
135 case '-':
136 case '~':
137 op = tok;
138 next();
139 asm_expr_unary(s1, pe);
140 if (pe->sym)
141 tcc_error("invalid operation with label");
142 if (op == '-')
143 pe->v = -pe->v;
144 else
145 pe->v = ~pe->v;
146 break;
147 case TOK_CCHAR:
148 case TOK_LCHAR:
149 pe->v = tokc.i;
150 pe->sym = NULL;
151 pe->pcrel = 0;
152 next();
153 break;
154 case '(':
155 next();
156 asm_expr(s1, pe);
157 skip(')');
158 break;
159 case '.':
160 pe->v = ind;
161 pe->sym = asm_section_sym(s1, cur_text_section);
162 pe->pcrel = 0;
163 next();
164 break;
165 default:
166 if (tok >= TOK_IDENT) {
167 ElfSym *esym;
168 /* label case : if the label was not found, add one */
169 sym = get_asm_sym(tok, NULL);
170 esym = elfsym(sym);
171 if (esym && esym->st_shndx == SHN_ABS) {
172 /* if absolute symbol, no need to put a symbol value */
173 pe->v = esym->st_value;
174 pe->sym = NULL;
175 pe->pcrel = 0;
176 } else {
177 pe->v = 0;
178 pe->sym = sym;
179 pe->pcrel = 0;
181 next();
182 } else {
183 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
185 break;
189 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
191 int op;
192 ExprValue e2;
194 asm_expr_unary(s1, pe);
195 for(;;) {
196 op = tok;
197 if (op != '*' && op != '/' && op != '%' &&
198 op != TOK_SHL && op != TOK_SAR)
199 break;
200 next();
201 asm_expr_unary(s1, &e2);
202 if (pe->sym || e2.sym)
203 tcc_error("invalid operation with label");
204 switch(op) {
205 case '*':
206 pe->v *= e2.v;
207 break;
208 case '/':
209 if (e2.v == 0) {
210 div_error:
211 tcc_error("division by zero");
213 pe->v /= e2.v;
214 break;
215 case '%':
216 if (e2.v == 0)
217 goto div_error;
218 pe->v %= e2.v;
219 break;
220 case TOK_SHL:
221 pe->v <<= e2.v;
222 break;
223 default:
224 case TOK_SAR:
225 pe->v >>= e2.v;
226 break;
231 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
233 int op;
234 ExprValue e2;
236 asm_expr_prod(s1, pe);
237 for(;;) {
238 op = tok;
239 if (op != '&' && op != '|' && op != '^')
240 break;
241 next();
242 asm_expr_prod(s1, &e2);
243 if (pe->sym || e2.sym)
244 tcc_error("invalid operation with label");
245 switch(op) {
246 case '&':
247 pe->v &= e2.v;
248 break;
249 case '|':
250 pe->v |= e2.v;
251 break;
252 default:
253 case '^':
254 pe->v ^= e2.v;
255 break;
260 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
262 int op;
263 ExprValue e2;
265 asm_expr_logic(s1, pe);
266 for(;;) {
267 op = tok;
268 if (op != '+' && op != '-')
269 break;
270 next();
271 asm_expr_logic(s1, &e2);
272 if (op == '+') {
273 if (pe->sym != NULL && e2.sym != NULL)
274 goto cannot_relocate;
275 pe->v += e2.v;
276 if (pe->sym == NULL && e2.sym != NULL)
277 pe->sym = e2.sym;
278 } else {
279 pe->v -= e2.v;
280 /* NOTE: we are less powerful than gas in that case
281 because we store only one symbol in the expression */
282 if (!e2.sym) {
283 /* OK */
284 } else if (pe->sym == e2.sym) {
285 /* OK */
286 pe->sym = NULL; /* same symbols can be subtracted to NULL */
287 } else {
288 ElfSym *esym1, *esym2;
289 esym1 = elfsym(pe->sym);
290 esym2 = elfsym(e2.sym);
291 if (esym1 && esym1->st_shndx == esym2->st_shndx
292 && esym1->st_shndx != SHN_UNDEF) {
293 /* we also accept defined symbols in the same section */
294 pe->v += esym1->st_value - esym2->st_value;
295 pe->sym = NULL;
296 } else if (esym2->st_shndx == cur_text_section->sh_num) {
297 /* When subtracting a defined symbol in current section
298 this actually makes the value PC-relative. */
299 pe->v -= esym2->st_value - ind - 4;
300 pe->pcrel = 1;
301 e2.sym = NULL;
302 } else {
303 cannot_relocate:
304 tcc_error("invalid operation with label");
311 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
313 int op;
314 ExprValue e2;
316 asm_expr_sum(s1, pe);
317 for(;;) {
318 op = tok;
319 if (op != TOK_EQ && op != TOK_NE
320 && (op > TOK_GT || op < TOK_ULE))
321 break;
322 next();
323 asm_expr_sum(s1, &e2);
324 if (pe->sym || e2.sym)
325 tcc_error("invalid operation with label");
326 switch(op) {
327 case TOK_EQ:
328 pe->v = pe->v == e2.v;
329 break;
330 case TOK_NE:
331 pe->v = pe->v != e2.v;
332 break;
333 case TOK_LT:
334 pe->v = (int64_t)pe->v < (int64_t)e2.v;
335 break;
336 case TOK_GE:
337 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
338 break;
339 case TOK_LE:
340 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
341 break;
342 case TOK_GT:
343 pe->v = (int64_t)pe->v > (int64_t)e2.v;
344 break;
345 default:
346 break;
348 /* GAS compare results are -1/0 not 1/0. */
349 pe->v = -(int64_t)pe->v;
353 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
355 asm_expr_cmp(s1, pe);
358 ST_FUNC int asm_int_expr(TCCState *s1)
360 ExprValue e;
361 asm_expr(s1, &e);
362 if (e.sym)
363 expect("constant");
364 return e.v;
367 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
368 int sh_num, int value)
370 Sym *sym;
371 ElfSym *esym;
373 sym = asm_label_find(label);
374 if (sym) {
375 esym = elfsym(sym);
376 /* A VT_EXTERN symbol, even if it has a section is considered
377 overridable. This is how we "define" .set targets. Real
378 definitions won't have VT_EXTERN set. */
379 if (esym && esym->st_shndx != SHN_UNDEF) {
380 /* the label is already defined */
381 if (IS_ASM_SYM(sym)
382 && (is_local == 1 || (sym->type.t & VT_EXTERN)))
383 goto new_label;
384 if (!(sym->type.t & VT_EXTERN))
385 tcc_error("assembler label '%s' already defined",
386 get_tok_str(label, NULL));
388 } else {
389 new_label:
390 sym = asm_label_push(label);
392 if (!sym->c)
393 put_extern_sym2(sym, SHN_UNDEF, 0, 0, 0);
394 esym = elfsym(sym);
395 esym->st_shndx = sh_num;
396 esym->st_value = value;
397 if (is_local != 2)
398 sym->type.t &= ~VT_EXTERN;
399 return sym;
402 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
404 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
407 /* Set the value of LABEL to that of some expression (possibly
408 involving other symbols). LABEL can be overwritten later still. */
409 static Sym* set_symbol(TCCState *s1, int label)
411 long n;
412 ExprValue e;
413 Sym *sym;
414 ElfSym *esym;
415 next();
416 asm_expr(s1, &e);
417 n = e.v;
418 esym = elfsym(e.sym);
419 if (esym)
420 n += esym->st_value;
421 sym = asm_new_label1(s1, label, 2, esym ? esym->st_shndx : SHN_ABS, n);
422 elfsym(sym)->st_other |= ST_ASM_SET;
423 return sym;
426 static void use_section1(TCCState *s1, Section *sec)
428 cur_text_section->data_offset = ind;
429 cur_text_section = sec;
430 ind = cur_text_section->data_offset;
433 static void use_section(TCCState *s1, const char *name)
435 Section *sec;
436 sec = find_section(s1, name);
437 use_section1(s1, sec);
440 static void push_section(TCCState *s1, const char *name)
442 Section *sec = find_section(s1, name);
443 sec->prev = cur_text_section;
444 use_section1(s1, sec);
447 static void pop_section(TCCState *s1)
449 Section *prev = cur_text_section->prev;
450 if (!prev)
451 tcc_error(".popsection without .pushsection");
452 cur_text_section->prev = NULL;
453 use_section1(s1, prev);
456 static void asm_parse_directive(TCCState *s1, int global)
458 int n, offset, v, size, tok1;
459 Section *sec;
460 uint8_t *ptr;
462 /* assembler directive */
463 sec = cur_text_section;
464 switch(tok) {
465 case TOK_ASMDIR_align:
466 case TOK_ASMDIR_balign:
467 case TOK_ASMDIR_p2align:
468 case TOK_ASMDIR_skip:
469 case TOK_ASMDIR_space:
470 tok1 = tok;
471 next();
472 n = asm_int_expr(s1);
473 if (tok1 == TOK_ASMDIR_p2align)
475 if (n < 0 || n > 30)
476 tcc_error("invalid p2align, must be between 0 and 30");
477 n = 1 << n;
478 tok1 = TOK_ASMDIR_align;
480 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
481 if (n < 0 || (n & (n-1)) != 0)
482 tcc_error("alignment must be a positive power of two");
483 offset = (ind + n - 1) & -n;
484 size = offset - ind;
485 /* the section must have a compatible alignment */
486 if (sec->sh_addralign < n)
487 sec->sh_addralign = n;
488 } else {
489 if (n < 0)
490 n = 0;
491 size = n;
493 v = 0;
494 if (tok == ',') {
495 next();
496 v = asm_int_expr(s1);
498 zero_pad:
499 if (sec->sh_type != SHT_NOBITS) {
500 sec->data_offset = ind;
501 ptr = section_ptr_add(sec, size);
502 memset(ptr, v, size);
504 ind += size;
505 break;
506 case TOK_ASMDIR_quad:
507 #ifdef TCC_TARGET_X86_64
508 size = 8;
509 goto asm_data;
510 #else
511 next();
512 for(;;) {
513 uint64_t vl;
514 const char *p;
516 p = tokc.str.data;
517 if (tok != TOK_PPNUM) {
518 error_constant:
519 tcc_error("64 bit constant");
521 vl = strtoll(p, (char **)&p, 0);
522 if (*p != '\0')
523 goto error_constant;
524 next();
525 if (sec->sh_type != SHT_NOBITS) {
526 /* XXX: endianness */
527 gen_le32(vl);
528 gen_le32(vl >> 32);
529 } else {
530 ind += 8;
532 if (tok != ',')
533 break;
534 next();
536 break;
537 #endif
538 case TOK_ASMDIR_byte:
539 size = 1;
540 goto asm_data;
541 case TOK_ASMDIR_word:
542 case TOK_ASMDIR_short:
543 size = 2;
544 goto asm_data;
545 case TOK_ASMDIR_long:
546 case TOK_ASMDIR_int:
547 size = 4;
548 asm_data:
549 next();
550 for(;;) {
551 ExprValue e;
552 asm_expr(s1, &e);
553 if (sec->sh_type != SHT_NOBITS) {
554 if (size == 4) {
555 gen_expr32(&e);
556 #ifdef TCC_TARGET_X86_64
557 } else if (size == 8) {
558 gen_expr64(&e);
559 #endif
560 } else {
561 if (e.sym)
562 expect("constant");
563 if (size == 1)
564 g(e.v);
565 else
566 gen_le16(e.v);
568 } else {
569 ind += size;
571 if (tok != ',')
572 break;
573 next();
575 break;
576 case TOK_ASMDIR_fill:
578 int repeat, size, val, i, j;
579 uint8_t repeat_buf[8];
580 next();
581 repeat = asm_int_expr(s1);
582 if (repeat < 0) {
583 tcc_error("repeat < 0; .fill ignored");
584 break;
586 size = 1;
587 val = 0;
588 if (tok == ',') {
589 next();
590 size = asm_int_expr(s1);
591 if (size < 0) {
592 tcc_error("size < 0; .fill ignored");
593 break;
595 if (size > 8)
596 size = 8;
597 if (tok == ',') {
598 next();
599 val = asm_int_expr(s1);
602 /* XXX: endianness */
603 repeat_buf[0] = val;
604 repeat_buf[1] = val >> 8;
605 repeat_buf[2] = val >> 16;
606 repeat_buf[3] = val >> 24;
607 repeat_buf[4] = 0;
608 repeat_buf[5] = 0;
609 repeat_buf[6] = 0;
610 repeat_buf[7] = 0;
611 for(i = 0; i < repeat; i++) {
612 for(j = 0; j < size; j++) {
613 g(repeat_buf[j]);
617 break;
618 case TOK_ASMDIR_rept:
620 int repeat;
621 TokenString *init_str;
622 next();
623 repeat = asm_int_expr(s1);
624 init_str = tok_str_alloc();
625 while (next(), tok != TOK_ASMDIR_endr) {
626 if (tok == CH_EOF)
627 tcc_error("we at end of file, .endr not found");
628 tok_str_add_tok(init_str);
630 tok_str_add(init_str, -1);
631 tok_str_add(init_str, 0);
632 begin_macro(init_str, 1);
633 while (repeat-- > 0) {
634 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
635 global);
636 macro_ptr = init_str->str;
638 end_macro();
639 next();
640 break;
642 case TOK_ASMDIR_org:
644 unsigned long n;
645 ExprValue e;
646 ElfSym *esym;
647 next();
648 asm_expr(s1, &e);
649 n = e.v;
650 esym = elfsym(e.sym);
651 if (esym) {
652 if (esym->st_shndx != cur_text_section->sh_num)
653 expect("constant or same-section symbol");
654 n += esym->st_value;
656 if (n < ind)
657 tcc_error("attempt to .org backwards");
658 v = 0;
659 size = n - ind;
660 goto zero_pad;
662 break;
663 case TOK_ASMDIR_set:
664 next();
665 tok1 = tok;
666 next();
667 /* Also accept '.set stuff', but don't do anything with this.
668 It's used in GAS to set various features like '.set mips16'. */
669 if (tok == ',')
670 set_symbol(s1, tok1);
671 break;
672 case TOK_ASMDIR_globl:
673 case TOK_ASMDIR_global:
674 case TOK_ASMDIR_weak:
675 case TOK_ASMDIR_hidden:
676 tok1 = tok;
677 do {
678 Sym *sym;
679 next();
680 sym = get_asm_sym(tok, NULL);
681 if (tok1 != TOK_ASMDIR_hidden)
682 sym->type.t &= ~VT_STATIC;
683 if (tok1 == TOK_ASMDIR_weak)
684 sym->a.weak = 1;
685 else if (tok1 == TOK_ASMDIR_hidden)
686 sym->a.visibility = STV_HIDDEN;
687 update_storage(sym);
688 next();
689 } while (tok == ',');
690 break;
691 case TOK_ASMDIR_string:
692 case TOK_ASMDIR_ascii:
693 case TOK_ASMDIR_asciz:
695 const uint8_t *p;
696 int i, size, t;
698 t = tok;
699 next();
700 for(;;) {
701 if (tok != TOK_STR)
702 expect("string constant");
703 p = tokc.str.data;
704 size = tokc.str.size;
705 if (t == TOK_ASMDIR_ascii && size > 0)
706 size--;
707 for(i = 0; i < size; i++)
708 g(p[i]);
709 next();
710 if (tok == ',') {
711 next();
712 } else if (tok != TOK_STR) {
713 break;
717 break;
718 case TOK_ASMDIR_text:
719 case TOK_ASMDIR_data:
720 case TOK_ASMDIR_bss:
722 char sname[64];
723 tok1 = tok;
724 n = 0;
725 next();
726 if (tok != ';' && tok != TOK_LINEFEED) {
727 n = asm_int_expr(s1);
728 next();
730 if (n)
731 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
732 else
733 sprintf(sname, "%s", get_tok_str(tok1, NULL));
734 use_section(s1, sname);
736 break;
737 case TOK_ASMDIR_file:
739 char filename[512];
741 filename[0] = '\0';
742 next();
744 if (tok == TOK_STR)
745 pstrcat(filename, sizeof(filename), tokc.str.data);
746 else
747 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
749 if (s1->warn_unsupported)
750 tcc_warning("ignoring .file %s", filename);
752 next();
754 break;
755 case TOK_ASMDIR_ident:
757 char ident[256];
759 ident[0] = '\0';
760 next();
762 if (tok == TOK_STR)
763 pstrcat(ident, sizeof(ident), tokc.str.data);
764 else
765 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
767 if (s1->warn_unsupported)
768 tcc_warning("ignoring .ident %s", ident);
770 next();
772 break;
773 case TOK_ASMDIR_size:
775 Sym *sym;
777 next();
778 sym = asm_label_find(tok);
779 if (!sym) {
780 tcc_error("label not found: %s", get_tok_str(tok, NULL));
783 /* XXX .size name,label2-label1 */
784 if (s1->warn_unsupported)
785 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
787 next();
788 skip(',');
789 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
790 next();
793 break;
794 case TOK_ASMDIR_type:
796 Sym *sym;
797 const char *newtype;
799 next();
800 sym = get_asm_sym(tok, NULL);
801 next();
802 skip(',');
803 if (tok == TOK_STR) {
804 newtype = tokc.str.data;
805 } else {
806 if (tok == '@' || tok == '%')
807 next();
808 newtype = get_tok_str(tok, NULL);
811 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
812 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
814 else if (s1->warn_unsupported)
815 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
816 get_tok_str(sym->v, NULL), sym->type.t, newtype);
818 next();
820 break;
821 case TOK_ASMDIR_pushsection:
822 case TOK_ASMDIR_section:
824 char sname[256];
825 int old_nb_section = s1->nb_sections;
827 tok1 = tok;
828 /* XXX: support more options */
829 next();
830 sname[0] = '\0';
831 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
832 if (tok == TOK_STR)
833 pstrcat(sname, sizeof(sname), tokc.str.data);
834 else
835 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
836 next();
838 if (tok == ',') {
839 /* skip section options */
840 next();
841 if (tok != TOK_STR)
842 expect("string constant");
843 next();
844 if (tok == ',') {
845 next();
846 if (tok == '@' || tok == '%')
847 next();
848 next();
851 last_text_section = cur_text_section;
852 if (tok1 == TOK_ASMDIR_section)
853 use_section(s1, sname);
854 else
855 push_section(s1, sname);
856 /* If we just allocated a new section reset its alignment to
857 1. new_section normally acts for GCC compatibility and
858 sets alignment to PTR_SIZE. The assembler behaves different. */
859 if (old_nb_section != s1->nb_sections)
860 cur_text_section->sh_addralign = 1;
862 break;
863 case TOK_ASMDIR_previous:
865 Section *sec;
866 next();
867 if (!last_text_section)
868 tcc_error("no previous section referenced");
869 sec = cur_text_section;
870 use_section1(s1, last_text_section);
871 last_text_section = sec;
873 break;
874 case TOK_ASMDIR_popsection:
875 next();
876 pop_section(s1);
877 break;
878 #ifdef TCC_TARGET_I386
879 case TOK_ASMDIR_code16:
881 next();
882 s1->seg_size = 16;
884 break;
885 case TOK_ASMDIR_code32:
887 next();
888 s1->seg_size = 32;
890 break;
891 #endif
892 #ifdef TCC_TARGET_X86_64
893 /* added for compatibility with GAS */
894 case TOK_ASMDIR_code64:
895 next();
896 break;
897 #endif
898 default:
899 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
900 break;
905 /* assemble a file */
906 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
908 int opcode;
909 int saved_parse_flags = parse_flags;
911 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
912 if (do_preprocess)
913 parse_flags |= PARSE_FLAG_PREPROCESS;
914 for(;;) {
915 next();
916 if (tok == TOK_EOF)
917 break;
918 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
919 redo:
920 if (tok == '#') {
921 /* horrible gas comment */
922 while (tok != TOK_LINEFEED)
923 next();
924 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
925 asm_parse_directive(s1, global);
926 } else if (tok == TOK_PPNUM) {
927 const char *p;
928 int n;
929 p = tokc.str.data;
930 n = strtoul(p, (char **)&p, 10);
931 if (*p != '\0')
932 expect("':'");
933 /* new local label */
934 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
935 next();
936 skip(':');
937 goto redo;
938 } else if (tok >= TOK_IDENT) {
939 /* instruction or label */
940 opcode = tok;
941 next();
942 if (tok == ':') {
943 /* new label */
944 asm_new_label(s1, opcode, 0);
945 next();
946 goto redo;
947 } else if (tok == '=') {
948 set_symbol(s1, opcode);
949 goto redo;
950 } else {
951 asm_opcode(s1, opcode);
954 /* end of line */
955 if (tok != ';' && tok != TOK_LINEFEED)
956 expect("end of line");
957 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
960 parse_flags = saved_parse_flags;
961 return 0;
964 /* Assemble the current file */
965 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
967 int ret;
968 tcc_debug_start(s1);
969 /* default section is text */
970 cur_text_section = text_section;
971 ind = cur_text_section->data_offset;
972 nocode_wanted = 0;
973 ret = tcc_assemble_internal(s1, do_preprocess, 1);
974 cur_text_section->data_offset = ind;
975 tcc_debug_end(s1);
976 return ret;
979 /********************************************************************/
980 /* GCC inline asm support */
982 /* assemble the string 'str' in the current C compilation unit without
983 C preprocessing. NOTE: str is modified by modifying the '\0' at the
984 end */
985 static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
987 const int *saved_macro_ptr = macro_ptr;
988 int dotid = set_idnum('.', IS_ID);
990 tcc_open_bf(s1, ":asm:", len);
991 memcpy(file->buffer, str, len);
992 macro_ptr = NULL;
993 tcc_assemble_internal(s1, 0, global);
994 tcc_close();
996 set_idnum('.', dotid);
997 macro_ptr = saved_macro_ptr;
1000 /* find a constraint by its number or id (gcc 3 extended
1001 syntax). return -1 if not found. Return in *pp in char after the
1002 constraint */
1003 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1004 const char *name, const char **pp)
1006 int index;
1007 TokenSym *ts;
1008 const char *p;
1010 if (isnum(*name)) {
1011 index = 0;
1012 while (isnum(*name)) {
1013 index = (index * 10) + (*name) - '0';
1014 name++;
1016 if ((unsigned)index >= nb_operands)
1017 index = -1;
1018 } else if (*name == '[') {
1019 name++;
1020 p = strchr(name, ']');
1021 if (p) {
1022 ts = tok_alloc(name, p - name);
1023 for(index = 0; index < nb_operands; index++) {
1024 if (operands[index].id == ts->tok)
1025 goto found;
1027 index = -1;
1028 found:
1029 name = p + 1;
1030 } else {
1031 index = -1;
1033 } else {
1034 index = -1;
1036 if (pp)
1037 *pp = name;
1038 return index;
1041 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1042 CString *out_str, CString *in_str)
1044 int c, index, modifier;
1045 const char *str;
1046 ASMOperand *op;
1047 SValue sv;
1049 cstr_new(out_str);
1050 str = in_str->data;
1051 for(;;) {
1052 c = *str++;
1053 if (c == '%') {
1054 if (*str == '%') {
1055 str++;
1056 goto add_char;
1058 modifier = 0;
1059 if (*str == 'c' || *str == 'n' ||
1060 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1061 *str == 'q' ||
1062 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1063 and make literal operands not be decorated with '$'. */
1064 *str == 'P')
1065 modifier = *str++;
1066 index = find_constraint(operands, nb_operands, str, &str);
1067 if (index < 0)
1068 tcc_error("invalid operand reference after %%");
1069 op = &operands[index];
1070 sv = *op->vt;
1071 if (op->reg >= 0) {
1072 sv.r = op->reg;
1073 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1074 sv.r |= VT_LVAL;
1076 subst_asm_operand(out_str, &sv, modifier);
1077 } else {
1078 add_char:
1079 cstr_ccat(out_str, c);
1080 if (c == '\0')
1081 break;
1087 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1088 int is_output)
1090 ASMOperand *op;
1091 int nb_operands;
1093 if (tok != ':') {
1094 nb_operands = *nb_operands_ptr;
1095 for(;;) {
1096 CString astr;
1097 if (nb_operands >= MAX_ASM_OPERANDS)
1098 tcc_error("too many asm operands");
1099 op = &operands[nb_operands++];
1100 op->id = 0;
1101 if (tok == '[') {
1102 next();
1103 if (tok < TOK_IDENT)
1104 expect("identifier");
1105 op->id = tok;
1106 next();
1107 skip(']');
1109 parse_mult_str(&astr, "string constant");
1110 op->constraint = tcc_malloc(astr.size);
1111 strcpy(op->constraint, astr.data);
1112 cstr_free(&astr);
1113 skip('(');
1114 gexpr();
1115 if (is_output) {
1116 if (!(vtop->type.t & VT_ARRAY))
1117 test_lvalue();
1118 } else {
1119 /* we want to avoid LLOCAL case, except when the 'm'
1120 constraint is used. Note that it may come from
1121 register storage, so we need to convert (reg)
1122 case */
1123 if ((vtop->r & VT_LVAL) &&
1124 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1125 (vtop->r & VT_VALMASK) < VT_CONST) &&
1126 !strchr(op->constraint, 'm')) {
1127 gv(RC_INT);
1130 op->vt = vtop;
1131 skip(')');
1132 if (tok == ',') {
1133 next();
1134 } else {
1135 break;
1138 *nb_operands_ptr = nb_operands;
1142 /* parse the GCC asm() instruction */
1143 ST_FUNC void asm_instr(void)
1145 CString astr, astr1;
1146 ASMOperand operands[MAX_ASM_OPERANDS];
1147 int nb_outputs, nb_operands, i, must_subst, out_reg;
1148 uint8_t clobber_regs[NB_ASM_REGS];
1149 Section *sec;
1151 /* since we always generate the asm() instruction, we can ignore
1152 volatile */
1153 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1154 next();
1156 parse_asm_str(&astr);
1157 nb_operands = 0;
1158 nb_outputs = 0;
1159 must_subst = 0;
1160 memset(clobber_regs, 0, sizeof(clobber_regs));
1161 if (tok == ':') {
1162 next();
1163 must_subst = 1;
1164 /* output args */
1165 parse_asm_operands(operands, &nb_operands, 1);
1166 nb_outputs = nb_operands;
1167 if (tok == ':') {
1168 next();
1169 if (tok != ')') {
1170 /* input args */
1171 parse_asm_operands(operands, &nb_operands, 0);
1172 if (tok == ':') {
1173 /* clobber list */
1174 /* XXX: handle registers */
1175 next();
1176 for(;;) {
1177 if (tok != TOK_STR)
1178 expect("string constant");
1179 asm_clobber(clobber_regs, tokc.str.data);
1180 next();
1181 if (tok == ',') {
1182 next();
1183 } else {
1184 break;
1191 skip(')');
1192 /* NOTE: we do not eat the ';' so that we can restore the current
1193 token after the assembler parsing */
1194 if (tok != ';')
1195 expect("';'");
1197 /* save all values in the memory */
1198 save_regs(0);
1200 /* compute constraints */
1201 asm_compute_constraints(operands, nb_operands, nb_outputs,
1202 clobber_regs, &out_reg);
1204 /* substitute the operands in the asm string. No substitution is
1205 done if no operands (GCC behaviour) */
1206 #ifdef ASM_DEBUG
1207 printf("asm: \"%s\"\n", (char *)astr.data);
1208 #endif
1209 if (must_subst) {
1210 subst_asm_operands(operands, nb_operands, &astr1, &astr);
1211 cstr_free(&astr);
1212 } else {
1213 astr1 = astr;
1215 #ifdef ASM_DEBUG
1216 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1217 #endif
1219 /* generate loads */
1220 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1221 clobber_regs, out_reg);
1223 /* We don't allow switching section within inline asm to
1224 bleed out to surrounding code. */
1225 sec = cur_text_section;
1226 /* assemble the string with tcc internal assembler */
1227 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1228 if (sec != cur_text_section) {
1229 tcc_warning("inline asm tries to change current section");
1230 use_section1(tcc_state, sec);
1233 /* restore the current C token */
1234 next();
1236 /* store the output values if needed */
1237 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1238 clobber_regs, out_reg);
1240 /* free everything */
1241 for(i=0;i<nb_operands;i++) {
1242 ASMOperand *op;
1243 op = &operands[i];
1244 tcc_free(op->constraint);
1245 vpop();
1247 cstr_free(&astr1);
1250 ST_FUNC void asm_global_instr(void)
1252 CString astr;
1253 int saved_nocode_wanted = nocode_wanted;
1255 /* Global asm blocks are always emitted. */
1256 nocode_wanted = 0;
1257 next();
1258 parse_asm_str(&astr);
1259 skip(')');
1260 /* NOTE: we do not eat the ';' so that we can restore the current
1261 token after the assembler parsing */
1262 if (tok != ';')
1263 expect("';'");
1265 #ifdef ASM_DEBUG
1266 printf("asm_global: \"%s\"\n", (char *)astr.data);
1267 #endif
1268 cur_text_section = text_section;
1269 ind = cur_text_section->data_offset;
1271 /* assemble the string with tcc internal assembler */
1272 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1274 cur_text_section->data_offset = ind;
1276 /* restore the current C token */
1277 next();
1279 cstr_free(&astr);
1280 nocode_wanted = saved_nocode_wanted;
1282 #endif /* CONFIG_TCC_ASM */