Fix spelling in help message
[tinycc.git] / tccasm.c
blob4d93b59dc956b6f5c483b9dad01987635541c0c4
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 (void) s1; /* not used */
30 snprintf(buf, sizeof(buf), "L..%u", n);
31 ts = tok_alloc(buf, strlen(buf));
32 return ts->tok;
35 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
36 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
37 static Sym sym_dot;
39 /* Return a symbol we can use inside the assembler, having name NAME.
40 The assembler symbol table is different from the C symbol table
41 (and the Sym members are used differently). But we must be able
42 to look up file-global C symbols from inside the assembler, e.g.
43 for global asm blocks to be able to refer to defined C symbols.
45 This routine gives back either an existing asm-internal
46 symbol, or a new one. In the latter case the new asm-internal
47 symbol is initialized with info from the C symbol table.
49 If CSYM is non-null we take symbol info from it, otherwise
50 we look up NAME in the C symbol table and use that. */
51 ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
53 Sym *sym = label_find(name);
54 if (!sym) {
55 sym = label_push(&tcc_state->asm_labels, name, 0);
56 sym->type.t = VT_VOID | VT_EXTERN;
57 if (!csym) {
58 csym = sym_find(name);
59 /* We might be called for an asm block from inside a C routine
60 and so might have local decls on the identifier stack. Search
61 for the first global one. */
62 while (csym && csym->scope)
63 csym = csym->prev_tok;
65 /* Now, if we have a defined global symbol copy over
66 section and offset. */
67 if (csym &&
68 ((csym->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST)) &&
69 csym->c) {
70 ElfW(Sym) *esym;
71 esym = &((ElfW(Sym) *)symtab_section->data)[csym->c];
72 sym->c = csym->c;
73 sym->r = esym->st_shndx;
74 sym->jnext = esym->st_value;
75 /* XXX can't yet store st_size anywhere. */
76 sym->type.t &= ~VT_EXTERN;
77 /* Mark that this asm symbol doesn't need to be fed back. */
78 sym->type.t |= VT_IMPORT;
81 return sym;
84 /* We do not use the C expression parser to handle symbols. Maybe the
85 C expression parser could be tweaked to do so. */
87 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
89 Sym *sym;
90 int op, label;
91 uint64_t n;
92 const char *p;
94 switch(tok) {
95 case TOK_PPNUM:
96 p = tokc.str.data;
97 n = strtoull(p, (char **)&p, 0);
98 if (*p == 'b' || *p == 'f') {
99 /* backward or forward label */
100 label = asm_get_local_label_name(s1, n);
101 sym = label_find(label);
102 if (*p == 'b') {
103 /* backward : find the last corresponding defined label */
104 if (sym && sym->r == 0)
105 sym = sym->prev_tok;
106 if (!sym)
107 tcc_error("local label '%d' not found backward", n);
108 } else {
109 /* forward */
110 if (!sym || sym->r) {
111 /* if the last label is defined, then define a new one */
112 sym = label_push(&s1->asm_labels, label, 0);
113 sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
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 = 0;
158 pe->sym = &sym_dot;
159 pe->pcrel = 0;
160 sym_dot.type.t = VT_VOID | VT_STATIC;
161 sym_dot.r = cur_text_section->sh_num;
162 sym_dot.jnext = ind;
163 next();
164 break;
165 default:
166 if (tok >= TOK_IDENT) {
167 /* label case : if the label was not found, add one */
168 sym = get_asm_sym(tok, NULL);
169 if (sym->r == SHN_ABS) {
170 /* if absolute symbol, no need to put a symbol value */
171 pe->v = sym->jnext;
172 pe->sym = NULL;
173 pe->pcrel = 0;
174 } else {
175 pe->v = 0;
176 pe->sym = sym;
177 pe->pcrel = 0;
179 next();
180 } else {
181 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
183 break;
187 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
189 int op;
190 ExprValue e2;
192 asm_expr_unary(s1, pe);
193 for(;;) {
194 op = tok;
195 if (op != '*' && op != '/' && op != '%' &&
196 op != TOK_SHL && op != TOK_SAR)
197 break;
198 next();
199 asm_expr_unary(s1, &e2);
200 if (pe->sym || e2.sym)
201 tcc_error("invalid operation with label");
202 switch(op) {
203 case '*':
204 pe->v *= e2.v;
205 break;
206 case '/':
207 if (e2.v == 0) {
208 div_error:
209 tcc_error("division by zero");
211 pe->v /= e2.v;
212 break;
213 case '%':
214 if (e2.v == 0)
215 goto div_error;
216 pe->v %= e2.v;
217 break;
218 case TOK_SHL:
219 pe->v <<= e2.v;
220 break;
221 default:
222 case TOK_SAR:
223 pe->v >>= e2.v;
224 break;
229 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
231 int op;
232 ExprValue e2;
234 asm_expr_prod(s1, pe);
235 for(;;) {
236 op = tok;
237 if (op != '&' && op != '|' && op != '^')
238 break;
239 next();
240 asm_expr_prod(s1, &e2);
241 if (pe->sym || e2.sym)
242 tcc_error("invalid operation with label");
243 switch(op) {
244 case '&':
245 pe->v &= e2.v;
246 break;
247 case '|':
248 pe->v |= e2.v;
249 break;
250 default:
251 case '^':
252 pe->v ^= e2.v;
253 break;
258 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
260 int op;
261 ExprValue e2;
263 asm_expr_logic(s1, pe);
264 for(;;) {
265 op = tok;
266 if (op != '+' && op != '-')
267 break;
268 next();
269 asm_expr_logic(s1, &e2);
270 if (op == '+') {
271 if (pe->sym != NULL && e2.sym != NULL)
272 goto cannot_relocate;
273 pe->v += e2.v;
274 if (pe->sym == NULL && e2.sym != NULL)
275 pe->sym = e2.sym;
276 } else {
277 pe->v -= e2.v;
278 /* NOTE: we are less powerful than gas in that case
279 because we store only one symbol in the expression */
280 if (!e2.sym) {
281 /* OK */
282 } else if (pe->sym == e2.sym) {
283 /* OK */
284 pe->sym = NULL; /* same symbols can be subtracted to NULL */
285 } else if (pe->sym && pe->sym->r == e2.sym->r && pe->sym->r != 0) {
286 /* we also accept defined symbols in the same section */
287 pe->v += pe->sym->jnext - e2.sym->jnext;
288 pe->sym = NULL;
289 } else if (e2.sym->r == cur_text_section->sh_num) {
290 /* When subtracting a defined symbol in current section
291 this actually makes the value PC-relative. */
292 pe->v -= e2.sym->jnext - ind - 4;
293 pe->pcrel = 1;
294 e2.sym = NULL;
295 } else {
296 cannot_relocate:
297 tcc_error("invalid operation with label");
303 static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
305 int op;
306 ExprValue e2;
308 asm_expr_sum(s1, pe);
309 for(;;) {
310 op = tok;
311 if (op != TOK_EQ && op != TOK_NE
312 && (op > TOK_GT || op < TOK_ULE))
313 break;
314 next();
315 asm_expr_sum(s1, &e2);
316 if (pe->sym || e2.sym)
317 tcc_error("invalid operation with label");
318 switch(op) {
319 case TOK_EQ:
320 pe->v = pe->v == e2.v;
321 break;
322 case TOK_NE:
323 pe->v = pe->v != e2.v;
324 break;
325 case TOK_LT:
326 pe->v = (int64_t)pe->v < (int64_t)e2.v;
327 break;
328 case TOK_GE:
329 pe->v = (int64_t)pe->v >= (int64_t)e2.v;
330 break;
331 case TOK_LE:
332 pe->v = (int64_t)pe->v <= (int64_t)e2.v;
333 break;
334 case TOK_GT:
335 pe->v = (int64_t)pe->v > (int64_t)e2.v;
336 break;
337 default:
338 break;
340 /* GAS compare results are -1/0 not 1/0. */
341 pe->v = -(int64_t)pe->v;
345 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
347 asm_expr_cmp(s1, pe);
350 ST_FUNC int asm_int_expr(TCCState *s1)
352 ExprValue e;
353 asm_expr(s1, &e);
354 if (e.sym)
355 expect("constant");
356 return e.v;
359 /* NOTE: the same name space as C labels is used to avoid using too
360 much memory when storing labels in TokenStrings */
361 static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
362 int sh_num, int value)
364 Sym *sym;
366 sym = label_find(label);
367 if (sym) {
368 /* A VT_EXTERN symbol, even if it has a section is considered
369 overridable. This is how we "define" .set targets. Real
370 definitions won't have VT_EXTERN set. */
371 if (sym->r && !(sym->type.t & VT_EXTERN)) {
372 /* the label is already defined */
373 if (!is_local) {
374 tcc_error("assembler label '%s' already defined",
375 get_tok_str(label, NULL));
376 } else {
377 /* redefinition of local labels is possible */
378 goto new_label;
381 } else {
382 new_label:
383 sym = label_push(&s1->asm_labels, label, 0);
384 /* If we need a symbol to hold a value, mark it as
385 tentative only (for .set). If this is for a real label
386 we'll remove VT_EXTERN. */
387 sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
389 sym->r = sh_num;
390 sym->jnext = value;
391 return sym;
394 static Sym* asm_new_label(TCCState *s1, int label, int is_local)
396 return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
399 /* Set the value of LABEL to that of some expression (possibly
400 involving other symbols). LABEL can be overwritten later still. */
401 static Sym* set_symbol(TCCState *s1, int label)
403 long n;
404 ExprValue e;
405 next();
406 asm_expr(s1, &e);
407 n = e.v;
408 if (e.sym)
409 n += e.sym->jnext;
410 return asm_new_label1(s1, label, 0, e.sym ? e.sym->r : SHN_ABS, n);
413 static void asm_free_labels(TCCState *st)
415 Sym *s, *s1;
416 Section *sec;
418 for(s = st->asm_labels; s != NULL; s = s1) {
419 s1 = s->prev;
420 /* define symbol value in object file */
421 s->type.t &= ~VT_EXTERN;
422 if (s->r && !(s->type.t & VT_IMPORT)) {
423 if (s->r == SHN_ABS)
424 sec = SECTION_ABS;
425 else
426 sec = st->sections[s->r];
427 put_extern_sym2(s, sec, s->jnext, 0, 0);
429 /* remove label */
430 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
431 sym_free(s);
433 st->asm_labels = NULL;
436 static void use_section1(TCCState *s1, Section *sec)
438 (void) s1; /* not used */
439 cur_text_section->data_offset = ind;
440 cur_text_section = sec;
441 ind = cur_text_section->data_offset;
444 static void use_section(TCCState *s1, const char *name)
446 Section *sec;
447 sec = find_section(s1, name);
448 use_section1(s1, sec);
451 static void push_section(TCCState *s1, const char *name)
453 Section *sec = find_section(s1, name);
454 sec->prev = cur_text_section;
455 use_section1(s1, sec);
458 static void pop_section(TCCState *s1)
460 Section *prev = cur_text_section->prev;
461 if (!prev)
462 tcc_error(".popsection without .pushsection");
463 cur_text_section->prev = NULL;
464 use_section1(s1, prev);
467 static void asm_parse_directive(TCCState *s1, int global)
469 int n, offset, v, size, tok1;
470 Section *sec;
471 uint8_t *ptr;
473 /* assembler directive */
474 sec = cur_text_section;
475 switch(tok) {
476 case TOK_ASMDIR_align:
477 case TOK_ASMDIR_balign:
478 case TOK_ASMDIR_p2align:
479 case TOK_ASMDIR_skip:
480 case TOK_ASMDIR_space:
481 tok1 = tok;
482 next();
483 n = asm_int_expr(s1);
484 if (tok1 == TOK_ASMDIR_p2align)
486 if (n < 0 || n > 30)
487 tcc_error("invalid p2align, must be between 0 and 30");
488 n = 1 << n;
489 tok1 = TOK_ASMDIR_align;
491 if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
492 if (n < 0 || (n & (n-1)) != 0)
493 tcc_error("alignment must be a positive power of two");
494 offset = (ind + n - 1) & -n;
495 size = offset - ind;
496 /* the section must have a compatible alignment */
497 if (sec->sh_addralign < n)
498 sec->sh_addralign = n;
499 } else {
500 if (n < 0)
501 n = 0;
502 size = n;
504 v = 0;
505 if (tok == ',') {
506 next();
507 v = asm_int_expr(s1);
509 zero_pad:
510 if (sec->sh_type != SHT_NOBITS) {
511 sec->data_offset = ind;
512 ptr = section_ptr_add(sec, size);
513 memset(ptr, v, size);
515 ind += size;
516 break;
517 case TOK_ASMDIR_quad:
518 #ifdef TCC_TARGET_X86_64
519 size = 8;
520 goto asm_data;
521 #else
522 next();
523 for(;;) {
524 uint64_t vl;
525 const char *p;
527 p = tokc.str.data;
528 if (tok != TOK_PPNUM) {
529 error_constant:
530 tcc_error("64 bit constant");
532 vl = strtoll(p, (char **)&p, 0);
533 if (*p != '\0')
534 goto error_constant;
535 next();
536 if (sec->sh_type != SHT_NOBITS) {
537 /* XXX: endianness */
538 gen_le32(vl);
539 gen_le32(vl >> 32);
540 } else {
541 ind += 8;
543 if (tok != ',')
544 break;
545 next();
547 break;
548 #endif
549 case TOK_ASMDIR_byte:
550 size = 1;
551 goto asm_data;
552 case TOK_ASMDIR_word:
553 case TOK_ASMDIR_short:
554 size = 2;
555 goto asm_data;
556 case TOK_ASMDIR_long:
557 case TOK_ASMDIR_int:
558 size = 4;
559 asm_data:
560 next();
561 for(;;) {
562 ExprValue e;
563 asm_expr(s1, &e);
564 if (sec->sh_type != SHT_NOBITS) {
565 if (size == 4) {
566 gen_expr32(&e);
567 #ifdef TCC_TARGET_X86_64
568 } else if (size == 8) {
569 gen_expr64(&e);
570 #endif
571 } else {
572 if (e.sym)
573 expect("constant");
574 if (size == 1)
575 g(e.v);
576 else
577 gen_le16(e.v);
579 } else {
580 ind += size;
582 if (tok != ',')
583 break;
584 next();
586 break;
587 case TOK_ASMDIR_fill:
589 int repeat, size, val, i, j;
590 uint8_t repeat_buf[8];
591 next();
592 repeat = asm_int_expr(s1);
593 if (repeat < 0) {
594 tcc_error("repeat < 0; .fill ignored");
595 break;
597 size = 1;
598 val = 0;
599 if (tok == ',') {
600 next();
601 size = asm_int_expr(s1);
602 if (size < 0) {
603 tcc_error("size < 0; .fill ignored");
604 break;
606 if (size > 8)
607 size = 8;
608 if (tok == ',') {
609 next();
610 val = asm_int_expr(s1);
613 /* XXX: endianness */
614 repeat_buf[0] = val;
615 repeat_buf[1] = val >> 8;
616 repeat_buf[2] = val >> 16;
617 repeat_buf[3] = val >> 24;
618 repeat_buf[4] = 0;
619 repeat_buf[5] = 0;
620 repeat_buf[6] = 0;
621 repeat_buf[7] = 0;
622 for(i = 0; i < repeat; i++) {
623 for(j = 0; j < size; j++) {
624 g(repeat_buf[j]);
628 break;
629 case TOK_ASMDIR_rept:
631 int repeat;
632 TokenString *init_str;
633 ParseState saved_parse_state = {0};
634 next();
635 repeat = asm_int_expr(s1);
636 init_str = tok_str_alloc();
637 next();
638 while ((tok != TOK_ASMDIR_endr) && (tok != CH_EOF)) {
639 tok_str_add_tok(init_str);
640 next();
642 if (tok == CH_EOF) tcc_error("we at end of file, .endr not found");
643 next();
644 tok_str_add(init_str, -1);
645 tok_str_add(init_str, 0);
646 save_parse_state(&saved_parse_state);
647 begin_macro(init_str, 1);
648 while (repeat-- > 0) {
649 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
650 global);
651 macro_ptr = init_str->str;
653 end_macro();
654 restore_parse_state(&saved_parse_state);
655 break;
657 case TOK_ASMDIR_org:
659 unsigned long n;
660 ExprValue e;
661 next();
662 asm_expr(s1, &e);
663 n = e.v;
664 if (e.sym) {
665 if (e.sym->r != cur_text_section->sh_num)
666 expect("constant or same-section symbol");
667 n += e.sym->jnext;
669 if (n < ind)
670 tcc_error("attempt to .org backwards");
671 v = 0;
672 size = n - ind;
673 goto zero_pad;
675 break;
676 case TOK_ASMDIR_set:
677 next();
678 tok1 = tok;
679 next();
680 /* Also accept '.set stuff', but don't do anything with this.
681 It's used in GAS to set various features like '.set mips16'. */
682 if (tok == ',')
683 set_symbol(s1, tok1);
684 break;
685 case TOK_ASMDIR_globl:
686 case TOK_ASMDIR_global:
687 case TOK_ASMDIR_weak:
688 case TOK_ASMDIR_hidden:
689 tok1 = tok;
690 do {
691 Sym *sym;
693 next();
694 sym = get_asm_sym(tok, NULL);
695 if (tok1 != TOK_ASMDIR_hidden)
696 sym->type.t &= ~VT_STATIC;
697 if (tok1 == TOK_ASMDIR_weak)
698 sym->type.t |= VT_WEAK;
699 else if (tok1 == TOK_ASMDIR_hidden)
700 sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
701 next();
702 } while (tok == ',');
703 break;
704 case TOK_ASMDIR_string:
705 case TOK_ASMDIR_ascii:
706 case TOK_ASMDIR_asciz:
708 const uint8_t *p;
709 int i, size, t;
711 t = tok;
712 next();
713 for(;;) {
714 if (tok != TOK_STR)
715 expect("string constant");
716 p = tokc.str.data;
717 size = tokc.str.size;
718 if (t == TOK_ASMDIR_ascii && size > 0)
719 size--;
720 for(i = 0; i < size; i++)
721 g(p[i]);
722 next();
723 if (tok == ',') {
724 next();
725 } else if (tok != TOK_STR) {
726 break;
730 break;
731 case TOK_ASMDIR_text:
732 case TOK_ASMDIR_data:
733 case TOK_ASMDIR_bss:
735 char sname[64];
736 tok1 = tok;
737 n = 0;
738 next();
739 if (tok != ';' && tok != TOK_LINEFEED) {
740 n = asm_int_expr(s1);
741 next();
743 if (n)
744 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
745 else
746 sprintf(sname, "%s", get_tok_str(tok1, NULL));
747 use_section(s1, sname);
749 break;
750 case TOK_ASMDIR_file:
752 char filename[512];
754 filename[0] = '\0';
755 next();
757 if (tok == TOK_STR)
758 pstrcat(filename, sizeof(filename), tokc.str.data);
759 else
760 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
762 if (s1->warn_unsupported)
763 tcc_warning("ignoring .file %s", filename);
765 next();
767 break;
768 case TOK_ASMDIR_ident:
770 char ident[256];
772 ident[0] = '\0';
773 next();
775 if (tok == TOK_STR)
776 pstrcat(ident, sizeof(ident), tokc.str.data);
777 else
778 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
780 if (s1->warn_unsupported)
781 tcc_warning("ignoring .ident %s", ident);
783 next();
785 break;
786 case TOK_ASMDIR_size:
788 Sym *sym;
790 next();
791 sym = label_find(tok);
792 if (!sym) {
793 tcc_error("label not found: %s", get_tok_str(tok, NULL));
796 /* XXX .size name,label2-label1 */
797 if (s1->warn_unsupported)
798 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
800 next();
801 skip(',');
802 while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
803 next();
806 break;
807 case TOK_ASMDIR_type:
809 Sym *sym;
810 const char *newtype;
812 next();
813 sym = get_asm_sym(tok, NULL);
814 next();
815 skip(',');
816 if (tok == TOK_STR) {
817 newtype = tokc.str.data;
818 } else {
819 if (tok == '@' || tok == '%')
820 next();
821 newtype = get_tok_str(tok, NULL);
824 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
825 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
827 else if (s1->warn_unsupported)
828 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
829 get_tok_str(sym->v, NULL), sym->type.t, newtype);
831 next();
833 break;
834 case TOK_ASMDIR_pushsection:
835 case TOK_ASMDIR_section:
837 char sname[256];
838 int old_nb_section = s1->nb_sections;
840 tok1 = tok;
841 /* XXX: support more options */
842 next();
843 sname[0] = '\0';
844 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
845 if (tok == TOK_STR)
846 pstrcat(sname, sizeof(sname), tokc.str.data);
847 else
848 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
849 next();
851 if (tok == ',') {
852 /* skip section options */
853 next();
854 if (tok != TOK_STR)
855 expect("string constant");
856 next();
857 if (tok == ',') {
858 next();
859 if (tok == '@' || tok == '%')
860 next();
861 next();
864 last_text_section = cur_text_section;
865 if (tok1 == TOK_ASMDIR_section)
866 use_section(s1, sname);
867 else
868 push_section(s1, sname);
869 /* If we just allocated a new section reset its alignment to
870 1. new_section normally acts for GCC compatibility and
871 sets alignment to PTR_SIZE. The assembler behaves different. */
872 if (old_nb_section != s1->nb_sections)
873 cur_text_section->sh_addralign = 1;
875 break;
876 case TOK_ASMDIR_previous:
878 Section *sec;
879 next();
880 if (!last_text_section)
881 tcc_error("no previous section referenced");
882 sec = cur_text_section;
883 use_section1(s1, last_text_section);
884 last_text_section = sec;
886 break;
887 case TOK_ASMDIR_popsection:
888 next();
889 pop_section(s1);
890 break;
891 #ifdef TCC_TARGET_I386
892 case TOK_ASMDIR_code16:
894 next();
895 s1->seg_size = 16;
897 break;
898 case TOK_ASMDIR_code32:
900 next();
901 s1->seg_size = 32;
903 break;
904 #endif
905 #ifdef TCC_TARGET_X86_64
906 /* added for compatibility with GAS */
907 case TOK_ASMDIR_code64:
908 next();
909 break;
910 #endif
911 default:
912 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
913 break;
918 /* assemble a file */
919 static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
921 int opcode;
923 /* XXX: undefine C labels */
925 ch = file->buf_ptr[0];
926 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
927 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
928 set_idnum('.', IS_ID);
929 if (do_preprocess)
930 parse_flags |= PARSE_FLAG_PREPROCESS;
931 for(;;) {
932 next();
933 if (tok == TOK_EOF)
934 break;
935 /* generate line number info */
936 if (global && s1->do_debug)
937 tcc_debug_line(s1);
938 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
939 redo:
940 if (tok == '#') {
941 /* horrible gas comment */
942 while (tok != TOK_LINEFEED)
943 next();
944 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
945 asm_parse_directive(s1, global);
946 } else if (tok == TOK_PPNUM) {
947 Sym *sym;
948 const char *p;
949 int n;
950 p = tokc.str.data;
951 n = strtoul(p, (char **)&p, 10);
952 if (*p != '\0')
953 expect("':'");
954 /* new local label */
955 sym = asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
956 /* Remove the marker for tentative definitions. */
957 sym->type.t &= ~VT_EXTERN;
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 /* handle "extern void vide(void); __asm__("vide: ret");" as
967 "__asm__("globl vide\nvide: ret");" */
968 Sym *sym = sym_find(opcode);
969 if (sym && (sym->type.t & VT_EXTERN) && global) {
970 sym = label_find(opcode);
971 if (!sym) {
972 sym = label_push(&s1->asm_labels, opcode, 0);
973 sym->type.t = VT_VOID | VT_EXTERN;
976 /* new label */
977 sym = asm_new_label(s1, opcode, 0);
978 sym->type.t &= ~VT_EXTERN;
979 next();
980 goto redo;
981 } else if (tok == '=') {
982 set_symbol(s1, opcode);
983 goto redo;
984 } else {
985 asm_opcode(s1, opcode);
988 /* end of line */
989 if (tok != ';' && tok != TOK_LINEFEED)
990 expect("end of line");
991 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
994 asm_free_labels(s1);
995 return 0;
998 /* Assemble the current file */
999 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
1001 Sym *define_start;
1002 int ret;
1004 define_start = define_stack;
1005 preprocess_start(s1);
1006 tcc_debug_start(s1);
1008 /* default section is text */
1009 cur_text_section = text_section;
1010 ind = cur_text_section->data_offset;
1011 nocode_wanted = 0;
1013 ret = tcc_assemble_internal(s1, do_preprocess, 1);
1015 cur_text_section->data_offset = ind;
1017 tcc_debug_end(s1);
1018 free_defines(define_start);
1019 return ret;
1022 /********************************************************************/
1023 /* GCC inline asm support */
1025 /* assemble the string 'str' in the current C compilation unit without
1026 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1027 end */
1028 static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
1030 int saved_parse_flags;
1031 const int *saved_macro_ptr;
1033 saved_parse_flags = parse_flags;
1034 saved_macro_ptr = macro_ptr;
1036 tcc_open_bf(s1, ":asm:", len);
1037 memcpy(file->buffer, str, len);
1039 macro_ptr = NULL;
1040 tcc_assemble_internal(s1, 0, global);
1041 tcc_close();
1043 parse_flags = saved_parse_flags;
1044 set_idnum('.', (parse_flags & PARSE_FLAG_ASM_FILE) ? IS_ID : 0);
1045 macro_ptr = saved_macro_ptr;
1048 /* find a constraint by its number or id (gcc 3 extended
1049 syntax). return -1 if not found. Return in *pp in char after the
1050 constraint */
1051 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
1052 const char *name, const char **pp)
1054 int index;
1055 TokenSym *ts;
1056 const char *p;
1058 if (isnum(*name)) {
1059 index = 0;
1060 while (isnum(*name)) {
1061 index = (index * 10) + (*name) - '0';
1062 name++;
1064 if ((unsigned)index >= nb_operands)
1065 index = -1;
1066 } else if (*name == '[') {
1067 name++;
1068 p = strchr(name, ']');
1069 if (p) {
1070 ts = tok_alloc(name, p - name);
1071 for(index = 0; index < nb_operands; index++) {
1072 if (operands[index].id == ts->tok)
1073 goto found;
1075 index = -1;
1076 found:
1077 name = p + 1;
1078 } else {
1079 index = -1;
1081 } else {
1082 index = -1;
1084 if (pp)
1085 *pp = name;
1086 return index;
1089 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
1090 int nb_outputs,
1091 CString *out_str, CString *in_str)
1093 int c, index, modifier;
1094 const char *str;
1095 ASMOperand *op;
1096 SValue sv;
1098 cstr_new(out_str);
1099 str = in_str->data;
1100 for(;;) {
1101 c = *str++;
1102 if (c == '%') {
1103 if (*str == '%') {
1104 str++;
1105 goto add_char;
1107 modifier = 0;
1108 if (*str == 'c' || *str == 'n' ||
1109 *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
1110 *str == 'q' ||
1111 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1112 and make literal operands not be decorated with '$'. */
1113 *str == 'P')
1114 modifier = *str++;
1115 index = find_constraint(operands, nb_operands, str, &str);
1116 if (index < 0)
1117 tcc_error("invalid operand reference after %%");
1118 op = &operands[index];
1119 sv = *op->vt;
1120 if (op->reg >= 0) {
1121 sv.r = op->reg;
1122 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
1123 sv.r |= VT_LVAL;
1125 subst_asm_operand(out_str, &sv, modifier);
1126 } else {
1127 add_char:
1128 cstr_ccat(out_str, c);
1129 if (c == '\0')
1130 break;
1136 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1137 int is_output)
1139 ASMOperand *op;
1140 int nb_operands;
1142 if (tok != ':') {
1143 nb_operands = *nb_operands_ptr;
1144 for(;;) {
1145 CString astr;
1146 if (nb_operands >= MAX_ASM_OPERANDS)
1147 tcc_error("too many asm operands");
1148 op = &operands[nb_operands++];
1149 op->id = 0;
1150 if (tok == '[') {
1151 next();
1152 if (tok < TOK_IDENT)
1153 expect("identifier");
1154 op->id = tok;
1155 next();
1156 skip(']');
1158 parse_mult_str(&astr, "string constant");
1159 op->constraint = tcc_malloc(astr.size);
1160 strcpy(op->constraint, astr.data);
1161 cstr_free(&astr);
1162 skip('(');
1163 gexpr();
1164 if (is_output) {
1165 if (!(vtop->type.t & VT_ARRAY))
1166 test_lvalue();
1167 } else {
1168 /* we want to avoid LLOCAL case, except when the 'm'
1169 constraint is used. Note that it may come from
1170 register storage, so we need to convert (reg)
1171 case */
1172 if ((vtop->r & VT_LVAL) &&
1173 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1174 (vtop->r & VT_VALMASK) < VT_CONST) &&
1175 !strchr(op->constraint, 'm')) {
1176 gv(RC_INT);
1179 op->vt = vtop;
1180 skip(')');
1181 if (tok == ',') {
1182 next();
1183 } else {
1184 break;
1187 *nb_operands_ptr = nb_operands;
1191 /* parse the GCC asm() instruction */
1192 ST_FUNC void asm_instr(void)
1194 CString astr, astr1;
1195 ASMOperand operands[MAX_ASM_OPERANDS];
1196 int nb_outputs, nb_operands, i, must_subst, out_reg;
1197 uint8_t clobber_regs[NB_ASM_REGS];
1199 next();
1200 /* since we always generate the asm() instruction, we can ignore
1201 volatile */
1202 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1203 next();
1205 parse_asm_str(&astr);
1206 nb_operands = 0;
1207 nb_outputs = 0;
1208 must_subst = 0;
1209 memset(clobber_regs, 0, sizeof(clobber_regs));
1210 if (tok == ':') {
1211 next();
1212 must_subst = 1;
1213 /* output args */
1214 parse_asm_operands(operands, &nb_operands, 1);
1215 nb_outputs = nb_operands;
1216 if (tok == ':') {
1217 next();
1218 if (tok != ')') {
1219 /* input args */
1220 parse_asm_operands(operands, &nb_operands, 0);
1221 if (tok == ':') {
1222 /* clobber list */
1223 /* XXX: handle registers */
1224 next();
1225 for(;;) {
1226 if (tok != TOK_STR)
1227 expect("string constant");
1228 asm_clobber(clobber_regs, tokc.str.data);
1229 next();
1230 if (tok == ',') {
1231 next();
1232 } else {
1233 break;
1240 skip(')');
1241 /* NOTE: we do not eat the ';' so that we can restore the current
1242 token after the assembler parsing */
1243 if (tok != ';')
1244 expect("';'");
1246 /* save all values in the memory */
1247 save_regs(0);
1249 /* compute constraints */
1250 asm_compute_constraints(operands, nb_operands, nb_outputs,
1251 clobber_regs, &out_reg);
1253 /* substitute the operands in the asm string. No substitution is
1254 done if no operands (GCC behaviour) */
1255 #ifdef ASM_DEBUG
1256 printf("asm: \"%s\"\n", (char *)astr.data);
1257 #endif
1258 if (must_subst) {
1259 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1260 cstr_free(&astr);
1261 } else {
1262 astr1 = astr;
1264 #ifdef ASM_DEBUG
1265 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1266 #endif
1268 /* generate loads */
1269 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1270 clobber_regs, out_reg);
1272 /* assemble the string with tcc internal assembler */
1273 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1275 /* restore the current C token */
1276 next();
1278 /* store the output values if needed */
1279 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1280 clobber_regs, out_reg);
1282 /* free everything */
1283 for(i=0;i<nb_operands;i++) {
1284 ASMOperand *op;
1285 op = &operands[i];
1286 tcc_free(op->constraint);
1287 vpop();
1289 cstr_free(&astr1);
1292 ST_FUNC void asm_global_instr(void)
1294 CString astr;
1295 int saved_nocode_wanted = nocode_wanted;
1297 /* Global asm blocks are always emitted. */
1298 nocode_wanted = 0;
1299 next();
1300 parse_asm_str(&astr);
1301 skip(')');
1302 /* NOTE: we do not eat the ';' so that we can restore the current
1303 token after the assembler parsing */
1304 if (tok != ';')
1305 expect("';'");
1307 #ifdef ASM_DEBUG
1308 printf("asm_global: \"%s\"\n", (char *)astr.data);
1309 #endif
1310 cur_text_section = text_section;
1311 ind = cur_text_section->data_offset;
1313 /* assemble the string with tcc internal assembler */
1314 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1316 cur_text_section->data_offset = ind;
1318 /* restore the current C token */
1319 next();
1321 cstr_free(&astr);
1322 nocode_wanted = saved_nocode_wanted;
1324 #endif /* CONFIG_TCC_ASM */