tccpe: #pragma pack(push) - support this form
[tinycc.git] / tccasm.c
blob2dd8921b1533c6c9b56b704c1c4abd9c2382281b
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 snprintf(buf, sizeof(buf), "L..%u", n);
31 return tok_alloc_const(buf);
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 /* If a C name has an _ prepended then only asm labels that start
39 with _ are representable in C, by removing the first _. ASM names
40 without _ at the beginning don't correspond to C names, but we use
41 the global C symbol table to track ASM names as well, so we need to
42 transform those into ones that don't conflict with a C name,
43 so prepend a '.' for them, but force the ELF asm name to be set. */
44 static int asm2cname(int v, int *addeddot)
46 const char *name;
47 *addeddot = 0;
48 if (!tcc_state->leading_underscore)
49 return v;
50 name = get_tok_str(v, NULL);
51 if (!name)
52 return v;
53 if (name[0] == '_') {
54 v = tok_alloc_const(name + 1);
55 } else if (!strchr(name, '.')) {
56 char newname[256];
57 snprintf(newname, sizeof newname, ".%s", name);
58 v = tok_alloc_const(newname);
59 *addeddot = 1;
61 return v;
64 static Sym *asm_label_find(int v)
66 Sym *sym;
67 int addeddot;
68 v = asm2cname(v, &addeddot);
69 sym = sym_find(v);
70 while (sym && sym->sym_scope && !(sym->type.t & VT_STATIC))
71 sym = sym->prev_tok;
72 return sym;
75 static Sym *asm_label_push(int v)
77 int addeddot, v2 = asm2cname(v, &addeddot);
78 /* We always add VT_EXTERN, for sym definition that's tentative
79 (for .set, removed for real defs), for mere references it's correct
80 as is. */
81 Sym *sym = global_identifier_push(v2, VT_ASM | VT_EXTERN | VT_STATIC, 0);
82 if (addeddot)
83 sym->asm_label = v;
84 return sym;
87 /* Return a symbol we can use inside the assembler, having name NAME.
88 Symbols from asm and C source share a namespace. If we generate
89 an asm symbol it's also a (file-global) C symbol, but it's
90 either not accessible by name (like "L.123"), or its type information
91 is such that it's not usable without a proper C declaration.
93 Sometimes we need symbols accessible by name from asm, which
94 are anonymous in C, in this case CSYM can be used to transfer
95 all information from that symbol to the (possibly newly created)
96 asm symbol. */
97 ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
99 Sym *sym = asm_label_find(name);
100 if (!sym) {
101 sym = asm_label_push(name);
102 if (csym)
103 sym->c = csym->c;
105 return sym;
108 static Sym* asm_section_sym(TCCState *s1, Section *sec)
110 char buf[100]; int label; Sym *sym;
111 snprintf(buf, sizeof buf, "L.%s", sec->name);
112 label = tok_alloc_const(buf);
113 sym = asm_label_find(label);
114 return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0);
117 /* We do not use the C expression parser to handle symbols. Maybe the
118 C expression parser could be tweaked to do so. */
120 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
122 Sym *sym;
123 int op, label;
124 uint64_t n;
125 const char *p;
127 switch(tok) {
128 case TOK_PPNUM:
129 p = tokc.str.data;
130 n = strtoull(p, (char **)&p, 0);
131 if (*p == 'b' || *p == 'f') {
132 /* backward or forward label */
133 label = asm_get_local_label_name(s1, n);
134 sym = asm_label_find(label);
135 if (*p == 'b') {
136 /* backward : find the last corresponding defined label */
137 if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))
138 sym = sym->prev_tok;
139 if (!sym)
140 tcc_error("local label '%d' not found backward", (int)n);
141 } else {
142 /* forward */
143 if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) {
144 /* if the last label is defined, then define a new one */
145 sym = asm_label_push(label);
148 pe->v = 0;
149 pe->sym = sym;
150 pe->pcrel = 0;
151 } else if (*p == '\0') {
152 pe->v = n;
153 pe->sym = NULL;
154 pe->pcrel = 0;
155 } else {
156 tcc_error("invalid number syntax");
158 next();
159 break;
160 case '+':
161 next();
162 asm_expr_unary(s1, pe);
163 break;
164 case '-':
165 case '~':
166 op = tok;
167 next();
168 asm_expr_unary(s1, pe);
169 if (pe->sym)
170 tcc_error("invalid operation with label");
171 if (op == '-')
172 pe->v = -pe->v;
173 else
174 pe->v = ~pe->v;
175 break;
176 case TOK_CCHAR:
177 case TOK_LCHAR:
178 pe->v = tokc.i;
179 pe->sym = NULL;
180 pe->pcrel = 0;
181 next();
182 break;
183 case '(':
184 next();
185 asm_expr(s1, pe);
186 skip(')');
187 break;
188 case '.':
189 pe->v = ind;
190 pe->sym = asm_section_sym(s1, cur_text_section);
191 pe->pcrel = 0;
192 next();
193 break;
194 default:
195 if (tok >= TOK_IDENT) {
196 ElfSym *esym;
197 /* label case : if the label was not found, add one */
198 sym = get_asm_sym(tok, NULL);
199 esym = elfsym(sym);
200 if (esym && esym->st_shndx == SHN_ABS) {
201 /* if absolute symbol, no need to put a symbol value */
202 pe->v = esym->st_value;
203 pe->sym = NULL;
204 pe->pcrel = 0;
205 } else {
206 pe->v = 0;
207 pe->sym = sym;
208 pe->pcrel = 0;
210 next();
211 } else {
212 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
214 break;
218 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
220 int op;
221 ExprValue e2;
223 asm_expr_unary(s1, pe);
224 for(;;) {
225 op = tok;
226 if (op != '*' && op != '/' && op != '%' &&
227 op != TOK_SHL && op != TOK_SAR)
228 break;
229 next();
230 asm_expr_unary(s1, &e2);
231 if (pe->sym || e2.sym)
232 tcc_error("invalid operation with label");
233 switch(op) {
234 case '*':
235 pe->v *= e2.v;
236 break;
237 case '/':
238 if (e2.v == 0) {
239 div_error:
240 tcc_error("division by zero");
242 pe->v /= e2.v;
243 break;
244 case '%':
245 if (e2.v == 0)
246 goto div_error;
247 pe->v %= e2.v;
248 break;
249 case TOK_SHL:
250 pe->v <<= e2.v;
251 break;
252 default:
253 case TOK_SAR:
254 pe->v >>= e2.v;
255 break;
260 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
262 int op;
263 ExprValue e2;
265 asm_expr_prod(s1, pe);
266 for(;;) {
267 op = tok;
268 if (op != '&' && op != '|' && op != '^')
269 break;
270 next();
271 asm_expr_prod(s1, &e2);
272 if (pe->sym || e2.sym)
273 tcc_error("invalid operation with label");
274 switch(op) {
275 case '&':
276 pe->v &= e2.v;
277 break;
278 case '|':
279 pe->v |= e2.v;
280 break;
281 default:
282 case '^':
283 pe->v ^= e2.v;
284 break;
289 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
291 int op;
292 ExprValue e2;
294 asm_expr_logic(s1, pe);
295 for(;;) {
296 op = tok;
297 if (op != '+' && op != '-')
298 break;
299 next();
300 asm_expr_logic(s1, &e2);
301 if (op == '+') {
302 if (pe->sym != NULL && e2.sym != NULL)
303 goto cannot_relocate;
304 pe->v += e2.v;
305 if (pe->sym == NULL && e2.sym != NULL)
306 pe->sym = e2.sym;
307 } else {
308 pe->v -= e2.v;
309 /* NOTE: we are less powerful than gas in that case
310 because we store only one symbol in the expression */
311 if (!e2.sym) {
312 /* OK */
313 } else if (pe->sym == e2.sym) {
314 /* OK */
315 pe->sym = NULL; /* same symbols can be subtracted to NULL */
316 } else {
317 ElfSym *esym1, *esym2;
318 esym1 = elfsym(pe->sym);
319 esym2 = elfsym(e2.sym);
320 if (esym1 && esym1->st_shndx == esym2->st_shndx
321 && esym1->st_shndx != SHN_UNDEF) {
322 /* we also accept defined symbols in the same section */
323 pe->v += esym1->st_value - esym2->st_value;
324 pe->sym = NULL;
325 } else if (esym2->st_shndx == cur_text_section->sh_num) {
326 /* When subtracting a defined symbol in current section
327 this actually makes the value PC-relative. */
328 pe->v -= esym2->st_value - ind - 4;
329 pe->pcrel = 1;
330 e2.sym = NULL;
331 } else {
332 cannot_relocate:
333 tcc_error("invalid operation with label");
340 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
342 int op;
343 ExprValue e2;
345 asm_expr_sum(s1, pe);
346 for(;;) {
347 op = tok;
348 if (op != TOK_EQ && op != TOK_NE
349 && (op > TOK_GT || op < TOK_ULE))
350 break;
351 next();
352 asm_expr_sum(s1, &e2);
353 if (pe->sym || e2.sym)
354 tcc_error("invalid operation with label");
355 switch(op) {
356 case TOK_EQ:
357 pe->v = pe->v == e2.v;
358 break;
359 case TOK_NE:
360 pe->v = pe->v != e2.v;
361 break;
362 case TOK_LT:
363 pe->v = (int64_t)pe->v < (int64_t)e2.v;
364 break;
365 case TOK_GE:
366 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
367 break;
368 case TOK_LE:
369 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
370 break;
371 case TOK_GT:
372 pe->v = (int64_t)pe->v > (int64_t)e2.v;
373 break;
374 default:
375 break;
377 /* GAS compare results are -1/0 not 1/0. */
378 pe->v = -(int64_t)pe->v;
382 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
384 asm_expr_cmp(s1, pe);
387 ST_FUNC int asm_int_expr(TCCState *s1)
389 ExprValue e;
390 asm_expr(s1, &e);
391 if (e.sym)
392 expect("constant");
393 return e.v;
396 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
397 int sh_num, int value)
399 Sym *sym;
400 ElfSym *esym;
402 sym = asm_label_find(label);
403 if (sym) {
404 esym = elfsym(sym);
405 /* A VT_EXTERN symbol, even if it has a section is considered
406 overridable. This is how we "define" .set targets. Real
407 definitions won't have VT_EXTERN set. */
408 if (esym && esym->st_shndx != SHN_UNDEF) {
409 /* the label is already defined */
410 if (IS_ASM_SYM(sym)
411 && (is_local == 1 || (sym->type.t & VT_EXTERN)))
412 goto new_label;
413 if (!(sym->type.t & VT_EXTERN))
414 tcc_error("assembler label '%s' already defined",
415 get_tok_str(label, NULL));
417 } else {
418 new_label:
419 sym = asm_label_push(label);
421 if (!sym->c)
422 put_extern_sym2(sym, SHN_UNDEF, 0, 0, 1);
423 esym = elfsym(sym);
424 esym->st_shndx = sh_num;
425 esym->st_value = value;
426 if (is_local != 2)
427 sym->type.t &= ~VT_EXTERN;
428 return sym;
431 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
433 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
436 /* Set the value of LABEL to that of some expression (possibly
437 involving other symbols). LABEL can be overwritten later still. */
438 static Sym* set_symbol(TCCState *s1, int label)
440 long n;
441 ExprValue e;
442 Sym *sym;
443 ElfSym *esym;
444 next();
445 asm_expr(s1, &e);
446 n = e.v;
447 esym = elfsym(e.sym);
448 if (esym)
449 n += esym->st_value;
450 sym = asm_new_label1(s1, label, 2, esym ? esym->st_shndx : SHN_ABS, n);
451 elfsym(sym)->st_other |= ST_ASM_SET;
452 return sym;
455 static void use_section1(TCCState *s1, Section *sec)
457 cur_text_section->data_offset = ind;
458 cur_text_section = sec;
459 ind = cur_text_section->data_offset;
462 static void use_section(TCCState *s1, const char *name)
464 Section *sec;
465 sec = find_section(s1, name);
466 use_section1(s1, sec);
469 static void push_section(TCCState *s1, const char *name)
471 Section *sec = find_section(s1, name);
472 sec->prev = cur_text_section;
473 use_section1(s1, sec);
476 static void pop_section(TCCState *s1)
478 Section *prev = cur_text_section->prev;
479 if (!prev)
480 tcc_error(".popsection without .pushsection");
481 cur_text_section->prev = NULL;
482 use_section1(s1, prev);
485 static void asm_parse_directive(TCCState *s1, int global)
487 int n, offset, v, size, tok1;
488 Section *sec;
489 uint8_t *ptr;
491 /* assembler directive */
492 sec = cur_text_section;
493 switch(tok) {
494 case TOK_ASMDIR_align:
495 case TOK_ASMDIR_balign:
496 case TOK_ASMDIR_p2align:
497 case TOK_ASMDIR_skip:
498 case TOK_ASMDIR_space:
499 tok1 = tok;
500 next();
501 n = asm_int_expr(s1);
502 if (tok1 == TOK_ASMDIR_p2align)
504 if (n < 0 || n > 30)
505 tcc_error("invalid p2align, must be between 0 and 30");
506 n = 1 << n;
507 tok1 = TOK_ASMDIR_align;
509 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
510 if (n < 0 || (n & (n-1)) != 0)
511 tcc_error("alignment must be a positive power of two");
512 offset = (ind + n - 1) & -n;
513 size = offset - ind;
514 /* the section must have a compatible alignment */
515 if (sec->sh_addralign < n)
516 sec->sh_addralign = n;
517 } else {
518 if (n < 0)
519 n = 0;
520 size = n;
522 v = 0;
523 if (tok == ',') {
524 next();
525 v = asm_int_expr(s1);
527 zero_pad:
528 if (sec->sh_type != SHT_NOBITS) {
529 sec->data_offset = ind;
530 ptr = section_ptr_add(sec, size);
531 memset(ptr, v, size);
533 ind += size;
534 break;
535 case TOK_ASMDIR_quad:
536 #ifdef TCC_TARGET_X86_64
537 size = 8;
538 goto asm_data;
539 #else
540 next();
541 for(;;) {
542 uint64_t vl;
543 const char *p;
545 p = tokc.str.data;
546 if (tok != TOK_PPNUM) {
547 error_constant:
548 tcc_error("64 bit constant");
550 vl = strtoll(p, (char **)&p, 0);
551 if (*p != '\0')
552 goto error_constant;
553 next();
554 if (sec->sh_type != SHT_NOBITS) {
555 /* XXX: endianness */
556 gen_le32(vl);
557 gen_le32(vl >> 32);
558 } else {
559 ind += 8;
561 if (tok != ',')
562 break;
563 next();
565 break;
566 #endif
567 case TOK_ASMDIR_byte:
568 size = 1;
569 goto asm_data;
570 case TOK_ASMDIR_word:
571 case TOK_ASMDIR_short:
572 size = 2;
573 goto asm_data;
574 case TOK_ASMDIR_long:
575 case TOK_ASMDIR_int:
576 size = 4;
577 asm_data:
578 next();
579 for(;;) {
580 ExprValue e;
581 asm_expr(s1, &e);
582 if (sec->sh_type != SHT_NOBITS) {
583 if (size == 4) {
584 gen_expr32(&e);
585 #ifdef TCC_TARGET_X86_64
586 } else if (size == 8) {
587 gen_expr64(&e);
588 #endif
589 } else {
590 if (e.sym)
591 expect("constant");
592 if (size == 1)
593 g(e.v);
594 else
595 gen_le16(e.v);
597 } else {
598 ind += size;
600 if (tok != ',')
601 break;
602 next();
604 break;
605 case TOK_ASMDIR_fill:
607 int repeat, size, val, i, j;
608 uint8_t repeat_buf[8];
609 next();
610 repeat = asm_int_expr(s1);
611 if (repeat < 0) {
612 tcc_error("repeat < 0; .fill ignored");
613 break;
615 size = 1;
616 val = 0;
617 if (tok == ',') {
618 next();
619 size = asm_int_expr(s1);
620 if (size < 0) {
621 tcc_error("size < 0; .fill ignored");
622 break;
624 if (size > 8)
625 size = 8;
626 if (tok == ',') {
627 next();
628 val = asm_int_expr(s1);
631 /* XXX: endianness */
632 repeat_buf[0] = val;
633 repeat_buf[1] = val >> 8;
634 repeat_buf[2] = val >> 16;
635 repeat_buf[3] = val >> 24;
636 repeat_buf[4] = 0;
637 repeat_buf[5] = 0;
638 repeat_buf[6] = 0;
639 repeat_buf[7] = 0;
640 for(i = 0; i < repeat; i++) {
641 for(j = 0; j < size; j++) {
642 g(repeat_buf[j]);
646 break;
647 case TOK_ASMDIR_rept:
649 int repeat;
650 TokenString *init_str;
651 next();
652 repeat = asm_int_expr(s1);
653 init_str = tok_str_alloc();
654 while (next(), tok != TOK_ASMDIR_endr) {
655 if (tok == CH_EOF)
656 tcc_error("we at end of file, .endr not found");
657 tok_str_add_tok(init_str);
659 tok_str_add(init_str, -1);
660 tok_str_add(init_str, 0);
661 begin_macro(init_str, 1);
662 while (repeat-- > 0) {
663 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
664 global);
665 macro_ptr = init_str->str;
667 end_macro();
668 next();
669 break;
671 case TOK_ASMDIR_org:
673 unsigned long n;
674 ExprValue e;
675 ElfSym *esym;
676 next();
677 asm_expr(s1, &e);
678 n = e.v;
679 esym = elfsym(e.sym);
680 if (esym) {
681 if (esym->st_shndx != cur_text_section->sh_num)
682 expect("constant or same-section symbol");
683 n += esym->st_value;
685 if (n < ind)
686 tcc_error("attempt to .org backwards");
687 v = 0;
688 size = n - ind;
689 goto zero_pad;
691 break;
692 case TOK_ASMDIR_set:
693 next();
694 tok1 = tok;
695 next();
696 /* Also accept '.set stuff', but don't do anything with this.
697 It's used in GAS to set various features like '.set mips16'. */
698 if (tok == ',')
699 set_symbol(s1, tok1);
700 break;
701 case TOK_ASMDIR_globl:
702 case TOK_ASMDIR_global:
703 case TOK_ASMDIR_weak:
704 case TOK_ASMDIR_hidden:
705 tok1 = tok;
706 do {
707 Sym *sym;
708 next();
709 sym = get_asm_sym(tok, NULL);
710 if (tok1 != TOK_ASMDIR_hidden)
711 sym->type.t &= ~VT_STATIC;
712 if (tok1 == TOK_ASMDIR_weak)
713 sym->a.weak = 1;
714 else if (tok1 == TOK_ASMDIR_hidden)
715 sym->a.visibility = STV_HIDDEN;
716 update_storage(sym);
717 next();
718 } while (tok == ',');
719 break;
720 case TOK_ASMDIR_string:
721 case TOK_ASMDIR_ascii:
722 case TOK_ASMDIR_asciz:
724 const uint8_t *p;
725 int i, size, t;
727 t = tok;
728 next();
729 for(;;) {
730 if (tok != TOK_STR)
731 expect("string constant");
732 p = tokc.str.data;
733 size = tokc.str.size;
734 if (t == TOK_ASMDIR_ascii && size > 0)
735 size--;
736 for(i = 0; i < size; i++)
737 g(p[i]);
738 next();
739 if (tok == ',') {
740 next();
741 } else if (tok != TOK_STR) {
742 break;
746 break;
747 case TOK_ASMDIR_text:
748 case TOK_ASMDIR_data:
749 case TOK_ASMDIR_bss:
751 char sname[64];
752 tok1 = tok;
753 n = 0;
754 next();
755 if (tok != ';' && tok != TOK_LINEFEED) {
756 n = asm_int_expr(s1);
757 next();
759 if (n)
760 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
761 else
762 sprintf(sname, "%s", get_tok_str(tok1, NULL));
763 use_section(s1, sname);
765 break;
766 case TOK_ASMDIR_file:
768 char filename[512];
770 filename[0] = '\0';
771 next();
772 if (tok == TOK_STR)
773 pstrcat(filename, sizeof(filename), tokc.str.data);
774 else
775 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
776 tcc_warning_c(warn_unsupported)("ignoring .file %s", filename);
777 next();
779 break;
780 case TOK_ASMDIR_ident:
782 char ident[256];
784 ident[0] = '\0';
785 next();
786 if (tok == TOK_STR)
787 pstrcat(ident, sizeof(ident), tokc.str.data);
788 else
789 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
790 tcc_warning_c(warn_unsupported)("ignoring .ident %s", ident);
791 next();
793 break;
794 case TOK_ASMDIR_size:
796 Sym *sym;
798 next();
799 sym = asm_label_find(tok);
800 if (!sym) {
801 tcc_error("label not found: %s", get_tok_str(tok, NULL));
803 /* XXX .size name,label2-label1 */
804 tcc_warning_c(warn_unsupported)("ignoring .size %s,*", get_tok_str(tok, NULL));
805 next();
806 skip(',');
807 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
808 next();
811 break;
812 case TOK_ASMDIR_type:
814 Sym *sym;
815 const char *newtype;
817 next();
818 sym = get_asm_sym(tok, NULL);
819 next();
820 skip(',');
821 if (tok == TOK_STR) {
822 newtype = tokc.str.data;
823 } else {
824 if (tok == '@' || tok == '%')
825 next();
826 newtype = get_tok_str(tok, NULL);
829 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
830 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
831 } else
832 tcc_warning_c(warn_unsupported)("change type of '%s' from 0x%x to '%s' ignored",
833 get_tok_str(sym->v, NULL), sym->type.t, newtype);
835 next();
837 break;
838 case TOK_ASMDIR_pushsection:
839 case TOK_ASMDIR_section:
841 char sname[256];
842 int old_nb_section = s1->nb_sections;
844 tok1 = tok;
845 /* XXX: support more options */
846 next();
847 sname[0] = '\0';
848 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
849 if (tok == TOK_STR)
850 pstrcat(sname, sizeof(sname), tokc.str.data);
851 else
852 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
853 next();
855 if (tok == ',') {
856 /* skip section options */
857 next();
858 if (tok != TOK_STR)
859 expect("string constant");
860 next();
861 if (tok == ',') {
862 next();
863 if (tok == '@' || tok == '%')
864 next();
865 next();
868 last_text_section = cur_text_section;
869 if (tok1 == TOK_ASMDIR_section)
870 use_section(s1, sname);
871 else
872 push_section(s1, sname);
873 /* If we just allocated a new section reset its alignment to
874 1. new_section normally acts for GCC compatibility and
875 sets alignment to PTR_SIZE. The assembler behaves different. */
876 if (old_nb_section != s1->nb_sections)
877 cur_text_section->sh_addralign = 1;
879 break;
880 case TOK_ASMDIR_previous:
882 Section *sec;
883 next();
884 if (!last_text_section)
885 tcc_error("no previous section referenced");
886 sec = cur_text_section;
887 use_section1(s1, last_text_section);
888 last_text_section = sec;
890 break;
891 case TOK_ASMDIR_popsection:
892 next();
893 pop_section(s1);
894 break;
895 #ifdef TCC_TARGET_I386
896 case TOK_ASMDIR_code16:
898 next();
899 s1->seg_size = 16;
901 break;
902 case TOK_ASMDIR_code32:
904 next();
905 s1->seg_size = 32;
907 break;
908 #endif
909 #ifdef TCC_TARGET_X86_64
910 /* added for compatibility with GAS */
911 case TOK_ASMDIR_code64:
912 next();
913 break;
914 #endif
915 default:
916 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
917 break;
922 /* assemble a file */
923 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
925 int opcode;
926 int saved_parse_flags = parse_flags;
928 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
929 if (do_preprocess)
930 parse_flags |= PARSE_FLAG_PREPROCESS;
931 for(;;) {
932 next();
933 if (tok == TOK_EOF)
934 break;
935 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
936 redo:
937 if (tok == '#') {
938 /* horrible gas comment */
939 while (tok != TOK_LINEFEED)
940 next();
941 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
942 asm_parse_directive(s1, global);
943 } else if (tok == TOK_PPNUM) {
944 const char *p;
945 int n;
946 p = tokc.str.data;
947 n = strtoul(p, (char **)&p, 10);
948 if (*p != '\0')
949 expect("':'");
950 /* new local label */
951 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
952 next();
953 skip(':');
954 goto redo;
955 } else if (tok >= TOK_IDENT) {
956 /* instruction or label */
957 opcode = tok;
958 next();
959 if (tok == ':') {
960 /* new label */
961 asm_new_label(s1, opcode, 0);
962 next();
963 goto redo;
964 } else if (tok == '=') {
965 set_symbol(s1, opcode);
966 goto redo;
967 } else {
968 asm_opcode(s1, opcode);
971 /* end of line */
972 if (tok != ';' && tok != TOK_LINEFEED)
973 expect("end of line");
974 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
977 parse_flags = saved_parse_flags;
978 return 0;
981 /* Assemble the current file */
982 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
984 int ret;
985 tcc_debug_start(s1);
986 /* default section is text */
987 cur_text_section = text_section;
988 ind = cur_text_section->data_offset;
989 nocode_wanted = 0;
990 ret = tcc_assemble_internal(s1, do_preprocess, 1);
991 cur_text_section->data_offset = ind;
992 tcc_debug_end(s1);
993 return ret;
996 /********************************************************************/
997 /* GCC inline asm support */
999 /* assemble the string 'str' in the current C compilation unit without
1000 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1001 end */
1002 static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
1004 const int *saved_macro_ptr = macro_ptr;
1005 int dotid = set_idnum('.', IS_ID);
1006 int dolid = set_idnum('$', 0);
1008 tcc_open_bf(s1, ":asm:", len);
1009 memcpy(file->buffer, str, len);
1010 macro_ptr = NULL;
1011 tcc_assemble_internal(s1, 0, global);
1012 tcc_close();
1014 set_idnum('$', dolid);
1015 set_idnum('.', dotid);
1016 macro_ptr = saved_macro_ptr;
1019 /* find a constraint by its number or id (gcc 3 extended
1020 syntax). return -1 if not found. Return in *pp in char after the
1021 constraint */
1022 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1023 const char *name, const char **pp)
1025 int index;
1026 TokenSym *ts;
1027 const char *p;
1029 if (isnum(*name)) {
1030 index = 0;
1031 while (isnum(*name)) {
1032 index = (index * 10) + (*name) - '0';
1033 name++;
1035 if ((unsigned)index >= nb_operands)
1036 index = -1;
1037 } else if (*name == '[') {
1038 name++;
1039 p = strchr(name, ']');
1040 if (p) {
1041 ts = tok_alloc(name, p - name);
1042 for(index = 0; index < nb_operands; index++) {
1043 if (operands[index].id == ts->tok)
1044 goto found;
1046 index = -1;
1047 found:
1048 name = p + 1;
1049 } else {
1050 index = -1;
1052 } else {
1053 index = -1;
1055 if (pp)
1056 *pp = name;
1057 return index;
1060 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
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];
1168 Section *sec;
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, &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 /* We don't allow switching section within inline asm to
1243 bleed out to surrounding code. */
1244 sec = cur_text_section;
1245 /* assemble the string with tcc internal assembler */
1246 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1247 if (sec != cur_text_section) {
1248 tcc_warning("inline asm tries to change current section");
1249 use_section1(tcc_state, sec);
1252 /* restore the current C token */
1253 next();
1255 /* store the output values if needed */
1256 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1257 clobber_regs, out_reg);
1259 /* free everything */
1260 for(i=0;i<nb_operands;i++) {
1261 ASMOperand *op;
1262 op = &operands[i];
1263 tcc_free(op->constraint);
1264 vpop();
1266 cstr_free(&astr1);
1269 ST_FUNC void asm_global_instr(void)
1271 CString astr;
1272 int saved_nocode_wanted = nocode_wanted;
1274 /* Global asm blocks are always emitted. */
1275 nocode_wanted = 0;
1276 next();
1277 parse_asm_str(&astr);
1278 skip(')');
1279 /* NOTE: we do not eat the ';' so that we can restore the current
1280 token after the assembler parsing */
1281 if (tok != ';')
1282 expect("';'");
1284 #ifdef ASM_DEBUG
1285 printf("asm_global: \"%s\"\n", (char *)astr.data);
1286 #endif
1287 cur_text_section = text_section;
1288 ind = cur_text_section->data_offset;
1290 /* assemble the string with tcc internal assembler */
1291 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1293 cur_text_section->data_offset = ind;
1295 /* restore the current C token */
1296 next();
1298 cstr_free(&astr);
1299 nocode_wanted = saved_nocode_wanted;
1302 /********************************************************/
1303 #else
1304 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
1306 tcc_error("asm not supported");
1309 ST_FUNC void asm_instr(void)
1311 tcc_error("inline asm() not supported");
1314 ST_FUNC void asm_global_instr(void)
1316 tcc_error("inline asm() not supported");
1318 #endif /* CONFIG_TCC_ASM */