TOK_PPNUM in asm (Edmund Grimley Evans version)
[tinycc.git] / tccasm.c
blob3fd69f780d01d2f33936817a590047e1c49b1541
1 /*
2 * GAS like assembler for TCC
3 *
4 * Copyright (c) 2001-2004 Fabrice Bellard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "tcc.h"
22 #ifdef CONFIG_TCC_ASM
24 ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
26 char buf[64];
27 TokenSym *ts;
29 snprintf(buf, sizeof(buf), "L..%u", n);
30 ts = tok_alloc(buf, strlen(buf));
31 return ts->tok;
34 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
35 static int tcc_assemble_internal(TCCState *s1, int do_preprocess);
36 static Sym sym_dot;
38 /* We do not use the C expression parser to handle symbols. Maybe the
39 C expression parser could be tweaked to do so. */
41 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
43 Sym *sym;
44 int op, n, label;
45 const char *p;
47 switch(tok) {
48 case TOK_PPNUM:
49 p = tokc.str.data;
50 n = strtoul(p, (char **)&p, 0);
51 if (*p == 'b' || *p == 'f') {
52 /* backward or forward label */
53 label = asm_get_local_label_name(s1, n);
54 sym = label_find(label);
55 if (*p == 'b') {
56 /* backward : find the last corresponding defined label */
57 if (sym && sym->r == 0)
58 sym = sym->prev_tok;
59 if (!sym)
60 tcc_error("local label '%d' not found backward", n);
61 } else {
62 /* forward */
63 if (!sym || sym->r) {
64 /* if the last label is defined, then define a new one */
65 sym = label_push(&s1->asm_labels, label, 0);
66 sym->type.t = VT_STATIC | VT_VOID;
69 pe->v = 0;
70 pe->sym = sym;
71 } else if (*p == '\0') {
72 pe->v = n;
73 pe->sym = NULL;
74 } else {
75 tcc_error("invalid number syntax");
77 next();
78 break;
79 case '+':
80 next();
81 asm_expr_unary(s1, pe);
82 break;
83 case '-':
84 case '~':
85 op = tok;
86 next();
87 asm_expr_unary(s1, pe);
88 if (pe->sym)
89 tcc_error("invalid operation with label");
90 if (op == '-')
91 pe->v = -pe->v;
92 else
93 pe->v = ~pe->v;
94 break;
95 case TOK_CCHAR:
96 case TOK_LCHAR:
97 pe->v = tokc.i;
98 pe->sym = NULL;
99 next();
100 break;
101 case '(':
102 next();
103 asm_expr(s1, pe);
104 skip(')');
105 break;
106 case '.':
107 pe->v = 0;
108 pe->sym = &sym_dot;
109 sym_dot.type.t = VT_VOID | VT_STATIC;
110 sym_dot.r = cur_text_section->sh_num;
111 sym_dot.jnext = ind;
112 next();
113 break;
114 default:
115 if (tok >= TOK_IDENT) {
116 /* label case : if the label was not found, add one */
117 sym = label_find(tok);
118 if (!sym) {
119 sym = label_push(&s1->asm_labels, tok, 0);
120 /* NOTE: by default, the symbol is global */
121 sym->type.t = VT_VOID;
123 if (sym->r == SHN_ABS) {
124 /* if absolute symbol, no need to put a symbol value */
125 pe->v = sym->jnext;
126 pe->sym = NULL;
127 } else {
128 pe->v = 0;
129 pe->sym = sym;
131 next();
132 } else {
133 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
135 break;
139 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
141 int op;
142 ExprValue e2;
144 asm_expr_unary(s1, pe);
145 for(;;) {
146 op = tok;
147 if (op != '*' && op != '/' && op != '%' &&
148 op != TOK_SHL && op != TOK_SAR)
149 break;
150 next();
151 asm_expr_unary(s1, &e2);
152 if (pe->sym || e2.sym)
153 tcc_error("invalid operation with label");
154 switch(op) {
155 case '*':
156 pe->v *= e2.v;
157 break;
158 case '/':
159 if (e2.v == 0) {
160 div_error:
161 tcc_error("division by zero");
163 pe->v /= e2.v;
164 break;
165 case '%':
166 if (e2.v == 0)
167 goto div_error;
168 pe->v %= e2.v;
169 break;
170 case TOK_SHL:
171 pe->v <<= e2.v;
172 break;
173 default:
174 case TOK_SAR:
175 pe->v >>= e2.v;
176 break;
181 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
183 int op;
184 ExprValue e2;
186 asm_expr_prod(s1, pe);
187 for(;;) {
188 op = tok;
189 if (op != '&' && op != '|' && op != '^')
190 break;
191 next();
192 asm_expr_prod(s1, &e2);
193 if (pe->sym || e2.sym)
194 tcc_error("invalid operation with label");
195 switch(op) {
196 case '&':
197 pe->v &= e2.v;
198 break;
199 case '|':
200 pe->v |= e2.v;
201 break;
202 default:
203 case '^':
204 pe->v ^= e2.v;
205 break;
210 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
212 int op;
213 ExprValue e2;
215 asm_expr_logic(s1, pe);
216 for(;;) {
217 op = tok;
218 if (op != '+' && op != '-')
219 break;
220 next();
221 asm_expr_logic(s1, &e2);
222 if (op == '+') {
223 if (pe->sym != NULL && e2.sym != NULL)
224 goto cannot_relocate;
225 pe->v += e2.v;
226 if (pe->sym == NULL && e2.sym != NULL)
227 pe->sym = e2.sym;
228 } else {
229 pe->v -= e2.v;
230 /* NOTE: we are less powerful than gas in that case
231 because we store only one symbol in the expression */
232 if (!pe->sym && !e2.sym) {
233 /* OK */
234 } else if (pe->sym && !e2.sym) {
235 /* OK */
236 } else if (pe->sym && e2.sym) {
237 if (pe->sym == e2.sym) {
238 /* OK */
239 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
240 /* we also accept defined symbols in the same section */
241 pe->v += pe->sym->jnext - e2.sym->jnext;
242 } else {
243 goto cannot_relocate;
245 pe->sym = NULL; /* same symbols can be subtracted to NULL */
246 } else {
247 cannot_relocate:
248 tcc_error("invalid operation with label");
254 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
256 asm_expr_sum(s1, pe);
259 ST_FUNC int asm_int_expr(TCCState *s1)
261 ExprValue e;
262 asm_expr(s1, &e);
263 if (e.sym)
264 expect("constant");
265 return e.v;
268 /* NOTE: the same name space as C labels is used to avoid using too
269 much memory when storing labels in TokenStrings */
270 static void asm_new_label1(TCCState *s1, int label, int is_local,
271 int sh_num, int value)
273 Sym *sym;
275 sym = label_find(label);
276 if (sym) {
277 if (sym->r) {
278 /* the label is already defined */
279 if (!is_local) {
280 tcc_error("assembler label '%s' already defined",
281 get_tok_str(label, NULL));
282 } else {
283 /* redefinition of local labels is possible */
284 goto new_label;
287 } else {
288 new_label:
289 sym = label_push(&s1->asm_labels, label, 0);
290 sym->type.t = VT_STATIC | VT_VOID;
292 sym->r = sh_num;
293 sym->jnext = value;
296 static void asm_new_label(TCCState *s1, int label, int is_local)
298 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
301 static void asm_free_labels(TCCState *st)
303 Sym *s, *s1;
304 Section *sec;
306 for(s = st->asm_labels; s != NULL; s = s1) {
307 s1 = s->prev;
308 /* define symbol value in object file */
309 if (s->r) {
310 if (s->r == SHN_ABS)
311 sec = SECTION_ABS;
312 else
313 sec = st->sections[s->r];
314 put_extern_sym2(s, sec, s->jnext, 0, 0);
316 /* remove label */
317 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
318 sym_free(s);
320 st->asm_labels = NULL;
323 static void use_section1(TCCState *s1, Section *sec)
325 cur_text_section->data_offset = ind;
326 cur_text_section = sec;
327 ind = cur_text_section->data_offset;
330 static void use_section(TCCState *s1, const char *name)
332 Section *sec;
333 sec = find_section(s1, name);
334 use_section1(s1, sec);
337 static void asm_parse_directive(TCCState *s1)
339 int n, offset, v, size, tok1;
340 Section *sec;
341 uint8_t *ptr;
343 /* assembler directive */
344 sec = cur_text_section;
345 switch(tok) {
346 case TOK_ASMDIR_align:
347 case TOK_ASMDIR_p2align:
348 case TOK_ASMDIR_skip:
349 case TOK_ASMDIR_space:
350 tok1 = tok;
351 next();
352 n = asm_int_expr(s1);
353 if (tok1 == TOK_ASMDIR_p2align)
355 if (n < 0 || n > 30)
356 tcc_error("invalid p2align, must be between 0 and 30");
357 n = 1 << n;
358 tok1 = TOK_ASMDIR_align;
360 if (tok1 == TOK_ASMDIR_align) {
361 if (n < 0 || (n & (n-1)) != 0)
362 tcc_error("alignment must be a positive power of two");
363 offset = (ind + n - 1) & -n;
364 size = offset - ind;
365 /* the section must have a compatible alignment */
366 if (sec->sh_addralign < n)
367 sec->sh_addralign = n;
368 } else {
369 size = n;
371 v = 0;
372 if (tok == ',') {
373 next();
374 v = asm_int_expr(s1);
376 zero_pad:
377 if (sec->sh_type != SHT_NOBITS) {
378 sec->data_offset = ind;
379 ptr = section_ptr_add(sec, size);
380 memset(ptr, v, size);
382 ind += size;
383 break;
384 case TOK_ASMDIR_quad:
385 next();
386 for(;;) {
387 uint64_t vl;
388 const char *p;
390 p = tokc.str.data;
391 if (tok != TOK_PPNUM) {
392 error_constant:
393 tcc_error("64 bit constant");
395 vl = strtoll(p, (char **)&p, 0);
396 if (*p != '\0')
397 goto error_constant;
398 next();
399 if (sec->sh_type != SHT_NOBITS) {
400 /* XXX: endianness */
401 gen_le32(vl);
402 gen_le32(vl >> 32);
403 } else {
404 ind += 8;
406 if (tok != ',')
407 break;
408 next();
410 break;
411 case TOK_ASMDIR_byte:
412 size = 1;
413 goto asm_data;
414 case TOK_ASMDIR_word:
415 case TOK_ASMDIR_short:
416 size = 2;
417 goto asm_data;
418 case TOK_ASMDIR_long:
419 case TOK_ASMDIR_int:
420 size = 4;
421 asm_data:
422 next();
423 for(;;) {
424 ExprValue e;
425 asm_expr(s1, &e);
426 if (sec->sh_type != SHT_NOBITS) {
427 if (size == 4) {
428 gen_expr32(&e);
429 } else {
430 if (e.sym)
431 expect("constant");
432 if (size == 1)
433 g(e.v);
434 else
435 gen_le16(e.v);
437 } else {
438 ind += size;
440 if (tok != ',')
441 break;
442 next();
444 break;
445 case TOK_ASMDIR_fill:
447 int repeat, size, val, i, j;
448 uint8_t repeat_buf[8];
449 next();
450 repeat = asm_int_expr(s1);
451 if (repeat < 0) {
452 tcc_error("repeat < 0; .fill ignored");
453 break;
455 size = 1;
456 val = 0;
457 if (tok == ',') {
458 next();
459 size = asm_int_expr(s1);
460 if (size < 0) {
461 tcc_error("size < 0; .fill ignored");
462 break;
464 if (size > 8)
465 size = 8;
466 if (tok == ',') {
467 next();
468 val = asm_int_expr(s1);
471 /* XXX: endianness */
472 repeat_buf[0] = val;
473 repeat_buf[1] = val >> 8;
474 repeat_buf[2] = val >> 16;
475 repeat_buf[3] = val >> 24;
476 repeat_buf[4] = 0;
477 repeat_buf[5] = 0;
478 repeat_buf[6] = 0;
479 repeat_buf[7] = 0;
480 for(i = 0; i < repeat; i++) {
481 for(j = 0; j < size; j++) {
482 g(repeat_buf[j]);
486 break;
487 case TOK_ASMDIR_rept:
489 int repeat;
490 TokenString init_str;
491 ParseState saved_parse_state = {0};
492 next();
493 repeat = asm_int_expr(s1);
494 tok_str_new(&init_str);
495 next();
496 while ((tok != TOK_ASMDIR_endr) && (tok != CH_EOF)) {
497 tok_str_add_tok(&init_str);
498 next();
500 if (tok == CH_EOF) tcc_error("we at end of file, .endr not found");
501 next();
502 tok_str_add(&init_str, -1);
503 tok_str_add(&init_str, 0);
504 save_parse_state(&saved_parse_state);
505 begin_macro(&init_str, 0);
506 while (repeat-- > 0) {
507 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS));
508 macro_ptr = init_str.str;
510 end_macro();
511 restore_parse_state(&saved_parse_state);
512 break;
514 case TOK_ASMDIR_org:
516 unsigned long n;
517 next();
518 /* XXX: handle section symbols too */
519 n = asm_int_expr(s1);
520 if (n < ind)
521 tcc_error("attempt to .org backwards");
522 v = 0;
523 size = n - ind;
524 goto zero_pad;
526 break;
527 case TOK_ASMDIR_globl:
528 case TOK_ASMDIR_global:
529 case TOK_ASMDIR_weak:
530 case TOK_ASMDIR_hidden:
531 tok1 = tok;
532 do {
533 Sym *sym;
535 next();
536 sym = label_find(tok);
537 if (!sym) {
538 sym = label_push(&s1->asm_labels, tok, 0);
539 sym->type.t = VT_VOID;
541 if (tok1 != TOK_ASMDIR_hidden)
542 sym->type.t &= ~VT_STATIC;
543 if (tok1 == TOK_ASMDIR_weak)
544 sym->type.t |= VT_WEAK;
545 else if (tok1 == TOK_ASMDIR_hidden)
546 sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
547 next();
548 } while (tok == ',');
549 break;
550 case TOK_ASMDIR_string:
551 case TOK_ASMDIR_ascii:
552 case TOK_ASMDIR_asciz:
554 const uint8_t *p;
555 int i, size, t;
557 t = tok;
558 next();
559 for(;;) {
560 if (tok != TOK_STR)
561 expect("string constant");
562 p = tokc.str.data;
563 size = tokc.str.size;
564 if (t == TOK_ASMDIR_ascii && size > 0)
565 size--;
566 for(i = 0; i < size; i++)
567 g(p[i]);
568 next();
569 if (tok == ',') {
570 next();
571 } else if (tok != TOK_STR) {
572 break;
576 break;
577 case TOK_ASMDIR_text:
578 case TOK_ASMDIR_data:
579 case TOK_ASMDIR_bss:
581 char sname[64];
582 tok1 = tok;
583 n = 0;
584 next();
585 if (tok != ';' && tok != TOK_LINEFEED) {
586 n = asm_int_expr(s1);
587 next();
589 if (n)
590 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
591 else
592 sprintf(sname, "%s", get_tok_str(tok1, NULL));
593 use_section(s1, sname);
595 break;
596 case TOK_ASMDIR_file:
598 char filename[512];
600 filename[0] = '\0';
601 next();
603 if (tok == TOK_STR)
604 pstrcat(filename, sizeof(filename), tokc.str.data);
605 else
606 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
608 if (s1->warn_unsupported)
609 tcc_warning("ignoring .file %s", filename);
611 next();
613 break;
614 case TOK_ASMDIR_ident:
616 char ident[256];
618 ident[0] = '\0';
619 next();
621 if (tok == TOK_STR)
622 pstrcat(ident, sizeof(ident), tokc.str.data);
623 else
624 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
626 if (s1->warn_unsupported)
627 tcc_warning("ignoring .ident %s", ident);
629 next();
631 break;
632 case TOK_ASMDIR_size:
634 Sym *sym;
636 next();
637 sym = label_find(tok);
638 if (!sym) {
639 tcc_error("label not found: %s", get_tok_str(tok, NULL));
642 /* XXX .size name,label2-label1 */
643 if (s1->warn_unsupported)
644 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
646 next();
647 skip(',');
648 while (tok != '\n' && tok != CH_EOF) {
649 next();
652 break;
653 case TOK_ASMDIR_type:
655 Sym *sym;
656 const char *newtype;
658 next();
659 sym = label_find(tok);
660 if (!sym) {
661 sym = label_push(&s1->asm_labels, tok, 0);
662 sym->type.t = VT_VOID;
665 next();
666 skip(',');
667 if (tok == TOK_STR) {
668 newtype = tokc.str.data;
669 } else {
670 if (tok == '@' || tok == '%')
671 next();
672 newtype = get_tok_str(tok, NULL);
675 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
676 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
678 else if (s1->warn_unsupported)
679 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
680 get_tok_str(sym->v, NULL), sym->type.t, newtype);
682 next();
684 break;
685 case TOK_ASMDIR_section:
687 char sname[256];
689 /* XXX: support more options */
690 next();
691 sname[0] = '\0';
692 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
693 if (tok == TOK_STR)
694 pstrcat(sname, sizeof(sname), tokc.str.data);
695 else
696 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
697 next();
699 if (tok == ',') {
700 /* skip section options */
701 next();
702 if (tok != TOK_STR)
703 expect("string constant");
704 next();
705 if (tok == ',') {
706 next();
707 if (tok == '@' || tok == '%')
708 next();
709 next();
712 last_text_section = cur_text_section;
713 use_section(s1, sname);
715 break;
716 case TOK_ASMDIR_previous:
718 Section *sec;
719 next();
720 if (!last_text_section)
721 tcc_error("no previous section referenced");
722 sec = cur_text_section;
723 use_section1(s1, last_text_section);
724 last_text_section = sec;
726 break;
727 #ifdef TCC_TARGET_I386
728 case TOK_ASMDIR_code16:
730 next();
731 s1->seg_size = 16;
733 break;
734 case TOK_ASMDIR_code32:
736 next();
737 s1->seg_size = 32;
739 break;
740 #endif
741 #ifdef TCC_TARGET_X86_64
742 /* added for compatibility with GAS */
743 case TOK_ASMDIR_code64:
744 next();
745 break;
746 #endif
747 default:
748 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
749 break;
754 /* assemble a file */
755 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
757 int opcode;
759 #if 0
760 /* print stats about opcodes */
762 const ASMInstr *pa;
763 int freq[4];
764 int op_vals[500];
765 int nb_op_vals, i, j;
767 nb_op_vals = 0;
768 memset(freq, 0, sizeof(freq));
769 for(pa = asm_instrs; pa->sym != 0; pa++) {
770 freq[pa->nb_ops]++;
771 for(i=0;i<pa->nb_ops;i++) {
772 for(j=0;j<nb_op_vals;j++) {
773 if (pa->op_type[i] == op_vals[j])
774 goto found;
776 op_vals[nb_op_vals++] = pa->op_type[i];
777 found: ;
780 for(i=0;i<nb_op_vals;i++) {
781 int v = op_vals[i];
782 if ((v & (v - 1)) != 0)
783 printf("%3d: %08x\n", i, v);
785 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
786 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
787 freq[0], freq[1], freq[2], freq[3]);
789 #endif
791 /* XXX: undefine C labels */
793 ch = file->buf_ptr[0];
794 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
795 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
796 if (do_preprocess)
797 parse_flags |= PARSE_FLAG_PREPROCESS;
798 next();
799 for(;;) {
800 if (tok == TOK_EOF)
801 break;
802 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
803 redo:
804 if (tok == '#') {
805 /* horrible gas comment */
806 while (tok != TOK_LINEFEED)
807 next();
808 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
809 asm_parse_directive(s1);
810 } else if (tok == TOK_PPNUM) {
811 const char *p;
812 int n;
813 p = tokc.str.data;
814 n = strtoul(p, (char **)&p, 10);
815 if (*p != '\0')
816 expect("':'");
817 /* new local label */
818 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
819 next();
820 skip(':');
821 goto redo;
822 } else if (tok >= TOK_IDENT) {
823 /* instruction or label */
824 opcode = tok;
825 next();
826 if (tok == ':') {
827 /* handle "extern void vide(void); __asm__("vide: ret");" as
828 "__asm__("globl vide\nvide: ret");" */
829 Sym *sym = sym_find(opcode);
830 if (sym && (sym->type.t & VT_EXTERN) && nocode_wanted) {
831 sym = label_find(opcode);
832 if (!sym) {
833 sym = label_push(&s1->asm_labels, opcode, 0);
834 sym->type.t = VT_VOID;
837 /* new label */
838 asm_new_label(s1, opcode, 0);
839 next();
840 goto redo;
841 } else if (tok == '=') {
842 int n;
843 next();
844 n = asm_int_expr(s1);
845 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
846 goto redo;
847 } else {
848 asm_opcode(s1, opcode);
851 /* end of line */
852 if (tok != ';' && tok != TOK_LINEFEED){
853 expect("end of line");
855 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
856 next();
859 asm_free_labels(s1);
861 return 0;
864 /* Assemble the current file */
865 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
867 Sym *define_start;
868 int ret;
870 preprocess_init(s1);
872 /* default section is text */
873 cur_text_section = text_section;
874 ind = cur_text_section->data_offset;
876 define_start = define_stack;
878 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
879 symbols can be safely used */
880 put_elf_sym(symtab_section, 0, 0,
881 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
882 SHN_ABS, file->filename);
884 ret = tcc_assemble_internal(s1, do_preprocess);
886 cur_text_section->data_offset = ind;
888 free_defines(define_start);
890 return ret;
893 /********************************************************************/
894 /* GCC inline asm support */
896 /* assemble the string 'str' in the current C compilation unit without
897 C preprocessing. NOTE: str is modified by modifying the '\0' at the
898 end */
899 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
901 int saved_parse_flags;
902 const int *saved_macro_ptr;
904 saved_parse_flags = parse_flags;
905 saved_macro_ptr = macro_ptr;
907 tcc_open_bf(s1, ":asm:", len);
908 memcpy(file->buffer, str, len);
910 macro_ptr = NULL;
911 tcc_assemble_internal(s1, 0);
912 tcc_close();
914 parse_flags = saved_parse_flags;
915 macro_ptr = saved_macro_ptr;
918 /* find a constraint by its number or id (gcc 3 extended
919 syntax). return -1 if not found. Return in *pp in char after the
920 constraint */
921 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
922 const char *name, const char **pp)
924 int index;
925 TokenSym *ts;
926 const char *p;
928 if (isnum(*name)) {
929 index = 0;
930 while (isnum(*name)) {
931 index = (index * 10) + (*name) - '0';
932 name++;
934 if ((unsigned)index >= nb_operands)
935 index = -1;
936 } else if (*name == '[') {
937 name++;
938 p = strchr(name, ']');
939 if (p) {
940 ts = tok_alloc(name, p - name);
941 for(index = 0; index < nb_operands; index++) {
942 if (operands[index].id == ts->tok)
943 goto found;
945 index = -1;
946 found:
947 name = p + 1;
948 } else {
949 index = -1;
951 } else {
952 index = -1;
954 if (pp)
955 *pp = name;
956 return index;
959 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
960 int nb_outputs,
961 CString *out_str, CString *in_str)
963 int c, index, modifier;
964 const char *str;
965 ASMOperand *op;
966 SValue sv;
968 cstr_new(out_str);
969 str = in_str->data;
970 for(;;) {
971 c = *str++;
972 if (c == '%') {
973 if (*str == '%') {
974 str++;
975 goto add_char;
977 modifier = 0;
978 if (*str == 'c' || *str == 'n' ||
979 *str == 'b' || *str == 'w' || *str == 'h')
980 modifier = *str++;
981 index = find_constraint(operands, nb_operands, str, &str);
982 if (index < 0)
983 tcc_error("invalid operand reference after %%");
984 op = &operands[index];
985 sv = *op->vt;
986 if (op->reg >= 0) {
987 sv.r = op->reg;
988 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
989 sv.r |= VT_LVAL;
991 subst_asm_operand(out_str, &sv, modifier);
992 } else {
993 add_char:
994 cstr_ccat(out_str, c);
995 if (c == '\0')
996 break;
1002 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
1003 int is_output)
1005 ASMOperand *op;
1006 int nb_operands;
1008 if (tok != ':') {
1009 nb_operands = *nb_operands_ptr;
1010 for(;;) {
1011 if (nb_operands >= MAX_ASM_OPERANDS)
1012 tcc_error("too many asm operands");
1013 op = &operands[nb_operands++];
1014 op->id = 0;
1015 if (tok == '[') {
1016 next();
1017 if (tok < TOK_IDENT)
1018 expect("identifier");
1019 op->id = tok;
1020 next();
1021 skip(']');
1023 if (tok != TOK_STR)
1024 expect("string constant");
1025 op->constraint = tcc_malloc(tokc.str.size);
1026 strcpy(op->constraint, tokc.str.data);
1027 next();
1028 skip('(');
1029 gexpr();
1030 if (is_output) {
1031 test_lvalue();
1032 } else {
1033 /* we want to avoid LLOCAL case, except when the 'm'
1034 constraint is used. Note that it may come from
1035 register storage, so we need to convert (reg)
1036 case */
1037 if ((vtop->r & VT_LVAL) &&
1038 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
1039 (vtop->r & VT_VALMASK) < VT_CONST) &&
1040 !strchr(op->constraint, 'm')) {
1041 gv(RC_INT);
1044 op->vt = vtop;
1045 skip(')');
1046 if (tok == ',') {
1047 next();
1048 } else {
1049 break;
1052 *nb_operands_ptr = nb_operands;
1056 /* parse the GCC asm() instruction */
1057 ST_FUNC void asm_instr(void)
1059 CString astr, astr1;
1060 ASMOperand operands[MAX_ASM_OPERANDS];
1061 int nb_outputs, nb_operands, i, must_subst, out_reg;
1062 uint8_t clobber_regs[NB_ASM_REGS];
1064 next();
1065 /* since we always generate the asm() instruction, we can ignore
1066 volatile */
1067 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1068 next();
1070 parse_asm_str(&astr);
1071 nb_operands = 0;
1072 nb_outputs = 0;
1073 must_subst = 0;
1074 memset(clobber_regs, 0, sizeof(clobber_regs));
1075 if (tok == ':') {
1076 next();
1077 must_subst = 1;
1078 /* output args */
1079 parse_asm_operands(operands, &nb_operands, 1);
1080 nb_outputs = nb_operands;
1081 if (tok == ':') {
1082 next();
1083 if (tok != ')') {
1084 /* input args */
1085 parse_asm_operands(operands, &nb_operands, 0);
1086 if (tok == ':') {
1087 /* clobber list */
1088 /* XXX: handle registers */
1089 next();
1090 for(;;) {
1091 if (tok != TOK_STR)
1092 expect("string constant");
1093 asm_clobber(clobber_regs, tokc.str.data);
1094 next();
1095 if (tok == ',') {
1096 next();
1097 } else {
1098 break;
1105 skip(')');
1106 /* NOTE: we do not eat the ';' so that we can restore the current
1107 token after the assembler parsing */
1108 if (tok != ';')
1109 expect("';'");
1111 /* save all values in the memory */
1112 save_regs(0);
1114 /* compute constraints */
1115 asm_compute_constraints(operands, nb_operands, nb_outputs,
1116 clobber_regs, &out_reg);
1118 /* substitute the operands in the asm string. No substitution is
1119 done if no operands (GCC behaviour) */
1120 #ifdef ASM_DEBUG
1121 printf("asm: \"%s\"\n", (char *)astr.data);
1122 #endif
1123 if (must_subst) {
1124 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1125 cstr_free(&astr);
1126 } else {
1127 astr1 = astr;
1129 #ifdef ASM_DEBUG
1130 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1131 #endif
1133 /* generate loads */
1134 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1135 clobber_regs, out_reg);
1137 /* assemble the string with tcc internal assembler */
1138 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1140 /* restore the current C token */
1141 next();
1143 /* store the output values if needed */
1144 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1145 clobber_regs, out_reg);
1147 /* free everything */
1148 for(i=0;i<nb_operands;i++) {
1149 ASMOperand *op;
1150 op = &operands[i];
1151 tcc_free(op->constraint);
1152 vpop();
1154 cstr_free(&astr1);
1157 ST_FUNC void asm_global_instr(void)
1159 CString astr;
1161 next();
1162 parse_asm_str(&astr);
1163 skip(')');
1164 /* NOTE: we do not eat the ';' so that we can restore the current
1165 token after the assembler parsing */
1166 if (tok != ';')
1167 expect("';'");
1169 #ifdef ASM_DEBUG
1170 printf("asm_global: \"%s\"\n", (char *)astr.data);
1171 #endif
1172 cur_text_section = text_section;
1173 ind = cur_text_section->data_offset;
1175 /* assemble the string with tcc internal assembler */
1176 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1178 cur_text_section->data_offset = ind;
1180 /* restore the current C token */
1181 next();
1183 cstr_free(&astr);
1185 #endif /* CONFIG_TCC_ASM */