tccasm: Implement .set sym, expr
[tinycc.git] / tccasm.c
blob0bf64561a706bc7606cf776722a558fd9f995908
1 /*
2 * GAS like assembler for TCC
3 *
4 * Copyright (c) 2001-2004 Fabrice Bellard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "tcc.h"
22 #ifdef CONFIG_TCC_ASM
24 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
26 char buf[64];
27 TokenSym *ts;
29 snprintf(buf, sizeof(buf), "L..%u", n);
30 ts = tok_alloc(buf, strlen(buf));
31 return ts->tok;
34 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
35 static int tcc_assemble_internal(TCCState *s1, int do_preprocess);
36 static Sym sym_dot;
38 /* We do not use the C expression parser to handle symbols. Maybe the
39 C expression parser could be tweaked to do so. */
41 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
43 Sym *sym;
44 int op, label;
45 unsigned long n;
46 const char *p;
48 switch(tok) {
49 case TOK_PPNUM:
50 p = tokc.str.data;
51 n = strtoul(p, (char **)&p, 0);
52 if (*p == 'b' || *p == 'f') {
53 /* backward or forward label */
54 label = asm_get_local_label_name(s1, n);
55 sym = label_find(label);
56 if (*p == 'b') {
57 /* backward : find the last corresponding defined label */
58 if (sym && sym->r == 0)
59 sym = sym->prev_tok;
60 if (!sym)
61 tcc_error("local label '%d' not found backward", n);
62 } else {
63 /* forward */
64 if (!sym || sym->r) {
65 /* if the last label is defined, then define a new one */
66 sym = label_push(&s1->asm_labels, label, 0);
67 sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
70 pe->v = 0;
71 pe->sym = sym;
72 pe->pcrel = 0;
73 } else if (*p == '\0') {
74 pe->v = n;
75 pe->sym = NULL;
76 pe->pcrel = 0;
77 } else {
78 tcc_error("invalid number syntax");
80 next();
81 break;
82 case '+':
83 next();
84 asm_expr_unary(s1, pe);
85 break;
86 case '-':
87 case '~':
88 op = tok;
89 next();
90 asm_expr_unary(s1, pe);
91 if (pe->sym)
92 tcc_error("invalid operation with label");
93 if (op == '-')
94 pe->v = -pe->v;
95 else
96 pe->v = ~pe->v;
97 break;
98 case TOK_CCHAR:
99 case TOK_LCHAR:
100 pe->v = tokc.i;
101 pe->sym = NULL;
102 pe->pcrel = 0;
103 next();
104 break;
105 case '(':
106 next();
107 asm_expr(s1, pe);
108 skip(')');
109 break;
110 case '.':
111 pe->v = 0;
112 pe->sym = &sym_dot;
113 pe->pcrel = 0;
114 sym_dot.type.t = VT_VOID | VT_STATIC;
115 sym_dot.r = cur_text_section->sh_num;
116 sym_dot.jnext = ind;
117 next();
118 break;
119 default:
120 if (tok >= TOK_IDENT) {
121 /* label case : if the label was not found, add one */
122 sym = label_find(tok);
123 if (!sym) {
124 sym = label_push(&s1->asm_labels, tok, 0);
125 /* NOTE: by default, the symbol is global */
126 sym->type.t = VT_VOID | VT_EXTERN;
128 if (sym->r == SHN_ABS) {
129 /* if absolute symbol, no need to put a symbol value */
130 pe->v = sym->jnext;
131 pe->sym = NULL;
132 pe->pcrel = 0;
133 } else {
134 pe->v = 0;
135 pe->sym = sym;
136 pe->pcrel = 0;
138 next();
139 } else {
140 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
142 break;
146 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
148 int op;
149 ExprValue e2;
151 asm_expr_unary(s1, pe);
152 for(;;) {
153 op = tok;
154 if (op != '*' && op != '/' && op != '%' &&
155 op != TOK_SHL && op != TOK_SAR)
156 break;
157 next();
158 asm_expr_unary(s1, &e2);
159 if (pe->sym || e2.sym)
160 tcc_error("invalid operation with label");
161 switch(op) {
162 case '*':
163 pe->v *= e2.v;
164 break;
165 case '/':
166 if (e2.v == 0) {
167 div_error:
168 tcc_error("division by zero");
170 pe->v /= e2.v;
171 break;
172 case '%':
173 if (e2.v == 0)
174 goto div_error;
175 pe->v %= e2.v;
176 break;
177 case TOK_SHL:
178 pe->v <<= e2.v;
179 break;
180 default:
181 case TOK_SAR:
182 pe->v >>= e2.v;
183 break;
188 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
190 int op;
191 ExprValue e2;
193 asm_expr_prod(s1, pe);
194 for(;;) {
195 op = tok;
196 if (op != '&' && op != '|' && op != '^')
197 break;
198 next();
199 asm_expr_prod(s1, &e2);
200 if (pe->sym || e2.sym)
201 tcc_error("invalid operation with label");
202 switch(op) {
203 case '&':
204 pe->v &= e2.v;
205 break;
206 case '|':
207 pe->v |= e2.v;
208 break;
209 default:
210 case '^':
211 pe->v ^= e2.v;
212 break;
217 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
219 int op;
220 ExprValue e2;
222 asm_expr_logic(s1, pe);
223 for(;;) {
224 op = tok;
225 if (op != '+' && op != '-')
226 break;
227 next();
228 asm_expr_logic(s1, &e2);
229 if (op == '+') {
230 if (pe->sym != NULL && e2.sym != NULL)
231 goto cannot_relocate;
232 pe->v += e2.v;
233 if (pe->sym == NULL && e2.sym != NULL)
234 pe->sym = e2.sym;
235 } else {
236 pe->v -= e2.v;
237 /* NOTE: we are less powerful than gas in that case
238 because we store only one symbol in the expression */
239 if (!e2.sym) {
240 /* OK */
241 } else if (pe->sym == e2.sym) {
242 /* OK */
243 pe->sym = NULL; /* same symbols can be subtracted to NULL */
244 } else if (pe->sym && pe->sym->r == e2.sym->r && pe->sym->r != 0) {
245 /* we also accept defined symbols in the same section */
246 pe->v += pe->sym->jnext - e2.sym->jnext;
247 pe->sym = NULL;
248 } else if (e2.sym->r == cur_text_section->sh_num) {
249 /* When subtracting a defined symbol in current section
250 this actually makes the value PC-relative. */
251 pe->v -= e2.sym->jnext - ind - 4;
252 pe->pcrel = 1;
253 e2.sym = NULL;
254 } else {
255 cannot_relocate:
256 tcc_error("invalid operation with label");
262 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
264 int op;
265 ExprValue e2;
267 asm_expr_sum(s1, pe);
268 for(;;) {
269 op = tok;
270 if (op != TOK_EQ && op != TOK_NE
271 && (op > TOK_GT || op < TOK_ULE))
272 break;
273 next();
274 asm_expr_sum(s1, &e2);
275 if (pe->sym || e2.sym)
276 tcc_error("invalid operation with label");
277 switch(op) {
278 case TOK_EQ:
279 pe->v = pe->v == e2.v;
280 break;
281 case TOK_NE:
282 pe->v = pe->v != e2.v;
283 break;
284 case TOK_LT:
285 pe->v = (int64_t)pe->v < (int64_t)e2.v;
286 break;
287 case TOK_GE:
288 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
289 break;
290 case TOK_LE:
291 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
292 break;
293 case TOK_GT:
294 pe->v = (int64_t)pe->v > (int64_t)e2.v;
295 break;
296 default:
297 break;
299 /* GAS compare results are -1/0 not 1/0. */
300 pe->v = -(int64_t)pe->v;
304 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
306 asm_expr_cmp(s1, pe);
309 ST_FUNC int asm_int_expr(TCCState *s1)
311 ExprValue e;
312 asm_expr(s1, &e);
313 if (e.sym)
314 expect("constant");
315 return e.v;
318 /* NOTE: the same name space as C labels is used to avoid using too
319 much memory when storing labels in TokenStrings */
320 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
321 int sh_num, int value)
323 Sym *sym;
325 sym = label_find(label);
326 if (sym) {
327 /* A VT_EXTERN symbol, even if it has a section is considered
328 overridable. This is how we "define" .set targets. Real
329 definitions won't have VT_EXTERN set. */
330 if (sym->r && !(sym->type.t & VT_EXTERN)) {
331 /* the label is already defined */
332 if (!is_local) {
333 tcc_error("assembler label '%s' already defined",
334 get_tok_str(label, NULL));
335 } else {
336 /* redefinition of local labels is possible */
337 goto new_label;
340 } else {
341 new_label:
342 sym = label_push(&s1->asm_labels, label, 0);
343 /* If we need a symbol to hold a value, mark it as
344 tentative only (for .set). If this is for a real label
345 we'll remove VT_EXTERN. */
346 sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
348 sym->r = sh_num;
349 sym->jnext = value;
350 return sym;
353 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
355 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
358 /* Set the value of LABEL to that of some expression (possibly
359 involving other symbols). LABEL can be overwritten later still. */
360 static Sym* set_symbol(TCCState *s1, int label)
362 Sym *sym;
363 long n;
364 ExprValue e;
365 next();
366 asm_expr(s1, &e);
367 n = e.v;
368 if (e.sym)
369 n += e.sym->jnext;
370 return asm_new_label1(s1, label, 0, e.sym ? e.sym->r : SHN_ABS, n);
373 static void asm_free_labels(TCCState *st)
375 Sym *s, *s1;
376 Section *sec;
378 for(s = st->asm_labels; s != NULL; s = s1) {
379 s1 = s->prev;
380 /* define symbol value in object file */
381 s->type.t &= ~VT_EXTERN;
382 if (s->r) {
383 if (s->r == SHN_ABS)
384 sec = SECTION_ABS;
385 else
386 sec = st->sections[s->r];
387 put_extern_sym2(s, sec, s->jnext, 0, 0);
389 /* remove label */
390 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
391 sym_free(s);
393 st->asm_labels = NULL;
396 static void use_section1(TCCState *s1, Section *sec)
398 cur_text_section->data_offset = ind;
399 cur_text_section = sec;
400 ind = cur_text_section->data_offset;
403 static void use_section(TCCState *s1, const char *name)
405 Section *sec;
406 sec = find_section(s1, name);
407 use_section1(s1, sec);
410 static void push_section(TCCState *s1, const char *name)
412 Section *sec = find_section(s1, name);
413 sec->prev = cur_text_section;
414 use_section1(s1, sec);
417 static void pop_section(TCCState *s1)
419 Section *prev = cur_text_section->prev;
420 if (!prev)
421 tcc_error(".popsection without .pushsection");
422 cur_text_section->prev = NULL;
423 use_section1(s1, prev);
426 static void asm_parse_directive(TCCState *s1)
428 int n, offset, v, size, tok1;
429 Section *sec;
430 uint8_t *ptr;
432 /* assembler directive */
433 sec = cur_text_section;
434 switch(tok) {
435 case TOK_ASMDIR_align:
436 case TOK_ASMDIR_balign:
437 case TOK_ASMDIR_p2align:
438 case TOK_ASMDIR_skip:
439 case TOK_ASMDIR_space:
440 tok1 = tok;
441 next();
442 n = asm_int_expr(s1);
443 if (tok1 == TOK_ASMDIR_p2align)
445 if (n < 0 || n > 30)
446 tcc_error("invalid p2align, must be between 0 and 30");
447 n = 1 << n;
448 tok1 = TOK_ASMDIR_align;
450 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
451 if (n < 0 || (n & (n-1)) != 0)
452 tcc_error("alignment must be a positive power of two");
453 offset = (ind + n - 1) & -n;
454 size = offset - ind;
455 /* the section must have a compatible alignment */
456 if (sec->sh_addralign < n)
457 sec->sh_addralign = n;
458 } else {
459 if (n < 0)
460 n = 0;
461 size = n;
463 v = 0;
464 if (tok == ',') {
465 next();
466 v = asm_int_expr(s1);
468 zero_pad:
469 if (sec->sh_type != SHT_NOBITS) {
470 sec->data_offset = ind;
471 ptr = section_ptr_add(sec, size);
472 memset(ptr, v, size);
474 ind += size;
475 break;
476 case TOK_ASMDIR_quad:
477 #ifdef TCC_TARGET_X86_64
478 size = 8;
479 goto asm_data;
480 #else
481 next();
482 for(;;) {
483 uint64_t vl;
484 const char *p;
486 p = tokc.str.data;
487 if (tok != TOK_PPNUM) {
488 error_constant:
489 tcc_error("64 bit constant");
491 vl = strtoll(p, (char **)&p, 0);
492 if (*p != '\0')
493 goto error_constant;
494 next();
495 if (sec->sh_type != SHT_NOBITS) {
496 /* XXX: endianness */
497 gen_le32(vl);
498 gen_le32(vl >> 32);
499 } else {
500 ind += 8;
502 if (tok != ',')
503 break;
504 next();
506 break;
507 #endif
508 case TOK_ASMDIR_byte:
509 size = 1;
510 goto asm_data;
511 case TOK_ASMDIR_word:
512 case TOK_ASMDIR_short:
513 size = 2;
514 goto asm_data;
515 case TOK_ASMDIR_long:
516 case TOK_ASMDIR_int:
517 size = 4;
518 asm_data:
519 next();
520 for(;;) {
521 ExprValue e;
522 asm_expr(s1, &e);
523 if (sec->sh_type != SHT_NOBITS) {
524 if (size == 4) {
525 gen_expr32(&e);
526 #ifdef TCC_TARGET_X86_64
527 } else if (size == 8) {
528 gen_expr64(&e);
529 #endif
530 } else {
531 if (e.sym)
532 expect("constant");
533 if (size == 1)
534 g(e.v);
535 else
536 gen_le16(e.v);
538 } else {
539 ind += size;
541 if (tok != ',')
542 break;
543 next();
545 break;
546 case TOK_ASMDIR_fill:
548 int repeat, size, val, i, j;
549 uint8_t repeat_buf[8];
550 next();
551 repeat = asm_int_expr(s1);
552 if (repeat < 0) {
553 tcc_error("repeat < 0; .fill ignored");
554 break;
556 size = 1;
557 val = 0;
558 if (tok == ',') {
559 next();
560 size = asm_int_expr(s1);
561 if (size < 0) {
562 tcc_error("size < 0; .fill ignored");
563 break;
565 if (size > 8)
566 size = 8;
567 if (tok == ',') {
568 next();
569 val = asm_int_expr(s1);
572 /* XXX: endianness */
573 repeat_buf[0] = val;
574 repeat_buf[1] = val >> 8;
575 repeat_buf[2] = val >> 16;
576 repeat_buf[3] = val >> 24;
577 repeat_buf[4] = 0;
578 repeat_buf[5] = 0;
579 repeat_buf[6] = 0;
580 repeat_buf[7] = 0;
581 for(i = 0; i < repeat; i++) {
582 for(j = 0; j < size; j++) {
583 g(repeat_buf[j]);
587 break;
588 case TOK_ASMDIR_rept:
590 int repeat;
591 TokenString *init_str;
592 ParseState saved_parse_state = {0};
593 next();
594 repeat = asm_int_expr(s1);
595 init_str = tok_str_alloc();
596 next();
597 while ((tok != TOK_ASMDIR_endr) && (tok != CH_EOF)) {
598 tok_str_add_tok(init_str);
599 next();
601 if (tok == CH_EOF) tcc_error("we at end of file, .endr not found");
602 next();
603 tok_str_add(init_str, -1);
604 tok_str_add(init_str, 0);
605 save_parse_state(&saved_parse_state);
606 begin_macro(init_str, 1);
607 while (repeat-- > 0) {
608 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS));
609 macro_ptr = init_str->str;
611 end_macro();
612 restore_parse_state(&saved_parse_state);
613 break;
615 case TOK_ASMDIR_org:
617 unsigned long n;
618 ExprValue e;
619 next();
620 asm_expr(s1, &e);
621 n = e.v;
622 if (e.sym) {
623 if (e.sym->r != cur_text_section->sh_num)
624 expect("constant or same-section symbol");
625 n += e.sym->jnext;
627 if (n < ind)
628 tcc_error("attempt to .org backwards");
629 v = 0;
630 size = n - ind;
631 goto zero_pad;
633 break;
634 case TOK_ASMDIR_set:
635 next();
636 tok1 = tok;
637 next();
638 /* Also accept '.set stuff', but don't do anything with this.
639 It's used in GAS to set various features like '.set mips16'. */
640 if (tok == ',')
641 set_symbol(s1, tok1);
642 break;
643 case TOK_ASMDIR_globl:
644 case TOK_ASMDIR_global:
645 case TOK_ASMDIR_weak:
646 case TOK_ASMDIR_hidden:
647 tok1 = tok;
648 do {
649 Sym *sym;
651 next();
652 sym = label_find(tok);
653 if (!sym) {
654 sym = label_push(&s1->asm_labels, tok, 0);
655 sym->type.t = VT_VOID | VT_EXTERN;
657 if (tok1 != TOK_ASMDIR_hidden)
658 sym->type.t &= ~VT_STATIC;
659 if (tok1 == TOK_ASMDIR_weak)
660 sym->type.t |= VT_WEAK;
661 else if (tok1 == TOK_ASMDIR_hidden)
662 sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
663 next();
664 } while (tok == ',');
665 break;
666 case TOK_ASMDIR_string:
667 case TOK_ASMDIR_ascii:
668 case TOK_ASMDIR_asciz:
670 const uint8_t *p;
671 int i, size, t;
673 t = tok;
674 next();
675 for(;;) {
676 if (tok != TOK_STR)
677 expect("string constant");
678 p = tokc.str.data;
679 size = tokc.str.size;
680 if (t == TOK_ASMDIR_ascii && size > 0)
681 size--;
682 for(i = 0; i < size; i++)
683 g(p[i]);
684 next();
685 if (tok == ',') {
686 next();
687 } else if (tok != TOK_STR) {
688 break;
692 break;
693 case TOK_ASMDIR_text:
694 case TOK_ASMDIR_data:
695 case TOK_ASMDIR_bss:
697 char sname[64];
698 tok1 = tok;
699 n = 0;
700 next();
701 if (tok != ';' && tok != TOK_LINEFEED) {
702 n = asm_int_expr(s1);
703 next();
705 if (n)
706 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
707 else
708 sprintf(sname, "%s", get_tok_str(tok1, NULL));
709 use_section(s1, sname);
711 break;
712 case TOK_ASMDIR_file:
714 char filename[512];
716 filename[0] = '\0';
717 next();
719 if (tok == TOK_STR)
720 pstrcat(filename, sizeof(filename), tokc.str.data);
721 else
722 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
724 if (s1->warn_unsupported)
725 tcc_warning("ignoring .file %s", filename);
727 next();
729 break;
730 case TOK_ASMDIR_ident:
732 char ident[256];
734 ident[0] = '\0';
735 next();
737 if (tok == TOK_STR)
738 pstrcat(ident, sizeof(ident), tokc.str.data);
739 else
740 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
742 if (s1->warn_unsupported)
743 tcc_warning("ignoring .ident %s", ident);
745 next();
747 break;
748 case TOK_ASMDIR_size:
750 Sym *sym;
752 next();
753 sym = label_find(tok);
754 if (!sym) {
755 tcc_error("label not found: %s", get_tok_str(tok, NULL));
758 /* XXX .size name,label2-label1 */
759 if (s1->warn_unsupported)
760 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
762 next();
763 skip(',');
764 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
765 next();
768 break;
769 case TOK_ASMDIR_type:
771 Sym *sym;
772 const char *newtype;
774 next();
775 sym = label_find(tok);
776 if (!sym) {
777 sym = label_push(&s1->asm_labels, tok, 0);
778 sym->type.t = VT_VOID | VT_EXTERN;
781 next();
782 skip(',');
783 if (tok == TOK_STR) {
784 newtype = tokc.str.data;
785 } else {
786 if (tok == '@' || tok == '%')
787 next();
788 newtype = get_tok_str(tok, NULL);
791 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
792 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
794 else if (s1->warn_unsupported)
795 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
796 get_tok_str(sym->v, NULL), sym->type.t, newtype);
798 next();
800 break;
801 case TOK_ASMDIR_pushsection:
802 case TOK_ASMDIR_section:
804 char sname[256];
805 int old_nb_section = s1->nb_sections;
807 tok1 = tok;
808 /* XXX: support more options */
809 next();
810 sname[0] = '\0';
811 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
812 if (tok == TOK_STR)
813 pstrcat(sname, sizeof(sname), tokc.str.data);
814 else
815 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
816 next();
818 if (tok == ',') {
819 /* skip section options */
820 next();
821 if (tok != TOK_STR)
822 expect("string constant");
823 next();
824 if (tok == ',') {
825 next();
826 if (tok == '@' || tok == '%')
827 next();
828 next();
831 last_text_section = cur_text_section;
832 if (tok1 == TOK_ASMDIR_section)
833 use_section(s1, sname);
834 else
835 push_section(s1, sname);
836 /* If we just allocated a new section reset its alignment to
837 1. new_section normally acts for GCC compatibility and
838 sets alignment to PTR_SIZE. The assembler behaves different. */
839 if (old_nb_section != s1->nb_sections)
840 cur_text_section->sh_addralign = 1;
842 break;
843 case TOK_ASMDIR_previous:
845 Section *sec;
846 next();
847 if (!last_text_section)
848 tcc_error("no previous section referenced");
849 sec = cur_text_section;
850 use_section1(s1, last_text_section);
851 last_text_section = sec;
853 break;
854 case TOK_ASMDIR_popsection:
855 next();
856 pop_section(s1);
857 break;
858 #ifdef TCC_TARGET_I386
859 case TOK_ASMDIR_code16:
861 next();
862 s1->seg_size = 16;
864 break;
865 case TOK_ASMDIR_code32:
867 next();
868 s1->seg_size = 32;
870 break;
871 #endif
872 #ifdef TCC_TARGET_X86_64
873 /* added for compatibility with GAS */
874 case TOK_ASMDIR_code64:
875 next();
876 break;
877 #endif
878 default:
879 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
880 break;
885 /* assemble a file */
886 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
888 int opcode;
890 /* XXX: undefine C labels */
892 ch = file->buf_ptr[0];
893 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
894 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
895 if (do_preprocess)
896 parse_flags |= PARSE_FLAG_PREPROCESS;
897 next();
898 for(;;) {
899 if (tok == TOK_EOF)
900 break;
901 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
902 redo:
903 if (tok == '#') {
904 /* horrible gas comment */
905 while (tok != TOK_LINEFEED)
906 next();
907 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
908 asm_parse_directive(s1);
909 } else if (tok == TOK_PPNUM) {
910 Sym *sym;
911 const char *p;
912 int n;
913 p = tokc.str.data;
914 n = strtoul(p, (char **)&p, 10);
915 if (*p != '\0')
916 expect("':'");
917 /* new local label */
918 sym = asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
919 /* Remove the marker for tentative definitions. */
920 sym->type.t &= ~VT_EXTERN;
921 next();
922 skip(':');
923 goto redo;
924 } else if (tok >= TOK_IDENT) {
925 /* instruction or label */
926 opcode = tok;
927 next();
928 if (tok == ':') {
929 /* handle "extern void vide(void); __asm__("vide: ret");" as
930 "__asm__("globl vide\nvide: ret");" */
931 Sym *sym = sym_find(opcode);
932 if (sym && (sym->type.t & VT_EXTERN) && nocode_wanted) {
933 sym = label_find(opcode);
934 if (!sym) {
935 sym = label_push(&s1->asm_labels, opcode, 0);
936 sym->type.t = VT_VOID | VT_EXTERN;
939 /* new label */
940 sym = asm_new_label(s1, opcode, 0);
941 sym->type.t &= ~VT_EXTERN;
942 next();
943 goto redo;
944 } else if (tok == '=') {
945 set_symbol(s1, opcode);
946 goto redo;
947 } else {
948 asm_opcode(s1, opcode);
951 /* end of line */
952 if (tok != ';' && tok != TOK_LINEFEED){
953 expect("end of line");
955 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
956 next();
959 asm_free_labels(s1);
961 return 0;
964 /* Assemble the current file */
965 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
967 Sym *define_start;
968 int ret;
970 preprocess_start(s1);
972 /* default section is text */
973 cur_text_section = text_section;
974 ind = cur_text_section->data_offset;
976 define_start = define_stack;
978 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
979 symbols can be safely used */
980 put_elf_sym(symtab_section, 0, 0,
981 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
982 SHN_ABS, file->filename);
984 ret = tcc_assemble_internal(s1, do_preprocess);
986 cur_text_section->data_offset = ind;
988 free_defines(define_start);
990 return ret;
993 /********************************************************************/
994 /* GCC inline asm support */
996 /* assemble the string 'str' in the current C compilation unit without
997 C preprocessing. NOTE: str is modified by modifying the '\0' at the
998 end */
999 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
1001 int saved_parse_flags;
1002 const int *saved_macro_ptr;
1004 saved_parse_flags = parse_flags;
1005 saved_macro_ptr = macro_ptr;
1007 tcc_open_bf(s1, ":asm:", len);
1008 memcpy(file->buffer, str, len);
1010 macro_ptr = NULL;
1011 tcc_assemble_internal(s1, 0);
1012 tcc_close();
1014 parse_flags = saved_parse_flags;
1015 macro_ptr = saved_macro_ptr;
1018 /* find a constraint by its number or id (gcc 3 extended
1019 syntax). return -1 if not found. Return in *pp in char after the
1020 constraint */
1021 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1022 const char *name, const char **pp)
1024 int index;
1025 TokenSym *ts;
1026 const char *p;
1028 if (isnum(*name)) {
1029 index = 0;
1030 while (isnum(*name)) {
1031 index = (index * 10) + (*name) - '0';
1032 name++;
1034 if ((unsigned)index >= nb_operands)
1035 index = -1;
1036 } else if (*name == '[') {
1037 name++;
1038 p = strchr(name, ']');
1039 if (p) {
1040 ts = tok_alloc(name, p - name);
1041 for(index = 0; index < nb_operands; index++) {
1042 if (operands[index].id == ts->tok)
1043 goto found;
1045 index = -1;
1046 found:
1047 name = p + 1;
1048 } else {
1049 index = -1;
1051 } else {
1052 index = -1;
1054 if (pp)
1055 *pp = name;
1056 return index;
1059 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1060 int nb_outputs,
1061 CString *out_str, CString *in_str)
1063 int c, index, modifier;
1064 const char *str;
1065 ASMOperand *op;
1066 SValue sv;
1068 cstr_new(out_str);
1069 str = in_str->data;
1070 for(;;) {
1071 c = *str++;
1072 if (c == '%') {
1073 if (*str == '%') {
1074 str++;
1075 goto add_char;
1077 modifier = 0;
1078 if (*str == 'c' || *str == 'n' ||
1079 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1080 *str == 'q' ||
1081 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1082 and make literal operands not be decorated with '$'. */
1083 *str == 'P')
1084 modifier = *str++;
1085 index = find_constraint(operands, nb_operands, str, &str);
1086 if (index < 0)
1087 tcc_error("invalid operand reference after %%");
1088 op = &operands[index];
1089 sv = *op->vt;
1090 if (op->reg >= 0) {
1091 sv.r = op->reg;
1092 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1093 sv.r |= VT_LVAL;
1095 subst_asm_operand(out_str, &sv, modifier);
1096 } else {
1097 add_char:
1098 cstr_ccat(out_str, c);
1099 if (c == '\0')
1100 break;
1106 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1107 int is_output)
1109 ASMOperand *op;
1110 int nb_operands;
1112 if (tok != ':') {
1113 nb_operands = *nb_operands_ptr;
1114 for(;;) {
1115 CString astr;
1116 if (nb_operands >= MAX_ASM_OPERANDS)
1117 tcc_error("too many asm operands");
1118 op = &operands[nb_operands++];
1119 op->id = 0;
1120 if (tok == '[') {
1121 next();
1122 if (tok < TOK_IDENT)
1123 expect("identifier");
1124 op->id = tok;
1125 next();
1126 skip(']');
1128 parse_mult_str(&astr, "string constant");
1129 op->constraint = tcc_malloc(astr.size);
1130 strcpy(op->constraint, astr.data);
1131 cstr_free(&astr);
1132 skip('(');
1133 gexpr();
1134 if (is_output) {
1135 if (!(vtop->type.t & VT_ARRAY))
1136 test_lvalue();
1137 } else {
1138 /* we want to avoid LLOCAL case, except when the 'm'
1139 constraint is used. Note that it may come from
1140 register storage, so we need to convert (reg)
1141 case */
1142 if ((vtop->r & VT_LVAL) &&
1143 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1144 (vtop->r & VT_VALMASK) < VT_CONST) &&
1145 !strchr(op->constraint, 'm')) {
1146 gv(RC_INT);
1149 op->vt = vtop;
1150 skip(')');
1151 if (tok == ',') {
1152 next();
1153 } else {
1154 break;
1157 *nb_operands_ptr = nb_operands;
1161 /* parse the GCC asm() instruction */
1162 ST_FUNC void asm_instr(void)
1164 CString astr, astr1;
1165 ASMOperand operands[MAX_ASM_OPERANDS];
1166 int nb_outputs, nb_operands, i, must_subst, out_reg;
1167 uint8_t clobber_regs[NB_ASM_REGS];
1169 next();
1170 /* since we always generate the asm() instruction, we can ignore
1171 volatile */
1172 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1173 next();
1175 parse_asm_str(&astr);
1176 nb_operands = 0;
1177 nb_outputs = 0;
1178 must_subst = 0;
1179 memset(clobber_regs, 0, sizeof(clobber_regs));
1180 if (tok == ':') {
1181 next();
1182 must_subst = 1;
1183 /* output args */
1184 parse_asm_operands(operands, &nb_operands, 1);
1185 nb_outputs = nb_operands;
1186 if (tok == ':') {
1187 next();
1188 if (tok != ')') {
1189 /* input args */
1190 parse_asm_operands(operands, &nb_operands, 0);
1191 if (tok == ':') {
1192 /* clobber list */
1193 /* XXX: handle registers */
1194 next();
1195 for(;;) {
1196 if (tok != TOK_STR)
1197 expect("string constant");
1198 asm_clobber(clobber_regs, tokc.str.data);
1199 next();
1200 if (tok == ',') {
1201 next();
1202 } else {
1203 break;
1210 skip(')');
1211 /* NOTE: we do not eat the ';' so that we can restore the current
1212 token after the assembler parsing */
1213 if (tok != ';')
1214 expect("';'");
1216 /* save all values in the memory */
1217 save_regs(0);
1219 /* compute constraints */
1220 asm_compute_constraints(operands, nb_operands, nb_outputs,
1221 clobber_regs, &out_reg);
1223 /* substitute the operands in the asm string. No substitution is
1224 done if no operands (GCC behaviour) */
1225 #ifdef ASM_DEBUG
1226 printf("asm: \"%s\"\n", (char *)astr.data);
1227 #endif
1228 if (must_subst) {
1229 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1230 cstr_free(&astr);
1231 } else {
1232 astr1 = astr;
1234 #ifdef ASM_DEBUG
1235 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1236 #endif
1238 /* generate loads */
1239 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1240 clobber_regs, out_reg);
1242 /* assemble the string with tcc internal assembler */
1243 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1245 /* restore the current C token */
1246 next();
1248 /* store the output values if needed */
1249 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1250 clobber_regs, out_reg);
1252 /* free everything */
1253 for(i=0;i<nb_operands;i++) {
1254 ASMOperand *op;
1255 op = &operands[i];
1256 tcc_free(op->constraint);
1257 vpop();
1259 cstr_free(&astr1);
1262 ST_FUNC void asm_global_instr(void)
1264 CString astr;
1266 next();
1267 parse_asm_str(&astr);
1268 skip(')');
1269 /* NOTE: we do not eat the ';' so that we can restore the current
1270 token after the assembler parsing */
1271 if (tok != ';')
1272 expect("';'");
1274 #ifdef ASM_DEBUG
1275 printf("asm_global: \"%s\"\n", (char *)astr.data);
1276 #endif
1277 cur_text_section = text_section;
1278 ind = cur_text_section->data_offset;
1280 /* assemble the string with tcc internal assembler */
1281 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1283 cur_text_section->data_offset = ind;
1285 /* restore the current C token */
1286 next();
1288 cstr_free(&astr);
1290 #endif /* CONFIG_TCC_ASM */