riscv: Handle JUMP_SLOT reloc
[tinycc.git] / tccasm.c
bloba8412f4785546ad0783ecf2b919ac0308de77f93
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 && !(sym->type.t & VT_STATIC))
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 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
916 redo:
917 if (tok == '#') {
918 /* horrible gas comment */
919 while (tok != TOK_LINEFEED)
920 next();
921 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
922 asm_parse_directive(s1, global);
923 } else if (tok == TOK_PPNUM) {
924 const char *p;
925 int n;
926 p = tokc.str.data;
927 n = strtoul(p, (char **)&p, 10);
928 if (*p != '\0')
929 expect("':'");
930 /* new local label */
931 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
932 next();
933 skip(':');
934 goto redo;
935 } else if (tok >= TOK_IDENT) {
936 /* instruction or label */
937 opcode = tok;
938 next();
939 if (tok == ':') {
940 /* new label */
941 asm_new_label(s1, opcode, 0);
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");
954 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
957 parse_flags = saved_parse_flags;
958 return 0;
961 /* Assemble the current file */
962 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
964 int ret;
965 tcc_debug_start(s1);
966 /* default section is text */
967 cur_text_section = text_section;
968 ind = cur_text_section->data_offset;
969 nocode_wanted = 0;
970 ret = tcc_assemble_internal(s1, do_preprocess, 1);
971 cur_text_section->data_offset = ind;
972 tcc_debug_end(s1);
973 return ret;
976 /********************************************************************/
977 /* GCC inline asm support */
979 /* assemble the string 'str' in the current C compilation unit without
980 C preprocessing. NOTE: str is modified by modifying the '\0' at the
981 end */
982 static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
984 const int *saved_macro_ptr = macro_ptr;
985 int dotid = set_idnum('.', IS_ID);
987 tcc_open_bf(s1, ":asm:", len);
988 memcpy(file->buffer, str, len);
989 macro_ptr = NULL;
990 tcc_assemble_internal(s1, 0, global);
991 tcc_close();
993 set_idnum('.', dotid);
994 macro_ptr = saved_macro_ptr;
997 /* find a constraint by its number or id (gcc 3 extended
998 syntax). return -1 if not found. Return in *pp in char after the
999 constraint */
1000 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1001 const char *name, const char **pp)
1003 int index;
1004 TokenSym *ts;
1005 const char *p;
1007 if (isnum(*name)) {
1008 index = 0;
1009 while (isnum(*name)) {
1010 index = (index * 10) + (*name) - '0';
1011 name++;
1013 if ((unsigned)index >= nb_operands)
1014 index = -1;
1015 } else if (*name == '[') {
1016 name++;
1017 p = strchr(name, ']');
1018 if (p) {
1019 ts = tok_alloc(name, p - name);
1020 for(index = 0; index < nb_operands; index++) {
1021 if (operands[index].id == ts->tok)
1022 goto found;
1024 index = -1;
1025 found:
1026 name = p + 1;
1027 } else {
1028 index = -1;
1030 } else {
1031 index = -1;
1033 if (pp)
1034 *pp = name;
1035 return index;
1038 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1039 CString *out_str, CString *in_str)
1041 int c, index, modifier;
1042 const char *str;
1043 ASMOperand *op;
1044 SValue sv;
1046 cstr_new(out_str);
1047 str = in_str->data;
1048 for(;;) {
1049 c = *str++;
1050 if (c == '%') {
1051 if (*str == '%') {
1052 str++;
1053 goto add_char;
1055 modifier = 0;
1056 if (*str == 'c' || *str == 'n' ||
1057 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1058 *str == 'q' ||
1059 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1060 and make literal operands not be decorated with '$'. */
1061 *str == 'P')
1062 modifier = *str++;
1063 index = find_constraint(operands, nb_operands, str, &str);
1064 if (index < 0)
1065 tcc_error("invalid operand reference after %%");
1066 op = &operands[index];
1067 sv = *op->vt;
1068 if (op->reg >= 0) {
1069 sv.r = op->reg;
1070 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1071 sv.r |= VT_LVAL;
1073 subst_asm_operand(out_str, &sv, modifier);
1074 } else {
1075 add_char:
1076 cstr_ccat(out_str, c);
1077 if (c == '\0')
1078 break;
1084 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1085 int is_output)
1087 ASMOperand *op;
1088 int nb_operands;
1090 if (tok != ':') {
1091 nb_operands = *nb_operands_ptr;
1092 for(;;) {
1093 CString astr;
1094 if (nb_operands >= MAX_ASM_OPERANDS)
1095 tcc_error("too many asm operands");
1096 op = &operands[nb_operands++];
1097 op->id = 0;
1098 if (tok == '[') {
1099 next();
1100 if (tok < TOK_IDENT)
1101 expect("identifier");
1102 op->id = tok;
1103 next();
1104 skip(']');
1106 parse_mult_str(&astr, "string constant");
1107 op->constraint = tcc_malloc(astr.size);
1108 strcpy(op->constraint, astr.data);
1109 cstr_free(&astr);
1110 skip('(');
1111 gexpr();
1112 if (is_output) {
1113 if (!(vtop->type.t & VT_ARRAY))
1114 test_lvalue();
1115 } else {
1116 /* we want to avoid LLOCAL case, except when the 'm'
1117 constraint is used. Note that it may come from
1118 register storage, so we need to convert (reg)
1119 case */
1120 if ((vtop->r & VT_LVAL) &&
1121 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1122 (vtop->r & VT_VALMASK) < VT_CONST) &&
1123 !strchr(op->constraint, 'm')) {
1124 gv(RC_INT);
1127 op->vt = vtop;
1128 skip(')');
1129 if (tok == ',') {
1130 next();
1131 } else {
1132 break;
1135 *nb_operands_ptr = nb_operands;
1139 /* parse the GCC asm() instruction */
1140 ST_FUNC void asm_instr(void)
1142 CString astr, astr1;
1143 ASMOperand operands[MAX_ASM_OPERANDS];
1144 int nb_outputs, nb_operands, i, must_subst, out_reg;
1145 uint8_t clobber_regs[NB_ASM_REGS];
1146 Section *sec;
1148 /* since we always generate the asm() instruction, we can ignore
1149 volatile */
1150 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1151 next();
1153 parse_asm_str(&astr);
1154 nb_operands = 0;
1155 nb_outputs = 0;
1156 must_subst = 0;
1157 memset(clobber_regs, 0, sizeof(clobber_regs));
1158 if (tok == ':') {
1159 next();
1160 must_subst = 1;
1161 /* output args */
1162 parse_asm_operands(operands, &nb_operands, 1);
1163 nb_outputs = nb_operands;
1164 if (tok == ':') {
1165 next();
1166 if (tok != ')') {
1167 /* input args */
1168 parse_asm_operands(operands, &nb_operands, 0);
1169 if (tok == ':') {
1170 /* clobber list */
1171 /* XXX: handle registers */
1172 next();
1173 for(;;) {
1174 if (tok != TOK_STR)
1175 expect("string constant");
1176 asm_clobber(clobber_regs, tokc.str.data);
1177 next();
1178 if (tok == ',') {
1179 next();
1180 } else {
1181 break;
1188 skip(')');
1189 /* NOTE: we do not eat the ';' so that we can restore the current
1190 token after the assembler parsing */
1191 if (tok != ';')
1192 expect("';'");
1194 /* save all values in the memory */
1195 save_regs(0);
1197 /* compute constraints */
1198 asm_compute_constraints(operands, nb_operands, nb_outputs,
1199 clobber_regs, &out_reg);
1201 /* substitute the operands in the asm string. No substitution is
1202 done if no operands (GCC behaviour) */
1203 #ifdef ASM_DEBUG
1204 printf("asm: \"%s\"\n", (char *)astr.data);
1205 #endif
1206 if (must_subst) {
1207 subst_asm_operands(operands, nb_operands, &astr1, &astr);
1208 cstr_free(&astr);
1209 } else {
1210 astr1 = astr;
1212 #ifdef ASM_DEBUG
1213 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1214 #endif
1216 /* generate loads */
1217 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1218 clobber_regs, out_reg);
1220 /* We don't allow switching section within inline asm to
1221 bleed out to surrounding code. */
1222 sec = cur_text_section;
1223 /* assemble the string with tcc internal assembler */
1224 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1225 if (sec != cur_text_section) {
1226 tcc_warning("inline asm tries to change current section");
1227 use_section1(tcc_state, sec);
1230 /* restore the current C token */
1231 next();
1233 /* store the output values if needed */
1234 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1235 clobber_regs, out_reg);
1237 /* free everything */
1238 for(i=0;i<nb_operands;i++) {
1239 ASMOperand *op;
1240 op = &operands[i];
1241 tcc_free(op->constraint);
1242 vpop();
1244 cstr_free(&astr1);
1247 ST_FUNC void asm_global_instr(void)
1249 CString astr;
1250 int saved_nocode_wanted = nocode_wanted;
1252 /* Global asm blocks are always emitted. */
1253 nocode_wanted = 0;
1254 next();
1255 parse_asm_str(&astr);
1256 skip(')');
1257 /* NOTE: we do not eat the ';' so that we can restore the current
1258 token after the assembler parsing */
1259 if (tok != ';')
1260 expect("';'");
1262 #ifdef ASM_DEBUG
1263 printf("asm_global: \"%s\"\n", (char *)astr.data);
1264 #endif
1265 cur_text_section = text_section;
1266 ind = cur_text_section->data_offset;
1268 /* assemble the string with tcc internal assembler */
1269 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1271 cur_text_section->data_offset = ind;
1273 /* restore the current C token */
1274 next();
1276 cstr_free(&astr);
1277 nocode_wanted = saved_nocode_wanted;
1279 #endif /* CONFIG_TCC_ASM */