x86asm: Add lzcnt/tzcnt support
[tinycc.git] / tccasm.c
blob65aae62facb46bcf23190a276573fe03dd91c9f6
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 */
26 static int asmgoto_n;
28 static int asm_get_prefix_name(TCCState *s1, const char *prefix, unsigned int n)
30 char buf[64];
31 snprintf(buf, sizeof(buf), "%s%u", prefix, n);
32 return tok_alloc_const(buf);
35 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
37 return asm_get_prefix_name(s1, "L..", n);
40 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
41 static Sym* asm_new_label(TCCState *s1, int label, int is_local);
42 static Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, int value);
44 /* If a C name has an _ prepended then only asm labels that start
45 with _ are representable in C, by removing the first _. ASM names
46 without _ at the beginning don't correspond to C names, but we use
47 the global C symbol table to track ASM names as well, so we need to
48 transform those into ones that don't conflict with a C name,
49 so prepend a '.' for them, but force the ELF asm name to be set. */
50 static int asm2cname(int v, int *addeddot)
52 const char *name;
53 *addeddot = 0;
54 if (!tcc_state->leading_underscore)
55 return v;
56 name = get_tok_str(v, NULL);
57 if (!name)
58 return v;
59 if (name[0] == '_') {
60 v = tok_alloc_const(name + 1);
61 } else if (!strchr(name, '.')) {
62 char newname[256];
63 snprintf(newname, sizeof newname, ".%s", name);
64 v = tok_alloc_const(newname);
65 *addeddot = 1;
67 return v;
70 static Sym *asm_label_find(int v)
72 Sym *sym;
73 int addeddot;
74 v = asm2cname(v, &addeddot);
75 sym = sym_find(v);
76 while (sym && sym->sym_scope && !(sym->type.t & VT_STATIC))
77 sym = sym->prev_tok;
78 return sym;
81 static Sym *asm_label_push(int v)
83 int addeddot, v2 = asm2cname(v, &addeddot);
84 /* We always add VT_EXTERN, for sym definition that's tentative
85 (for .set, removed for real defs), for mere references it's correct
86 as is. */
87 Sym *sym = global_identifier_push(v2, VT_ASM | VT_EXTERN | VT_STATIC, 0);
88 if (addeddot)
89 sym->asm_label = v;
90 return sym;
93 /* Return a symbol we can use inside the assembler, having name NAME.
94 Symbols from asm and C source share a namespace. If we generate
95 an asm symbol it's also a (file-global) C symbol, but it's
96 either not accessible by name (like "L.123"), or its type information
97 is such that it's not usable without a proper C declaration.
99 Sometimes we need symbols accessible by name from asm, which
100 are anonymous in C, in this case CSYM can be used to transfer
101 all information from that symbol to the (possibly newly created)
102 asm symbol. */
103 ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
105 Sym *sym = asm_label_find(name);
106 if (!sym) {
107 sym = asm_label_push(name);
108 if (csym)
109 sym->c = csym->c;
111 return sym;
114 static Sym* asm_section_sym(TCCState *s1, Section *sec)
116 char buf[100]; int label; Sym *sym;
117 snprintf(buf, sizeof buf, "L.%s", sec->name);
118 label = tok_alloc_const(buf);
119 sym = asm_label_find(label);
120 return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0);
123 /* We do not use the C expression parser to handle symbols. Maybe the
124 C expression parser could be tweaked to do so. */
126 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
128 Sym *sym;
129 int op, label;
130 uint64_t n;
131 const char *p;
133 switch(tok) {
134 case TOK_PPNUM:
135 p = tokc.str.data;
136 n = strtoull(p, (char **)&p, 0);
137 if (*p == 'b' || *p == 'f') {
138 /* backward or forward label */
139 label = asm_get_local_label_name(s1, n);
140 sym = asm_label_find(label);
141 if (*p == 'b') {
142 /* backward : find the last corresponding defined label */
143 if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF))
144 sym = sym->prev_tok;
145 if (!sym)
146 tcc_error("local label '%d' not found backward", (int)n);
147 } else {
148 /* forward */
149 if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) {
150 /* if the last label is defined, then define a new one */
151 sym = asm_label_push(label);
154 pe->v = 0;
155 pe->sym = sym;
156 pe->pcrel = 0;
157 } else if (*p == '\0') {
158 pe->v = n;
159 pe->sym = NULL;
160 pe->pcrel = 0;
161 } else {
162 tcc_error("invalid number syntax");
164 next();
165 break;
166 case '+':
167 next();
168 asm_expr_unary(s1, pe);
169 break;
170 case '-':
171 case '~':
172 op = tok;
173 next();
174 asm_expr_unary(s1, pe);
175 if (pe->sym)
176 tcc_error("invalid operation with label");
177 if (op == '-')
178 pe->v = -pe->v;
179 else
180 pe->v = ~pe->v;
181 break;
182 case TOK_CCHAR:
183 case TOK_LCHAR:
184 pe->v = tokc.i;
185 pe->sym = NULL;
186 pe->pcrel = 0;
187 next();
188 break;
189 case '(':
190 next();
191 asm_expr(s1, pe);
192 skip(')');
193 break;
194 case '.':
195 pe->v = ind;
196 pe->sym = asm_section_sym(s1, cur_text_section);
197 pe->pcrel = 0;
198 next();
199 break;
200 default:
201 if (tok >= TOK_IDENT) {
202 ElfSym *esym;
203 /* label case : if the label was not found, add one */
204 sym = get_asm_sym(tok, NULL);
205 esym = elfsym(sym);
206 if (esym && esym->st_shndx == SHN_ABS) {
207 /* if absolute symbol, no need to put a symbol value */
208 pe->v = esym->st_value;
209 pe->sym = NULL;
210 pe->pcrel = 0;
211 } else {
212 pe->v = 0;
213 pe->sym = sym;
214 pe->pcrel = 0;
216 next();
217 } else {
218 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
220 break;
224 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
226 int op;
227 ExprValue e2;
229 asm_expr_unary(s1, pe);
230 for(;;) {
231 op = tok;
232 if (op != '*' && op != '/' && op != '%' &&
233 op != TOK_SHL && op != TOK_SAR)
234 break;
235 next();
236 asm_expr_unary(s1, &e2);
237 if (pe->sym || e2.sym)
238 tcc_error("invalid operation with label");
239 switch(op) {
240 case '*':
241 pe->v *= e2.v;
242 break;
243 case '/':
244 if (e2.v == 0) {
245 div_error:
246 tcc_error("division by zero");
248 pe->v /= e2.v;
249 break;
250 case '%':
251 if (e2.v == 0)
252 goto div_error;
253 pe->v %= e2.v;
254 break;
255 case TOK_SHL:
256 pe->v <<= e2.v;
257 break;
258 default:
259 case TOK_SAR:
260 pe->v >>= e2.v;
261 break;
266 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
268 int op;
269 ExprValue e2;
271 asm_expr_prod(s1, pe);
272 for(;;) {
273 op = tok;
274 if (op != '&' && op != '|' && op != '^')
275 break;
276 next();
277 asm_expr_prod(s1, &e2);
278 if (pe->sym || e2.sym)
279 tcc_error("invalid operation with label");
280 switch(op) {
281 case '&':
282 pe->v &= e2.v;
283 break;
284 case '|':
285 pe->v |= e2.v;
286 break;
287 default:
288 case '^':
289 pe->v ^= e2.v;
290 break;
295 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
297 int op;
298 ExprValue e2;
300 asm_expr_logic(s1, pe);
301 for(;;) {
302 op = tok;
303 if (op != '+' && op != '-')
304 break;
305 next();
306 asm_expr_logic(s1, &e2);
307 if (op == '+') {
308 if (pe->sym != NULL && e2.sym != NULL)
309 goto cannot_relocate;
310 pe->v += e2.v;
311 if (pe->sym == NULL && e2.sym != NULL)
312 pe->sym = e2.sym;
313 } else {
314 pe->v -= e2.v;
315 /* NOTE: we are less powerful than gas in that case
316 because we store only one symbol in the expression */
317 if (!e2.sym) {
318 /* OK */
319 } else if (pe->sym == e2.sym) {
320 /* OK */
321 pe->sym = NULL; /* same symbols can be subtracted to NULL */
322 } else {
323 ElfSym *esym1, *esym2;
324 esym1 = elfsym(pe->sym);
325 esym2 = elfsym(e2.sym);
326 if (esym1 && esym1->st_shndx == esym2->st_shndx
327 && esym1->st_shndx != SHN_UNDEF) {
328 /* we also accept defined symbols in the same section */
329 pe->v += esym1->st_value - esym2->st_value;
330 pe->sym = NULL;
331 } else if (esym2->st_shndx == cur_text_section->sh_num) {
332 /* When subtracting a defined symbol in current section
333 this actually makes the value PC-relative. */
334 pe->v -= esym2->st_value - ind - 4;
335 pe->pcrel = 1;
336 e2.sym = NULL;
337 } else {
338 cannot_relocate:
339 tcc_error("invalid operation with label");
346 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
348 int op;
349 ExprValue e2;
351 asm_expr_sum(s1, pe);
352 for(;;) {
353 op = tok;
354 if (op != TOK_EQ && op != TOK_NE
355 && (op > TOK_GT || op < TOK_ULE))
356 break;
357 next();
358 asm_expr_sum(s1, &e2);
359 if (pe->sym || e2.sym)
360 tcc_error("invalid operation with label");
361 switch(op) {
362 case TOK_EQ:
363 pe->v = pe->v == e2.v;
364 break;
365 case TOK_NE:
366 pe->v = pe->v != e2.v;
367 break;
368 case TOK_LT:
369 pe->v = (int64_t)pe->v < (int64_t)e2.v;
370 break;
371 case TOK_GE:
372 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
373 break;
374 case TOK_LE:
375 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
376 break;
377 case TOK_GT:
378 pe->v = (int64_t)pe->v > (int64_t)e2.v;
379 break;
380 default:
381 break;
383 /* GAS compare results are -1/0 not 1/0. */
384 pe->v = -(int64_t)pe->v;
388 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
390 asm_expr_cmp(s1, pe);
393 ST_FUNC int asm_int_expr(TCCState *s1)
395 ExprValue e;
396 asm_expr(s1, &e);
397 if (e.sym)
398 expect("constant");
399 return e.v;
402 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
403 int sh_num, int value)
405 Sym *sym;
406 ElfSym *esym;
408 sym = asm_label_find(label);
409 if (sym) {
410 esym = elfsym(sym);
411 /* A VT_EXTERN symbol, even if it has a section is considered
412 overridable. This is how we "define" .set targets. Real
413 definitions won't have VT_EXTERN set. */
414 if (esym && esym->st_shndx != SHN_UNDEF) {
415 /* the label is already defined */
416 if (IS_ASM_SYM(sym)
417 && (is_local == 1 || (sym->type.t & VT_EXTERN)))
418 goto new_label;
419 if (!(sym->type.t & VT_EXTERN))
420 tcc_error("assembler label '%s' already defined",
421 get_tok_str(label, NULL));
423 } else {
424 new_label:
425 sym = asm_label_push(label);
427 if (!sym->c)
428 put_extern_sym2(sym, SHN_UNDEF, 0, 0, 1);
429 esym = elfsym(sym);
430 esym->st_shndx = sh_num;
431 esym->st_value = value;
432 if (is_local != 2)
433 sym->type.t &= ~VT_EXTERN;
434 return sym;
437 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
439 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
442 /* Set the value of LABEL to that of some expression (possibly
443 involving other symbols). LABEL can be overwritten later still. */
444 static Sym* set_symbol(TCCState *s1, int label)
446 long n;
447 ExprValue e;
448 Sym *sym;
449 ElfSym *esym;
450 next();
451 asm_expr(s1, &e);
452 n = e.v;
453 esym = elfsym(e.sym);
454 if (esym)
455 n += esym->st_value;
456 sym = asm_new_label1(s1, label, 2, esym ? esym->st_shndx : SHN_ABS, n);
457 elfsym(sym)->st_other |= ST_ASM_SET;
458 return sym;
461 static void use_section1(TCCState *s1, Section *sec)
463 cur_text_section->data_offset = ind;
464 cur_text_section = sec;
465 ind = cur_text_section->data_offset;
468 static void use_section(TCCState *s1, const char *name)
470 Section *sec;
471 sec = find_section(s1, name);
472 use_section1(s1, sec);
475 static void push_section(TCCState *s1, const char *name)
477 Section *sec = find_section(s1, name);
478 sec->prev = cur_text_section;
479 use_section1(s1, sec);
482 static void pop_section(TCCState *s1)
484 Section *prev = cur_text_section->prev;
485 if (!prev)
486 tcc_error(".popsection without .pushsection");
487 cur_text_section->prev = NULL;
488 use_section1(s1, prev);
491 static void asm_parse_directive(TCCState *s1, int global)
493 int n, offset, v, size, tok1;
494 Section *sec;
495 uint8_t *ptr;
497 /* assembler directive */
498 sec = cur_text_section;
499 switch(tok) {
500 case TOK_ASMDIR_align:
501 case TOK_ASMDIR_balign:
502 case TOK_ASMDIR_p2align:
503 case TOK_ASMDIR_skip:
504 case TOK_ASMDIR_space:
505 tok1 = tok;
506 next();
507 n = asm_int_expr(s1);
508 if (tok1 == TOK_ASMDIR_p2align)
510 if (n < 0 || n > 30)
511 tcc_error("invalid p2align, must be between 0 and 30");
512 n = 1 << n;
513 tok1 = TOK_ASMDIR_align;
515 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
516 if (n < 0 || (n & (n-1)) != 0)
517 tcc_error("alignment must be a positive power of two");
518 offset = (ind + n - 1) & -n;
519 size = offset - ind;
520 /* the section must have a compatible alignment */
521 if (sec->sh_addralign < n)
522 sec->sh_addralign = n;
523 } else {
524 if (n < 0)
525 n = 0;
526 size = n;
528 v = 0;
529 if (tok == ',') {
530 next();
531 v = asm_int_expr(s1);
533 zero_pad:
534 if (sec->sh_type != SHT_NOBITS) {
535 sec->data_offset = ind;
536 ptr = section_ptr_add(sec, size);
537 memset(ptr, v, size);
539 ind += size;
540 break;
541 case TOK_ASMDIR_quad:
542 #ifdef TCC_TARGET_X86_64
543 size = 8;
544 goto asm_data;
545 #else
546 next();
547 for(;;) {
548 uint64_t vl;
549 const char *p;
551 p = tokc.str.data;
552 if (tok != TOK_PPNUM) {
553 error_constant:
554 tcc_error("64 bit constant");
556 vl = strtoll(p, (char **)&p, 0);
557 if (*p != '\0')
558 goto error_constant;
559 next();
560 if (sec->sh_type != SHT_NOBITS) {
561 /* XXX: endianness */
562 gen_le32(vl);
563 gen_le32(vl >> 32);
564 } else {
565 ind += 8;
567 if (tok != ',')
568 break;
569 next();
571 break;
572 #endif
573 case TOK_ASMDIR_byte:
574 size = 1;
575 goto asm_data;
576 case TOK_ASMDIR_word:
577 case TOK_ASMDIR_short:
578 size = 2;
579 goto asm_data;
580 case TOK_ASMDIR_long:
581 case TOK_ASMDIR_int:
582 size = 4;
583 asm_data:
584 next();
585 for(;;) {
586 ExprValue e;
587 asm_expr(s1, &e);
588 if (sec->sh_type != SHT_NOBITS) {
589 if (size == 4) {
590 gen_expr32(&e);
591 #ifdef TCC_TARGET_X86_64
592 } else if (size == 8) {
593 gen_expr64(&e);
594 #endif
595 } else {
596 if (e.sym)
597 expect("constant");
598 if (size == 1)
599 g(e.v);
600 else
601 gen_le16(e.v);
603 } else {
604 ind += size;
606 if (tok != ',')
607 break;
608 next();
610 break;
611 case TOK_ASMDIR_fill:
613 int repeat, size, val, i, j;
614 uint8_t repeat_buf[8];
615 next();
616 repeat = asm_int_expr(s1);
617 if (repeat < 0) {
618 tcc_error("repeat < 0; .fill ignored");
619 break;
621 size = 1;
622 val = 0;
623 if (tok == ',') {
624 next();
625 size = asm_int_expr(s1);
626 if (size < 0) {
627 tcc_error("size < 0; .fill ignored");
628 break;
630 if (size > 8)
631 size = 8;
632 if (tok == ',') {
633 next();
634 val = asm_int_expr(s1);
637 /* XXX: endianness */
638 repeat_buf[0] = val;
639 repeat_buf[1] = val >> 8;
640 repeat_buf[2] = val >> 16;
641 repeat_buf[3] = val >> 24;
642 repeat_buf[4] = 0;
643 repeat_buf[5] = 0;
644 repeat_buf[6] = 0;
645 repeat_buf[7] = 0;
646 for(i = 0; i < repeat; i++) {
647 for(j = 0; j < size; j++) {
648 g(repeat_buf[j]);
652 break;
653 case TOK_ASMDIR_rept:
655 int repeat;
656 TokenString *init_str;
657 next();
658 repeat = asm_int_expr(s1);
659 init_str = tok_str_alloc();
660 while (next(), tok != TOK_ASMDIR_endr) {
661 if (tok == CH_EOF)
662 tcc_error("we at end of file, .endr not found");
663 tok_str_add_tok(init_str);
665 tok_str_add(init_str, -1);
666 tok_str_add(init_str, 0);
667 begin_macro(init_str, 1);
668 while (repeat-- > 0) {
669 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
670 global);
671 macro_ptr = init_str->str;
673 end_macro();
674 next();
675 break;
677 case TOK_ASMDIR_org:
679 unsigned long n;
680 ExprValue e;
681 ElfSym *esym;
682 next();
683 asm_expr(s1, &e);
684 n = e.v;
685 esym = elfsym(e.sym);
686 if (esym) {
687 if (esym->st_shndx != cur_text_section->sh_num)
688 expect("constant or same-section symbol");
689 n += esym->st_value;
691 if (n < ind)
692 tcc_error("attempt to .org backwards");
693 v = 0;
694 size = n - ind;
695 goto zero_pad;
697 break;
698 case TOK_ASMDIR_set:
699 next();
700 tok1 = tok;
701 next();
702 /* Also accept '.set stuff', but don't do anything with this.
703 It's used in GAS to set various features like '.set mips16'. */
704 if (tok == ',')
705 set_symbol(s1, tok1);
706 break;
707 case TOK_ASMDIR_globl:
708 case TOK_ASMDIR_global:
709 case TOK_ASMDIR_weak:
710 case TOK_ASMDIR_hidden:
711 tok1 = tok;
712 do {
713 Sym *sym;
714 next();
715 sym = get_asm_sym(tok, NULL);
716 if (tok1 != TOK_ASMDIR_hidden)
717 sym->type.t &= ~VT_STATIC;
718 if (tok1 == TOK_ASMDIR_weak)
719 sym->a.weak = 1;
720 else if (tok1 == TOK_ASMDIR_hidden)
721 sym->a.visibility = STV_HIDDEN;
722 update_storage(sym);
723 next();
724 } while (tok == ',');
725 break;
726 case TOK_ASMDIR_string:
727 case TOK_ASMDIR_ascii:
728 case TOK_ASMDIR_asciz:
730 const uint8_t *p;
731 int i, size, t;
733 t = tok;
734 next();
735 for(;;) {
736 if (tok != TOK_STR)
737 expect("string constant");
738 p = tokc.str.data;
739 size = tokc.str.size;
740 if (t == TOK_ASMDIR_ascii && size > 0)
741 size--;
742 for(i = 0; i < size; i++)
743 g(p[i]);
744 next();
745 if (tok == ',') {
746 next();
747 } else if (tok != TOK_STR) {
748 break;
752 break;
753 case TOK_ASMDIR_text:
754 case TOK_ASMDIR_data:
755 case TOK_ASMDIR_bss:
757 char sname[64];
758 tok1 = tok;
759 n = 0;
760 next();
761 if (tok != ';' && tok != TOK_LINEFEED) {
762 n = asm_int_expr(s1);
763 next();
765 if (n)
766 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
767 else
768 sprintf(sname, "%s", get_tok_str(tok1, NULL));
769 use_section(s1, sname);
771 break;
772 case TOK_ASMDIR_file:
774 char filename[512];
776 filename[0] = '\0';
777 next();
778 if (tok == TOK_STR)
779 pstrcat(filename, sizeof(filename), tokc.str.data);
780 else
781 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
782 tcc_warning_c(warn_unsupported)("ignoring .file %s", filename);
783 next();
785 break;
786 case TOK_ASMDIR_ident:
788 char ident[256];
790 ident[0] = '\0';
791 next();
792 if (tok == TOK_STR)
793 pstrcat(ident, sizeof(ident), tokc.str.data);
794 else
795 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
796 tcc_warning_c(warn_unsupported)("ignoring .ident %s", ident);
797 next();
799 break;
800 case TOK_ASMDIR_size:
802 Sym *sym;
804 next();
805 sym = asm_label_find(tok);
806 if (!sym) {
807 tcc_error("label not found: %s", get_tok_str(tok, NULL));
809 /* XXX .size name,label2-label1 */
810 tcc_warning_c(warn_unsupported)("ignoring .size %s,*", get_tok_str(tok, NULL));
811 next();
812 skip(',');
813 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
814 next();
817 break;
818 case TOK_ASMDIR_type:
820 Sym *sym;
821 const char *newtype;
823 next();
824 sym = get_asm_sym(tok, NULL);
825 next();
826 skip(',');
827 if (tok == TOK_STR) {
828 newtype = tokc.str.data;
829 } else {
830 if (tok == '@' || tok == '%')
831 next();
832 newtype = get_tok_str(tok, NULL);
835 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
836 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
837 } else
838 tcc_warning_c(warn_unsupported)("change type of '%s' from 0x%x to '%s' ignored",
839 get_tok_str(sym->v, NULL), sym->type.t, newtype);
841 next();
843 break;
844 case TOK_ASMDIR_pushsection:
845 case TOK_ASMDIR_section:
847 char sname[256];
848 int old_nb_section = s1->nb_sections;
850 tok1 = tok;
851 /* XXX: support more options */
852 next();
853 sname[0] = '\0';
854 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
855 if (tok == TOK_STR)
856 pstrcat(sname, sizeof(sname), tokc.str.data);
857 else
858 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
859 next();
861 if (tok == ',') {
862 /* skip section options */
863 next();
864 if (tok != TOK_STR)
865 expect("string constant");
866 next();
867 if (tok == ',') {
868 next();
869 if (tok == '@' || tok == '%')
870 next();
871 next();
874 last_text_section = cur_text_section;
875 if (tok1 == TOK_ASMDIR_section)
876 use_section(s1, sname);
877 else
878 push_section(s1, sname);
879 /* If we just allocated a new section reset its alignment to
880 1. new_section normally acts for GCC compatibility and
881 sets alignment to PTR_SIZE. The assembler behaves different. */
882 if (old_nb_section != s1->nb_sections)
883 cur_text_section->sh_addralign = 1;
885 break;
886 case TOK_ASMDIR_previous:
888 Section *sec;
889 next();
890 if (!last_text_section)
891 tcc_error("no previous section referenced");
892 sec = cur_text_section;
893 use_section1(s1, last_text_section);
894 last_text_section = sec;
896 break;
897 case TOK_ASMDIR_popsection:
898 next();
899 pop_section(s1);
900 break;
901 #ifdef TCC_TARGET_I386
902 case TOK_ASMDIR_code16:
904 next();
905 s1->seg_size = 16;
907 break;
908 case TOK_ASMDIR_code32:
910 next();
911 s1->seg_size = 32;
913 break;
914 #endif
915 #ifdef TCC_TARGET_X86_64
916 /* added for compatibility with GAS */
917 case TOK_ASMDIR_code64:
918 next();
919 break;
920 #endif
921 default:
922 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
923 break;
928 /* assemble a file */
929 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
931 int opcode;
932 int saved_parse_flags = parse_flags;
934 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
935 if (do_preprocess)
936 parse_flags |= PARSE_FLAG_PREPROCESS;
937 for(;;) {
938 next();
939 if (tok == TOK_EOF)
940 break;
941 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
942 redo:
943 if (tok == '#') {
944 /* horrible gas comment */
945 while (tok != TOK_LINEFEED)
946 next();
947 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
948 asm_parse_directive(s1, global);
949 } else if (tok == TOK_PPNUM) {
950 const char *p;
951 int n;
952 p = tokc.str.data;
953 n = strtoul(p, (char **)&p, 10);
954 if (*p != '\0')
955 expect("':'");
956 /* new local label */
957 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
958 next();
959 skip(':');
960 goto redo;
961 } else if (tok >= TOK_IDENT) {
962 /* instruction or label */
963 opcode = tok;
964 next();
965 if (tok == ':') {
966 /* new label */
967 asm_new_label(s1, opcode, 0);
968 next();
969 goto redo;
970 } else if (tok == '=') {
971 set_symbol(s1, opcode);
972 goto redo;
973 } else {
974 asm_opcode(s1, opcode);
977 /* end of line */
978 if (tok != ';' && tok != TOK_LINEFEED)
979 expect("end of line");
980 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
983 parse_flags = saved_parse_flags;
984 return 0;
987 /* Assemble the current file */
988 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
990 int ret;
991 tcc_debug_start(s1);
992 /* default section is text */
993 cur_text_section = text_section;
994 ind = cur_text_section->data_offset;
995 nocode_wanted = 0;
996 ret = tcc_assemble_internal(s1, do_preprocess, 1);
997 cur_text_section->data_offset = ind;
998 tcc_debug_end(s1);
999 return ret;
1002 /********************************************************************/
1003 /* GCC inline asm support */
1005 /* assemble the string 'str' in the current C compilation unit without
1006 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1007 end */
1008 static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
1010 const int *saved_macro_ptr = macro_ptr;
1011 int dotid = set_idnum('.', IS_ID);
1012 int dolid = set_idnum('$', 0);
1014 tcc_open_bf(s1, ":asm:", len);
1015 memcpy(file->buffer, str, len);
1016 macro_ptr = NULL;
1017 tcc_assemble_internal(s1, 0, global);
1018 tcc_close();
1020 set_idnum('$', dolid);
1021 set_idnum('.', dotid);
1022 macro_ptr = saved_macro_ptr;
1025 /* find a constraint by its number or id (gcc 3 extended
1026 syntax). return -1 if not found. Return in *pp in char after the
1027 constraint */
1028 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1029 const char *name, const char **pp)
1031 int index;
1032 TokenSym *ts;
1033 const char *p;
1035 if (isnum(*name)) {
1036 index = 0;
1037 while (isnum(*name)) {
1038 index = (index * 10) + (*name) - '0';
1039 name++;
1041 if ((unsigned)index >= nb_operands)
1042 index = -1;
1043 } else if (*name == '[') {
1044 name++;
1045 p = strchr(name, ']');
1046 if (p) {
1047 ts = tok_alloc(name, p - name);
1048 for(index = 0; index < nb_operands; index++) {
1049 if (operands[index].id == ts->tok)
1050 goto found;
1052 index = -1;
1053 found:
1054 name = p + 1;
1055 } else {
1056 index = -1;
1058 } else {
1059 index = -1;
1061 if (pp)
1062 *pp = name;
1063 return index;
1066 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1067 CString *out_str, CString *in_str)
1069 int c, index, modifier;
1070 const char *str;
1071 ASMOperand *op;
1072 SValue sv;
1074 cstr_new(out_str);
1075 str = in_str->data;
1076 for(;;) {
1077 c = *str++;
1078 if (c == '%') {
1079 if (*str == '%') {
1080 str++;
1081 goto add_char;
1083 modifier = 0;
1084 if (*str == 'c' || *str == 'n' ||
1085 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1086 *str == 'q' || *str == 'l' ||
1087 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1088 and make literal operands not be decorated with '$'. */
1089 *str == 'P')
1090 modifier = *str++;
1091 index = find_constraint(operands, nb_operands, str, &str);
1092 if (index < 0)
1093 tcc_error("invalid operand reference after %%");
1094 op = &operands[index];
1095 if (modifier == 'l') {
1096 cstr_cat(out_str, get_tok_str(op->is_label, NULL), -1);
1097 } else {
1098 sv = *op->vt;
1099 if (op->reg >= 0) {
1100 sv.r = op->reg;
1101 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1102 sv.r |= VT_LVAL;
1104 subst_asm_operand(out_str, &sv, modifier);
1106 } else {
1107 add_char:
1108 cstr_ccat(out_str, c);
1109 if (c == '\0')
1110 break;
1116 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1117 int is_output)
1119 ASMOperand *op;
1120 int nb_operands;
1122 if (tok != ':') {
1123 nb_operands = *nb_operands_ptr;
1124 for(;;) {
1125 CString astr;
1126 if (nb_operands >= MAX_ASM_OPERANDS)
1127 tcc_error("too many asm operands");
1128 op = &operands[nb_operands++];
1129 op->id = 0;
1130 if (tok == '[') {
1131 next();
1132 if (tok < TOK_IDENT)
1133 expect("identifier");
1134 op->id = tok;
1135 next();
1136 skip(']');
1138 parse_mult_str(&astr, "string constant");
1139 op->constraint = tcc_malloc(astr.size);
1140 strcpy(op->constraint, astr.data);
1141 cstr_free(&astr);
1142 skip('(');
1143 gexpr();
1144 if (is_output) {
1145 if (!(vtop->type.t & VT_ARRAY))
1146 test_lvalue();
1147 } else {
1148 /* we want to avoid LLOCAL case, except when the 'm'
1149 constraint is used. Note that it may come from
1150 register storage, so we need to convert (reg)
1151 case */
1152 if ((vtop->r & VT_LVAL) &&
1153 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1154 (vtop->r & VT_VALMASK) < VT_CONST) &&
1155 !strchr(op->constraint, 'm')) {
1156 gv(RC_INT);
1159 op->vt = vtop;
1160 skip(')');
1161 if (tok == ',') {
1162 next();
1163 } else {
1164 break;
1167 *nb_operands_ptr = nb_operands;
1171 /* parse the GCC asm() instruction */
1172 ST_FUNC void asm_instr(void)
1174 CString astr, astr1;
1175 ASMOperand operands[MAX_ASM_OPERANDS];
1176 int nb_outputs, nb_operands, i, must_subst, out_reg, nb_labels;
1177 uint8_t clobber_regs[NB_ASM_REGS];
1178 Section *sec;
1180 /* since we always generate the asm() instruction, we can ignore
1181 volatile */
1182 while (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3
1183 || tok == TOK_GOTO) {
1184 next();
1186 parse_asm_str(&astr);
1187 nb_operands = 0;
1188 nb_outputs = 0;
1189 nb_labels = 0;
1190 must_subst = 0;
1191 memset(clobber_regs, 0, sizeof(clobber_regs));
1192 if (tok == ':') {
1193 next();
1194 must_subst = 1;
1195 /* output args */
1196 parse_asm_operands(operands, &nb_operands, 1);
1197 nb_outputs = nb_operands;
1198 if (tok == ':') {
1199 next();
1200 if (tok != ')') {
1201 /* input args */
1202 parse_asm_operands(operands, &nb_operands, 0);
1203 if (tok == ':') {
1204 /* clobber list */
1205 /* XXX: handle registers */
1206 next();
1207 for(;;) {
1208 if (tok == ':')
1209 break;
1210 if (tok != TOK_STR)
1211 expect("string constant");
1212 asm_clobber(clobber_regs, tokc.str.data);
1213 next();
1214 if (tok == ',') {
1215 next();
1216 } else {
1217 break;
1221 if (tok == ':') {
1222 /* goto labels */
1223 next();
1224 for (;;) {
1225 Sym *csym;
1226 int asmname;
1227 if (nb_operands + nb_labels >= MAX_ASM_OPERANDS)
1228 tcc_error("too many asm operands");
1229 if (tok < TOK_UIDENT)
1230 expect("label identifier");
1231 operands[nb_operands + nb_labels++].id = tok;
1233 csym = label_find(tok);
1234 if (!csym) {
1235 csym = label_push(&global_label_stack, tok,
1236 LABEL_FORWARD);
1237 } else {
1238 if (csym->r == LABEL_DECLARED)
1239 csym->r = LABEL_FORWARD;
1241 next();
1242 asmname = asm_get_prefix_name(tcc_state, "LG.",
1243 ++asmgoto_n);
1244 if (!csym->c)
1245 put_extern_sym2(csym, SHN_UNDEF, 0, 0, 1);
1246 get_asm_sym(asmname, csym);
1247 operands[nb_operands + nb_labels - 1].is_label = asmname;
1249 if (tok != ',')
1250 break;
1251 next();
1257 skip(')');
1258 /* NOTE: we do not eat the ';' so that we can restore the current
1259 token after the assembler parsing */
1260 if (tok != ';')
1261 expect("';'");
1263 /* save all values in the memory */
1264 save_regs(0);
1266 /* compute constraints */
1267 asm_compute_constraints(operands, nb_operands, nb_outputs,
1268 clobber_regs, &out_reg);
1270 /* substitute the operands in the asm string. No substitution is
1271 done if no operands (GCC behaviour) */
1272 #ifdef ASM_DEBUG
1273 printf("asm: \"%s\"\n", (char *)astr.data);
1274 #endif
1275 if (must_subst) {
1276 subst_asm_operands(operands, nb_operands + nb_labels, &astr1, &astr);
1277 cstr_free(&astr);
1278 } else {
1279 astr1 = astr;
1281 #ifdef ASM_DEBUG
1282 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1283 #endif
1285 /* generate loads */
1286 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1287 clobber_regs, out_reg);
1289 /* We don't allow switching section within inline asm to
1290 bleed out to surrounding code. */
1291 sec = cur_text_section;
1292 /* assemble the string with tcc internal assembler */
1293 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1294 if (sec != cur_text_section) {
1295 tcc_warning("inline asm tries to change current section");
1296 use_section1(tcc_state, sec);
1299 /* restore the current C token */
1300 next();
1302 /* store the output values if needed */
1303 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1304 clobber_regs, out_reg);
1306 /* free everything */
1307 for(i=0;i<nb_operands;i++) {
1308 ASMOperand *op;
1309 op = &operands[i];
1310 tcc_free(op->constraint);
1311 vpop();
1313 cstr_free(&astr1);
1316 ST_FUNC void asm_global_instr(void)
1318 CString astr;
1319 int saved_nocode_wanted = nocode_wanted;
1321 /* Global asm blocks are always emitted. */
1322 nocode_wanted = 0;
1323 next();
1324 parse_asm_str(&astr);
1325 skip(')');
1326 /* NOTE: we do not eat the ';' so that we can restore the current
1327 token after the assembler parsing */
1328 if (tok != ';')
1329 expect("';'");
1331 #ifdef ASM_DEBUG
1332 printf("asm_global: \"%s\"\n", (char *)astr.data);
1333 #endif
1334 cur_text_section = text_section;
1335 ind = cur_text_section->data_offset;
1337 /* assemble the string with tcc internal assembler */
1338 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1340 cur_text_section->data_offset = ind;
1342 /* restore the current C token */
1343 next();
1345 cstr_free(&astr);
1346 nocode_wanted = saved_nocode_wanted;
1349 /********************************************************/
1350 #else
1351 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
1353 tcc_error("asm not supported");
1356 ST_FUNC void asm_instr(void)
1358 tcc_error("inline asm() not supported");
1361 ST_FUNC void asm_global_instr(void)
1363 tcc_error("inline asm() not supported");
1365 #endif /* CONFIG_TCC_ASM */