Don't allow section switches in local asm instructions
[tinycc.git] / tccasm.c
blob54d28dac7f0fe4b07f840295b972b0f6deb29bce
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 return global_identifier_push(v, VT_ASM | VT_EXTERN | VT_STATIC, 0);
54 /* Return a symbol we can use inside the assembler, having name NAME.
55 Symbols from asm and C source share a namespace. If we generate
56 an asm symbol it's also a (file-global) C symbol, but it's
57 either not accessible by name (like "L.123"), or its type information
58 is such that it's not usable without a proper C declaration.
60 Sometimes we need symbols accessible by name from asm, which
61 are anonymous in C, in this case CSYM can be used to transfer
62 all information from that symbol to the (possibly newly created)
63 asm symbol. */
64 ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
66 Sym *sym = asm_label_find(name);
67 if (!sym) {
68 sym = asm_label_push(name);
69 if (csym)
70 sym->c = csym->c;
72 return sym;
75 static Sym* asm_section_sym(TCCState *s1, Section *sec)
77 char buf[100];
78 int label = tok_alloc(buf,
79 snprintf(buf, sizeof buf, "L.%s", sec->name)
80 )->tok;
81 Sym *sym = asm_label_find(label);
82 return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0);
85 /* We do not use the C expression parser to handle symbols. Maybe the
86 C expression parser could be tweaked to do so. */
88 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
90 Sym *sym;
91 int op, label;
92 uint64_t n;
93 const char *p;
95 switch(tok) {
96 case TOK_PPNUM:
97 p = tokc.str.data;
98 n = strtoull(p, (char **)&p, 0);
99 if (*p == 'b' || *p == 'f') {
100 /* backward or forward label */
101 label = asm_get_local_label_name(s1, n);
102 sym = asm_label_find(label);
103 if (*p == 'b') {
104 /* backward : find the last corresponding defined label */
105 if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))
106 sym = sym->prev_tok;
107 if (!sym)
108 tcc_error("local label '%d' not found backward", n);
109 } else {
110 /* forward */
111 if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) {
112 /* if the last label is defined, then define a new one */
113 sym = asm_label_push(label);
116 pe->v = 0;
117 pe->sym = sym;
118 pe->pcrel = 0;
119 } else if (*p == '\0') {
120 pe->v = n;
121 pe->sym = NULL;
122 pe->pcrel = 0;
123 } else {
124 tcc_error("invalid number syntax");
126 next();
127 break;
128 case '+':
129 next();
130 asm_expr_unary(s1, pe);
131 break;
132 case '-':
133 case '~':
134 op = tok;
135 next();
136 asm_expr_unary(s1, pe);
137 if (pe->sym)
138 tcc_error("invalid operation with label");
139 if (op == '-')
140 pe->v = -pe->v;
141 else
142 pe->v = ~pe->v;
143 break;
144 case TOK_CCHAR:
145 case TOK_LCHAR:
146 pe->v = tokc.i;
147 pe->sym = NULL;
148 pe->pcrel = 0;
149 next();
150 break;
151 case '(':
152 next();
153 asm_expr(s1, pe);
154 skip(')');
155 break;
156 case '.':
157 pe->v = ind;
158 pe->sym = asm_section_sym(s1, cur_text_section);
159 pe->pcrel = 0;
160 next();
161 break;
162 default:
163 if (tok >= TOK_IDENT) {
164 ElfSym *esym;
165 /* label case : if the label was not found, add one */
166 sym = get_asm_sym(tok, NULL);
167 esym = elfsym(sym);
168 if (esym && esym->st_shndx == SHN_ABS) {
169 /* if absolute symbol, no need to put a symbol value */
170 pe->v = esym->st_value;
171 pe->sym = NULL;
172 pe->pcrel = 0;
173 } else {
174 pe->v = 0;
175 pe->sym = sym;
176 pe->pcrel = 0;
178 next();
179 } else {
180 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
182 break;
186 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
188 int op;
189 ExprValue e2;
191 asm_expr_unary(s1, pe);
192 for(;;) {
193 op = tok;
194 if (op != '*' && op != '/' && op != '%' &&
195 op != TOK_SHL && op != TOK_SAR)
196 break;
197 next();
198 asm_expr_unary(s1, &e2);
199 if (pe->sym || e2.sym)
200 tcc_error("invalid operation with label");
201 switch(op) {
202 case '*':
203 pe->v *= e2.v;
204 break;
205 case '/':
206 if (e2.v == 0) {
207 div_error:
208 tcc_error("division by zero");
210 pe->v /= e2.v;
211 break;
212 case '%':
213 if (e2.v == 0)
214 goto div_error;
215 pe->v %= e2.v;
216 break;
217 case TOK_SHL:
218 pe->v <<= e2.v;
219 break;
220 default:
221 case TOK_SAR:
222 pe->v >>= e2.v;
223 break;
228 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
230 int op;
231 ExprValue e2;
233 asm_expr_prod(s1, pe);
234 for(;;) {
235 op = tok;
236 if (op != '&' && op != '|' && op != '^')
237 break;
238 next();
239 asm_expr_prod(s1, &e2);
240 if (pe->sym || e2.sym)
241 tcc_error("invalid operation with label");
242 switch(op) {
243 case '&':
244 pe->v &= e2.v;
245 break;
246 case '|':
247 pe->v |= e2.v;
248 break;
249 default:
250 case '^':
251 pe->v ^= e2.v;
252 break;
257 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
259 int op;
260 ExprValue e2;
262 asm_expr_logic(s1, pe);
263 for(;;) {
264 op = tok;
265 if (op != '+' && op != '-')
266 break;
267 next();
268 asm_expr_logic(s1, &e2);
269 if (op == '+') {
270 if (pe->sym != NULL && e2.sym != NULL)
271 goto cannot_relocate;
272 pe->v += e2.v;
273 if (pe->sym == NULL && e2.sym != NULL)
274 pe->sym = e2.sym;
275 } else {
276 pe->v -= e2.v;
277 /* NOTE: we are less powerful than gas in that case
278 because we store only one symbol in the expression */
279 if (!e2.sym) {
280 /* OK */
281 } else if (pe->sym == e2.sym) {
282 /* OK */
283 pe->sym = NULL; /* same symbols can be subtracted to NULL */
284 } else {
285 ElfSym *esym1, *esym2;
286 esym1 = elfsym(pe->sym);
287 esym2 = elfsym(e2.sym);
288 if (esym1 && esym1->st_shndx == esym2->st_shndx
289 && esym1->st_shndx != SHN_UNDEF) {
290 /* we also accept defined symbols in the same section */
291 pe->v += esym1->st_value - esym2->st_value;
292 pe->sym = NULL;
293 } else if (esym2->st_shndx == cur_text_section->sh_num) {
294 /* When subtracting a defined symbol in current section
295 this actually makes the value PC-relative. */
296 pe->v -= esym2->st_value - ind - 4;
297 pe->pcrel = 1;
298 e2.sym = NULL;
299 } else {
300 cannot_relocate:
301 tcc_error("invalid operation with label");
308 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
310 int op;
311 ExprValue e2;
313 asm_expr_sum(s1, pe);
314 for(;;) {
315 op = tok;
316 if (op != TOK_EQ && op != TOK_NE
317 && (op > TOK_GT || op < TOK_ULE))
318 break;
319 next();
320 asm_expr_sum(s1, &e2);
321 if (pe->sym || e2.sym)
322 tcc_error("invalid operation with label");
323 switch(op) {
324 case TOK_EQ:
325 pe->v = pe->v == e2.v;
326 break;
327 case TOK_NE:
328 pe->v = pe->v != e2.v;
329 break;
330 case TOK_LT:
331 pe->v = (int64_t)pe->v < (int64_t)e2.v;
332 break;
333 case TOK_GE:
334 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
335 break;
336 case TOK_LE:
337 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
338 break;
339 case TOK_GT:
340 pe->v = (int64_t)pe->v > (int64_t)e2.v;
341 break;
342 default:
343 break;
345 /* GAS compare results are -1/0 not 1/0. */
346 pe->v = -(int64_t)pe->v;
350 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
352 asm_expr_cmp(s1, pe);
355 ST_FUNC int asm_int_expr(TCCState *s1)
357 ExprValue e;
358 asm_expr(s1, &e);
359 if (e.sym)
360 expect("constant");
361 return e.v;
364 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
365 int sh_num, int value)
367 Sym *sym;
368 ElfSym *esym;
370 sym = asm_label_find(label);
371 if (sym) {
372 esym = elfsym(sym);
373 /* A VT_EXTERN symbol, even if it has a section is considered
374 overridable. This is how we "define" .set targets. Real
375 definitions won't have VT_EXTERN set. */
376 if (esym && esym->st_shndx != SHN_UNDEF) {
377 /* the label is already defined */
378 if (IS_ASM_SYM(sym)
379 && (is_local == 1 || (sym->type.t & VT_EXTERN)))
380 goto new_label;
381 if (!(sym->type.t & VT_EXTERN))
382 tcc_error("assembler label '%s' already defined",
383 get_tok_str(label, NULL));
385 } else {
386 new_label:
387 sym = asm_label_push(label);
389 if (!sym->c)
390 put_extern_sym2(sym, SHN_UNDEF, 0, 0, 0);
391 esym = elfsym(sym);
392 esym->st_shndx = sh_num;
393 esym->st_value = value;
394 if (is_local != 2)
395 sym->type.t &= ~VT_EXTERN;
396 return sym;
399 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
401 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
404 /* Set the value of LABEL to that of some expression (possibly
405 involving other symbols). LABEL can be overwritten later still. */
406 static Sym* set_symbol(TCCState *s1, int label)
408 long n;
409 ExprValue e;
410 Sym *sym;
411 ElfSym *esym;
412 next();
413 asm_expr(s1, &e);
414 n = e.v;
415 esym = elfsym(e.sym);
416 if (esym)
417 n += esym->st_value;
418 sym = asm_new_label1(s1, label, 2, esym ? esym->st_shndx : SHN_ABS, n);
419 elfsym(sym)->st_other |= ST_ASM_SET;
420 return sym;
423 static void use_section1(TCCState *s1, Section *sec)
425 cur_text_section->data_offset = ind;
426 cur_text_section = sec;
427 ind = cur_text_section->data_offset;
430 static void use_section(TCCState *s1, const char *name)
432 Section *sec;
433 sec = find_section(s1, name);
434 use_section1(s1, sec);
437 static void push_section(TCCState *s1, const char *name)
439 Section *sec = find_section(s1, name);
440 sec->prev = cur_text_section;
441 use_section1(s1, sec);
444 static void pop_section(TCCState *s1)
446 Section *prev = cur_text_section->prev;
447 if (!prev)
448 tcc_error(".popsection without .pushsection");
449 cur_text_section->prev = NULL;
450 use_section1(s1, prev);
453 static void asm_parse_directive(TCCState *s1, int global)
455 int n, offset, v, size, tok1;
456 Section *sec;
457 uint8_t *ptr;
459 /* assembler directive */
460 sec = cur_text_section;
461 switch(tok) {
462 case TOK_ASMDIR_align:
463 case TOK_ASMDIR_balign:
464 case TOK_ASMDIR_p2align:
465 case TOK_ASMDIR_skip:
466 case TOK_ASMDIR_space:
467 tok1 = tok;
468 next();
469 n = asm_int_expr(s1);
470 if (tok1 == TOK_ASMDIR_p2align)
472 if (n < 0 || n > 30)
473 tcc_error("invalid p2align, must be between 0 and 30");
474 n = 1 << n;
475 tok1 = TOK_ASMDIR_align;
477 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
478 if (n < 0 || (n & (n-1)) != 0)
479 tcc_error("alignment must be a positive power of two");
480 offset = (ind + n - 1) & -n;
481 size = offset - ind;
482 /* the section must have a compatible alignment */
483 if (sec->sh_addralign < n)
484 sec->sh_addralign = n;
485 } else {
486 if (n < 0)
487 n = 0;
488 size = n;
490 v = 0;
491 if (tok == ',') {
492 next();
493 v = asm_int_expr(s1);
495 zero_pad:
496 if (sec->sh_type != SHT_NOBITS) {
497 sec->data_offset = ind;
498 ptr = section_ptr_add(sec, size);
499 memset(ptr, v, size);
501 ind += size;
502 break;
503 case TOK_ASMDIR_quad:
504 #ifdef TCC_TARGET_X86_64
505 size = 8;
506 goto asm_data;
507 #else
508 next();
509 for(;;) {
510 uint64_t vl;
511 const char *p;
513 p = tokc.str.data;
514 if (tok != TOK_PPNUM) {
515 error_constant:
516 tcc_error("64 bit constant");
518 vl = strtoll(p, (char **)&p, 0);
519 if (*p != '\0')
520 goto error_constant;
521 next();
522 if (sec->sh_type != SHT_NOBITS) {
523 /* XXX: endianness */
524 gen_le32(vl);
525 gen_le32(vl >> 32);
526 } else {
527 ind += 8;
529 if (tok != ',')
530 break;
531 next();
533 break;
534 #endif
535 case TOK_ASMDIR_byte:
536 size = 1;
537 goto asm_data;
538 case TOK_ASMDIR_word:
539 case TOK_ASMDIR_short:
540 size = 2;
541 goto asm_data;
542 case TOK_ASMDIR_long:
543 case TOK_ASMDIR_int:
544 size = 4;
545 asm_data:
546 next();
547 for(;;) {
548 ExprValue e;
549 asm_expr(s1, &e);
550 if (sec->sh_type != SHT_NOBITS) {
551 if (size == 4) {
552 gen_expr32(&e);
553 #ifdef TCC_TARGET_X86_64
554 } else if (size == 8) {
555 gen_expr64(&e);
556 #endif
557 } else {
558 if (e.sym)
559 expect("constant");
560 if (size == 1)
561 g(e.v);
562 else
563 gen_le16(e.v);
565 } else {
566 ind += size;
568 if (tok != ',')
569 break;
570 next();
572 break;
573 case TOK_ASMDIR_fill:
575 int repeat, size, val, i, j;
576 uint8_t repeat_buf[8];
577 next();
578 repeat = asm_int_expr(s1);
579 if (repeat < 0) {
580 tcc_error("repeat < 0; .fill ignored");
581 break;
583 size = 1;
584 val = 0;
585 if (tok == ',') {
586 next();
587 size = asm_int_expr(s1);
588 if (size < 0) {
589 tcc_error("size < 0; .fill ignored");
590 break;
592 if (size > 8)
593 size = 8;
594 if (tok == ',') {
595 next();
596 val = asm_int_expr(s1);
599 /* XXX: endianness */
600 repeat_buf[0] = val;
601 repeat_buf[1] = val >> 8;
602 repeat_buf[2] = val >> 16;
603 repeat_buf[3] = val >> 24;
604 repeat_buf[4] = 0;
605 repeat_buf[5] = 0;
606 repeat_buf[6] = 0;
607 repeat_buf[7] = 0;
608 for(i = 0; i < repeat; i++) {
609 for(j = 0; j < size; j++) {
610 g(repeat_buf[j]);
614 break;
615 case TOK_ASMDIR_rept:
617 int repeat;
618 TokenString *init_str;
619 next();
620 repeat = asm_int_expr(s1);
621 init_str = tok_str_alloc();
622 while (next(), tok != TOK_ASMDIR_endr) {
623 if (tok == CH_EOF)
624 tcc_error("we at end of file, .endr not found");
625 tok_str_add_tok(init_str);
627 tok_str_add(init_str, -1);
628 tok_str_add(init_str, 0);
629 begin_macro(init_str, 1);
630 while (repeat-- > 0) {
631 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
632 global);
633 macro_ptr = init_str->str;
635 end_macro();
636 next();
637 break;
639 case TOK_ASMDIR_org:
641 unsigned long n;
642 ExprValue e;
643 ElfSym *esym;
644 next();
645 asm_expr(s1, &e);
646 n = e.v;
647 esym = elfsym(e.sym);
648 if (esym) {
649 if (esym->st_shndx != cur_text_section->sh_num)
650 expect("constant or same-section symbol");
651 n += esym->st_value;
653 if (n < ind)
654 tcc_error("attempt to .org backwards");
655 v = 0;
656 size = n - ind;
657 goto zero_pad;
659 break;
660 case TOK_ASMDIR_set:
661 next();
662 tok1 = tok;
663 next();
664 /* Also accept '.set stuff', but don't do anything with this.
665 It's used in GAS to set various features like '.set mips16'. */
666 if (tok == ',')
667 set_symbol(s1, tok1);
668 break;
669 case TOK_ASMDIR_globl:
670 case TOK_ASMDIR_global:
671 case TOK_ASMDIR_weak:
672 case TOK_ASMDIR_hidden:
673 tok1 = tok;
674 do {
675 Sym *sym;
676 next();
677 sym = get_asm_sym(tok, NULL);
678 if (tok1 != TOK_ASMDIR_hidden)
679 sym->type.t &= ~VT_STATIC;
680 if (tok1 == TOK_ASMDIR_weak)
681 sym->a.weak = 1;
682 else if (tok1 == TOK_ASMDIR_hidden)
683 sym->a.visibility = STV_HIDDEN;
684 update_storage(sym);
685 next();
686 } while (tok == ',');
687 break;
688 case TOK_ASMDIR_string:
689 case TOK_ASMDIR_ascii:
690 case TOK_ASMDIR_asciz:
692 const uint8_t *p;
693 int i, size, t;
695 t = tok;
696 next();
697 for(;;) {
698 if (tok != TOK_STR)
699 expect("string constant");
700 p = tokc.str.data;
701 size = tokc.str.size;
702 if (t == TOK_ASMDIR_ascii && size > 0)
703 size--;
704 for(i = 0; i < size; i++)
705 g(p[i]);
706 next();
707 if (tok == ',') {
708 next();
709 } else if (tok != TOK_STR) {
710 break;
714 break;
715 case TOK_ASMDIR_text:
716 case TOK_ASMDIR_data:
717 case TOK_ASMDIR_bss:
719 char sname[64];
720 tok1 = tok;
721 n = 0;
722 next();
723 if (tok != ';' && tok != TOK_LINEFEED) {
724 n = asm_int_expr(s1);
725 next();
727 if (n)
728 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
729 else
730 sprintf(sname, "%s", get_tok_str(tok1, NULL));
731 use_section(s1, sname);
733 break;
734 case TOK_ASMDIR_file:
736 char filename[512];
738 filename[0] = '\0';
739 next();
741 if (tok == TOK_STR)
742 pstrcat(filename, sizeof(filename), tokc.str.data);
743 else
744 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
746 if (s1->warn_unsupported)
747 tcc_warning("ignoring .file %s", filename);
749 next();
751 break;
752 case TOK_ASMDIR_ident:
754 char ident[256];
756 ident[0] = '\0';
757 next();
759 if (tok == TOK_STR)
760 pstrcat(ident, sizeof(ident), tokc.str.data);
761 else
762 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
764 if (s1->warn_unsupported)
765 tcc_warning("ignoring .ident %s", ident);
767 next();
769 break;
770 case TOK_ASMDIR_size:
772 Sym *sym;
774 next();
775 sym = asm_label_find(tok);
776 if (!sym) {
777 tcc_error("label not found: %s", get_tok_str(tok, NULL));
780 /* XXX .size name,label2-label1 */
781 if (s1->warn_unsupported)
782 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
784 next();
785 skip(',');
786 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
787 next();
790 break;
791 case TOK_ASMDIR_type:
793 Sym *sym;
794 const char *newtype;
796 next();
797 sym = get_asm_sym(tok, NULL);
798 next();
799 skip(',');
800 if (tok == TOK_STR) {
801 newtype = tokc.str.data;
802 } else {
803 if (tok == '@' || tok == '%')
804 next();
805 newtype = get_tok_str(tok, NULL);
808 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
809 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
811 else if (s1->warn_unsupported)
812 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
813 get_tok_str(sym->v, NULL), sym->type.t, newtype);
815 next();
817 break;
818 case TOK_ASMDIR_pushsection:
819 case TOK_ASMDIR_section:
821 char sname[256];
822 int old_nb_section = s1->nb_sections;
824 tok1 = tok;
825 /* XXX: support more options */
826 next();
827 sname[0] = '\0';
828 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
829 if (tok == TOK_STR)
830 pstrcat(sname, sizeof(sname), tokc.str.data);
831 else
832 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
833 next();
835 if (tok == ',') {
836 /* skip section options */
837 next();
838 if (tok != TOK_STR)
839 expect("string constant");
840 next();
841 if (tok == ',') {
842 next();
843 if (tok == '@' || tok == '%')
844 next();
845 next();
848 last_text_section = cur_text_section;
849 if (tok1 == TOK_ASMDIR_section)
850 use_section(s1, sname);
851 else
852 push_section(s1, sname);
853 /* If we just allocated a new section reset its alignment to
854 1. new_section normally acts for GCC compatibility and
855 sets alignment to PTR_SIZE. The assembler behaves different. */
856 if (old_nb_section != s1->nb_sections)
857 cur_text_section->sh_addralign = 1;
859 break;
860 case TOK_ASMDIR_previous:
862 Section *sec;
863 next();
864 if (!last_text_section)
865 tcc_error("no previous section referenced");
866 sec = cur_text_section;
867 use_section1(s1, last_text_section);
868 last_text_section = sec;
870 break;
871 case TOK_ASMDIR_popsection:
872 next();
873 pop_section(s1);
874 break;
875 #ifdef TCC_TARGET_I386
876 case TOK_ASMDIR_code16:
878 next();
879 s1->seg_size = 16;
881 break;
882 case TOK_ASMDIR_code32:
884 next();
885 s1->seg_size = 32;
887 break;
888 #endif
889 #ifdef TCC_TARGET_X86_64
890 /* added for compatibility with GAS */
891 case TOK_ASMDIR_code64:
892 next();
893 break;
894 #endif
895 default:
896 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
897 break;
902 /* assemble a file */
903 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
905 int opcode;
906 int saved_parse_flags = parse_flags;
908 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
909 if (do_preprocess)
910 parse_flags |= PARSE_FLAG_PREPROCESS;
911 for(;;) {
912 next();
913 if (tok == TOK_EOF)
914 break;
915 /* generate line number info */
916 if (global && s1->do_debug)
917 tcc_debug_line(s1);
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 next();
1152 /* since we always generate the asm() instruction, we can ignore
1153 volatile */
1154 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1155 next();
1157 parse_asm_str(&astr);
1158 nb_operands = 0;
1159 nb_outputs = 0;
1160 must_subst = 0;
1161 memset(clobber_regs, 0, sizeof(clobber_regs));
1162 if (tok == ':') {
1163 next();
1164 must_subst = 1;
1165 /* output args */
1166 parse_asm_operands(operands, &nb_operands, 1);
1167 nb_outputs = nb_operands;
1168 if (tok == ':') {
1169 next();
1170 if (tok != ')') {
1171 /* input args */
1172 parse_asm_operands(operands, &nb_operands, 0);
1173 if (tok == ':') {
1174 /* clobber list */
1175 /* XXX: handle registers */
1176 next();
1177 for(;;) {
1178 if (tok != TOK_STR)
1179 expect("string constant");
1180 asm_clobber(clobber_regs, tokc.str.data);
1181 next();
1182 if (tok == ',') {
1183 next();
1184 } else {
1185 break;
1192 skip(')');
1193 /* NOTE: we do not eat the ';' so that we can restore the current
1194 token after the assembler parsing */
1195 if (tok != ';')
1196 expect("';'");
1198 /* save all values in the memory */
1199 save_regs(0);
1201 /* compute constraints */
1202 asm_compute_constraints(operands, nb_operands, nb_outputs,
1203 clobber_regs, &out_reg);
1205 /* substitute the operands in the asm string. No substitution is
1206 done if no operands (GCC behaviour) */
1207 #ifdef ASM_DEBUG
1208 printf("asm: \"%s\"\n", (char *)astr.data);
1209 #endif
1210 if (must_subst) {
1211 subst_asm_operands(operands, nb_operands, &astr1, &astr);
1212 cstr_free(&astr);
1213 } else {
1214 astr1 = astr;
1216 #ifdef ASM_DEBUG
1217 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1218 #endif
1220 /* generate loads */
1221 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1222 clobber_regs, out_reg);
1224 /* We don't allow switching section within inline asm to
1225 bleed out to surrounding code. */
1226 sec = cur_text_section;
1227 /* assemble the string with tcc internal assembler */
1228 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1229 if (sec != cur_text_section) {
1230 tcc_warning("inline asm tries to change current section");
1231 use_section1(tcc_state, sec);
1234 /* restore the current C token */
1235 next();
1237 /* store the output values if needed */
1238 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1239 clobber_regs, out_reg);
1241 /* free everything */
1242 for(i=0;i<nb_operands;i++) {
1243 ASMOperand *op;
1244 op = &operands[i];
1245 tcc_free(op->constraint);
1246 vpop();
1248 cstr_free(&astr1);
1251 ST_FUNC void asm_global_instr(void)
1253 CString astr;
1254 int saved_nocode_wanted = nocode_wanted;
1256 /* Global asm blocks are always emitted. */
1257 nocode_wanted = 0;
1258 next();
1259 parse_asm_str(&astr);
1260 skip(')');
1261 /* NOTE: we do not eat the ';' so that we can restore the current
1262 token after the assembler parsing */
1263 if (tok != ';')
1264 expect("';'");
1266 #ifdef ASM_DEBUG
1267 printf("asm_global: \"%s\"\n", (char *)astr.data);
1268 #endif
1269 cur_text_section = text_section;
1270 ind = cur_text_section->data_offset;
1272 /* assemble the string with tcc internal assembler */
1273 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1275 cur_text_section->data_offset = ind;
1277 /* restore the current C token */
1278 next();
1280 cstr_free(&astr);
1281 nocode_wanted = saved_nocode_wanted;
1283 #endif /* CONFIG_TCC_ASM */