Move utility functions `trimfront/back` to tccpp.c
[tinycc.git] / tccasm.c
blobf6fe369d75cb8b66d6a516434c395266102651e6
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);
36 /* We do not use the C expression parser to handle symbols. Maybe the
37 C expression parser could be tweaked to do so. */
39 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
41 Sym *sym;
42 int op, n, label;
43 const char *p;
45 switch(tok) {
46 case TOK_PPNUM:
47 p = tokc.str.data;
48 n = strtoul(p, (char **)&p, 0);
49 if (*p == 'b' || *p == 'f') {
50 /* backward or forward label */
51 label = asm_get_local_label_name(s1, n);
52 sym = label_find(label);
53 if (*p == 'b') {
54 /* backward : find the last corresponding defined label */
55 if (sym && sym->r == 0)
56 sym = sym->prev_tok;
57 if (!sym)
58 tcc_error("local label '%d' not found backward", n);
59 } else {
60 /* forward */
61 if (!sym || sym->r) {
62 /* if the last label is defined, then define a new one */
63 sym = label_push(&s1->asm_labels, label, 0);
64 sym->type.t = VT_STATIC | VT_VOID;
67 pe->v = 0;
68 pe->sym = sym;
69 } else if (*p == '\0') {
70 pe->v = n;
71 pe->sym = NULL;
72 } else {
73 tcc_error("invalid number syntax");
75 next();
76 break;
77 case '+':
78 next();
79 asm_expr_unary(s1, pe);
80 break;
81 case '-':
82 case '~':
83 op = tok;
84 next();
85 asm_expr_unary(s1, pe);
86 if (pe->sym)
87 tcc_error("invalid operation with label");
88 if (op == '-')
89 pe->v = -pe->v;
90 else
91 pe->v = ~pe->v;
92 break;
93 case TOK_CCHAR:
94 case TOK_LCHAR:
95 pe->v = tokc.i;
96 pe->sym = NULL;
97 next();
98 break;
99 case '(':
100 next();
101 asm_expr(s1, pe);
102 skip(')');
103 break;
104 default:
105 if (tok >= TOK_IDENT) {
106 /* label case : if the label was not found, add one */
107 sym = label_find(tok);
108 if (!sym) {
109 sym = label_push(&s1->asm_labels, tok, 0);
110 /* NOTE: by default, the symbol is global */
111 sym->type.t = VT_VOID;
113 if (sym->r == SHN_ABS) {
114 /* if absolute symbol, no need to put a symbol value */
115 pe->v = sym->jnext;
116 pe->sym = NULL;
117 } else {
118 pe->v = 0;
119 pe->sym = sym;
121 next();
122 } else {
123 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
125 break;
129 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
131 int op;
132 ExprValue e2;
134 asm_expr_unary(s1, pe);
135 for(;;) {
136 op = tok;
137 if (op != '*' && op != '/' && op != '%' &&
138 op != TOK_SHL && op != TOK_SAR)
139 break;
140 next();
141 asm_expr_unary(s1, &e2);
142 if (pe->sym || e2.sym)
143 tcc_error("invalid operation with label");
144 switch(op) {
145 case '*':
146 pe->v *= e2.v;
147 break;
148 case '/':
149 if (e2.v == 0) {
150 div_error:
151 tcc_error("division by zero");
153 pe->v /= e2.v;
154 break;
155 case '%':
156 if (e2.v == 0)
157 goto div_error;
158 pe->v %= e2.v;
159 break;
160 case TOK_SHL:
161 pe->v <<= e2.v;
162 break;
163 default:
164 case TOK_SAR:
165 pe->v >>= e2.v;
166 break;
171 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
173 int op;
174 ExprValue e2;
176 asm_expr_prod(s1, pe);
177 for(;;) {
178 op = tok;
179 if (op != '&' && op != '|' && op != '^')
180 break;
181 next();
182 asm_expr_prod(s1, &e2);
183 if (pe->sym || e2.sym)
184 tcc_error("invalid operation with label");
185 switch(op) {
186 case '&':
187 pe->v &= e2.v;
188 break;
189 case '|':
190 pe->v |= e2.v;
191 break;
192 default:
193 case '^':
194 pe->v ^= e2.v;
195 break;
200 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
202 int op;
203 ExprValue e2;
205 asm_expr_logic(s1, pe);
206 for(;;) {
207 op = tok;
208 if (op != '+' && op != '-')
209 break;
210 next();
211 asm_expr_logic(s1, &e2);
212 if (op == '+') {
213 if (pe->sym != NULL && e2.sym != NULL)
214 goto cannot_relocate;
215 pe->v += e2.v;
216 if (pe->sym == NULL && e2.sym != NULL)
217 pe->sym = e2.sym;
218 } else {
219 pe->v -= e2.v;
220 /* NOTE: we are less powerful than gas in that case
221 because we store only one symbol in the expression */
222 if (!pe->sym && !e2.sym) {
223 /* OK */
224 } else if (pe->sym && !e2.sym) {
225 /* OK */
226 } else if (pe->sym && e2.sym) {
227 if (pe->sym == e2.sym) {
228 /* OK */
229 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
230 /* we also accept defined symbols in the same section */
231 pe->v += pe->sym->jnext - e2.sym->jnext;
232 } else {
233 goto cannot_relocate;
235 pe->sym = NULL; /* same symbols can be subtracted to NULL */
236 } else {
237 cannot_relocate:
238 tcc_error("invalid operation with label");
244 ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
246 asm_expr_sum(s1, pe);
249 ST_FUNC int asm_int_expr(TCCState *s1)
251 ExprValue e;
252 asm_expr(s1, &e);
253 if (e.sym)
254 expect("constant");
255 return e.v;
258 /* NOTE: the same name space as C labels is used to avoid using too
259 much memory when storing labels in TokenStrings */
260 static void asm_new_label1(TCCState *s1, int label, int is_local,
261 int sh_num, int value)
263 Sym *sym;
265 sym = label_find(label);
266 if (sym) {
267 if (sym->r) {
268 /* the label is already defined */
269 if (!is_local) {
270 tcc_error("assembler label '%s' already defined",
271 get_tok_str(label, NULL));
272 } else {
273 /* redefinition of local labels is possible */
274 goto new_label;
277 } else {
278 new_label:
279 sym = label_push(&s1->asm_labels, label, 0);
280 sym->type.t = VT_STATIC | VT_VOID;
282 sym->r = sh_num;
283 sym->jnext = value;
286 static void asm_new_label(TCCState *s1, int label, int is_local)
288 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
291 static void asm_free_labels(TCCState *st)
293 Sym *s, *s1;
294 Section *sec;
296 for(s = st->asm_labels; s != NULL; s = s1) {
297 s1 = s->prev;
298 /* define symbol value in object file */
299 if (s->r) {
300 if (s->r == SHN_ABS)
301 sec = SECTION_ABS;
302 else
303 sec = st->sections[s->r];
304 put_extern_sym2(s, sec, s->jnext, 0, 0);
306 /* remove label */
307 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
308 sym_free(s);
310 st->asm_labels = NULL;
313 static void use_section1(TCCState *s1, Section *sec)
315 cur_text_section->data_offset = ind;
316 cur_text_section = sec;
317 ind = cur_text_section->data_offset;
320 static void use_section(TCCState *s1, const char *name)
322 Section *sec;
323 sec = find_section(s1, name);
324 use_section1(s1, sec);
327 static void asm_parse_directive(TCCState *s1)
329 int n, offset, v, size, tok1;
330 Section *sec;
331 uint8_t *ptr;
333 /* assembler directive */
334 sec = cur_text_section;
335 switch(tok) {
336 case TOK_ASMDIR_align:
337 case TOK_ASMDIR_p2align:
338 case TOK_ASMDIR_skip:
339 case TOK_ASMDIR_space:
340 tok1 = tok;
341 next();
342 n = asm_int_expr(s1);
343 if (tok1 == TOK_ASMDIR_p2align)
345 if (n < 0 || n > 30)
346 tcc_error("invalid p2align, must be between 0 and 30");
347 n = 1 << n;
348 tok1 = TOK_ASMDIR_align;
350 if (tok1 == TOK_ASMDIR_align) {
351 if (n < 0 || (n & (n-1)) != 0)
352 tcc_error("alignment must be a positive power of two");
353 offset = (ind + n - 1) & -n;
354 size = offset - ind;
355 /* the section must have a compatible alignment */
356 if (sec->sh_addralign < n)
357 sec->sh_addralign = n;
358 } else {
359 size = n;
361 v = 0;
362 if (tok == ',') {
363 next();
364 v = asm_int_expr(s1);
366 zero_pad:
367 if (sec->sh_type != SHT_NOBITS) {
368 sec->data_offset = ind;
369 ptr = section_ptr_add(sec, size);
370 memset(ptr, v, size);
372 ind += size;
373 break;
374 case TOK_ASMDIR_quad:
375 next();
376 for(;;) {
377 uint64_t vl;
378 const char *p;
380 p = tokc.str.data;
381 if (tok != TOK_PPNUM) {
382 error_constant:
383 tcc_error("64 bit constant");
385 vl = strtoll(p, (char **)&p, 0);
386 if (*p != '\0')
387 goto error_constant;
388 next();
389 if (sec->sh_type != SHT_NOBITS) {
390 /* XXX: endianness */
391 gen_le32(vl);
392 gen_le32(vl >> 32);
393 } else {
394 ind += 8;
396 if (tok != ',')
397 break;
398 next();
400 break;
401 case TOK_ASMDIR_byte:
402 size = 1;
403 goto asm_data;
404 case TOK_ASMDIR_word:
405 case TOK_ASMDIR_short:
406 size = 2;
407 goto asm_data;
408 case TOK_ASMDIR_long:
409 case TOK_ASMDIR_int:
410 size = 4;
411 asm_data:
412 next();
413 for(;;) {
414 ExprValue e;
415 asm_expr(s1, &e);
416 if (sec->sh_type != SHT_NOBITS) {
417 if (size == 4) {
418 gen_expr32(&e);
419 } else {
420 if (e.sym)
421 expect("constant");
422 if (size == 1)
423 g(e.v);
424 else
425 gen_le16(e.v);
427 } else {
428 ind += size;
430 if (tok != ',')
431 break;
432 next();
434 break;
435 case TOK_ASMDIR_fill:
437 int repeat, size, val, i, j;
438 uint8_t repeat_buf[8];
439 next();
440 repeat = asm_int_expr(s1);
441 if (repeat < 0) {
442 tcc_error("repeat < 0; .fill ignored");
443 break;
445 size = 1;
446 val = 0;
447 if (tok == ',') {
448 next();
449 size = asm_int_expr(s1);
450 if (size < 0) {
451 tcc_error("size < 0; .fill ignored");
452 break;
454 if (size > 8)
455 size = 8;
456 if (tok == ',') {
457 next();
458 val = asm_int_expr(s1);
461 /* XXX: endianness */
462 repeat_buf[0] = val;
463 repeat_buf[1] = val >> 8;
464 repeat_buf[2] = val >> 16;
465 repeat_buf[3] = val >> 24;
466 repeat_buf[4] = 0;
467 repeat_buf[5] = 0;
468 repeat_buf[6] = 0;
469 repeat_buf[7] = 0;
470 for(i = 0; i < repeat; i++) {
471 for(j = 0; j < size; j++) {
472 g(repeat_buf[j]);
476 break;
477 case TOK_ASMDIR_org:
479 unsigned long n;
480 next();
481 /* XXX: handle section symbols too */
482 n = asm_int_expr(s1);
483 if (n < ind)
484 tcc_error("attempt to .org backwards");
485 v = 0;
486 size = n - ind;
487 goto zero_pad;
489 break;
490 case TOK_ASMDIR_globl:
491 case TOK_ASMDIR_global:
492 case TOK_ASMDIR_weak:
493 case TOK_ASMDIR_hidden:
494 tok1 = tok;
495 do {
496 Sym *sym;
498 next();
499 sym = label_find(tok);
500 if (!sym) {
501 sym = label_push(&s1->asm_labels, tok, 0);
502 sym->type.t = VT_VOID;
504 if (tok1 != TOK_ASMDIR_hidden)
505 sym->type.t &= ~VT_STATIC;
506 if (tok1 == TOK_ASMDIR_weak)
507 sym->type.t |= VT_WEAK;
508 else if (tok1 == TOK_ASMDIR_hidden)
509 sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
510 next();
511 } while (tok == ',');
512 break;
513 case TOK_ASMDIR_string:
514 case TOK_ASMDIR_ascii:
515 case TOK_ASMDIR_asciz:
517 const uint8_t *p;
518 int i, size, t;
520 t = tok;
521 next();
522 for(;;) {
523 if (tok != TOK_STR)
524 expect("string constant");
525 p = tokc.str.data;
526 size = tokc.str.size;
527 if (t == TOK_ASMDIR_ascii && size > 0)
528 size--;
529 for(i = 0; i < size; i++)
530 g(p[i]);
531 next();
532 if (tok == ',') {
533 next();
534 } else if (tok != TOK_STR) {
535 break;
539 break;
540 case TOK_ASMDIR_text:
541 case TOK_ASMDIR_data:
542 case TOK_ASMDIR_bss:
544 char sname[64];
545 tok1 = tok;
546 n = 0;
547 next();
548 if (tok != ';' && tok != TOK_LINEFEED) {
549 n = asm_int_expr(s1);
550 next();
552 if (n)
553 sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
554 else
555 sprintf(sname, "%s", get_tok_str(tok1, NULL));
556 use_section(s1, sname);
558 break;
559 case TOK_ASMDIR_file:
561 char filename[512];
563 filename[0] = '\0';
564 next();
566 if (tok == TOK_STR)
567 pstrcat(filename, sizeof(filename), tokc.str.data);
568 else
569 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
571 if (s1->warn_unsupported)
572 tcc_warning("ignoring .file %s", filename);
574 next();
576 break;
577 case TOK_ASMDIR_ident:
579 char ident[256];
581 ident[0] = '\0';
582 next();
584 if (tok == TOK_STR)
585 pstrcat(ident, sizeof(ident), tokc.str.data);
586 else
587 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
589 if (s1->warn_unsupported)
590 tcc_warning("ignoring .ident %s", ident);
592 next();
594 break;
595 case TOK_ASMDIR_size:
597 Sym *sym;
599 next();
600 sym = label_find(tok);
601 if (!sym) {
602 tcc_error("label not found: %s", get_tok_str(tok, NULL));
605 /* XXX .size name,label2-label1 */
606 if (s1->warn_unsupported)
607 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
609 next();
610 skip(',');
611 while (tok != '\n' && tok != CH_EOF) {
612 next();
615 break;
616 case TOK_ASMDIR_type:
618 Sym *sym;
619 const char *newtype;
621 next();
622 sym = label_find(tok);
623 if (!sym) {
624 sym = label_push(&s1->asm_labels, tok, 0);
625 sym->type.t = VT_VOID;
628 next();
629 skip(',');
630 if (tok == TOK_STR) {
631 newtype = tokc.str.data;
632 } else {
633 if (tok == '@' || tok == '%')
634 next();
635 newtype = get_tok_str(tok, NULL);
638 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
639 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
641 else if (s1->warn_unsupported)
642 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
643 get_tok_str(sym->v, NULL), sym->type.t, newtype);
645 next();
647 break;
648 case TOK_ASMDIR_section:
650 char sname[256];
652 /* XXX: support more options */
653 next();
654 sname[0] = '\0';
655 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
656 if (tok == TOK_STR)
657 pstrcat(sname, sizeof(sname), tokc.str.data);
658 else
659 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
660 next();
662 if (tok == ',') {
663 /* skip section options */
664 next();
665 if (tok != TOK_STR)
666 expect("string constant");
667 next();
668 if (tok == ',') {
669 next();
670 if (tok == '@' || tok == '%')
671 next();
672 next();
675 last_text_section = cur_text_section;
676 use_section(s1, sname);
678 break;
679 case TOK_ASMDIR_previous:
681 Section *sec;
682 next();
683 if (!last_text_section)
684 tcc_error("no previous section referenced");
685 sec = cur_text_section;
686 use_section1(s1, last_text_section);
687 last_text_section = sec;
689 break;
690 #ifdef TCC_TARGET_I386
691 case TOK_ASMDIR_code16:
693 next();
694 s1->seg_size = 16;
696 break;
697 case TOK_ASMDIR_code32:
699 next();
700 s1->seg_size = 32;
702 break;
703 #endif
704 #ifdef TCC_TARGET_X86_64
705 /* added for compatibility with GAS */
706 case TOK_ASMDIR_code64:
707 next();
708 break;
709 #endif
710 default:
711 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
712 break;
717 /* assemble a file */
718 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
720 int opcode;
722 #if 0
723 /* print stats about opcodes */
725 const ASMInstr *pa;
726 int freq[4];
727 int op_vals[500];
728 int nb_op_vals, i, j;
730 nb_op_vals = 0;
731 memset(freq, 0, sizeof(freq));
732 for(pa = asm_instrs; pa->sym != 0; pa++) {
733 freq[pa->nb_ops]++;
734 for(i=0;i<pa->nb_ops;i++) {
735 for(j=0;j<nb_op_vals;j++) {
736 if (pa->op_type[i] == op_vals[j])
737 goto found;
739 op_vals[nb_op_vals++] = pa->op_type[i];
740 found: ;
743 for(i=0;i<nb_op_vals;i++) {
744 int v = op_vals[i];
745 if ((v & (v - 1)) != 0)
746 printf("%3d: %08x\n", i, v);
748 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
749 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
750 freq[0], freq[1], freq[2], freq[3]);
752 #endif
754 /* XXX: undefine C labels */
756 ch = file->buf_ptr[0];
757 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
758 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
759 if (do_preprocess)
760 parse_flags |= PARSE_FLAG_PREPROCESS;
761 next();
762 for(;;) {
763 if (tok == TOK_EOF)
764 break;
765 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
766 redo:
767 if (tok == '#') {
768 /* horrible gas comment */
769 while (tok != TOK_LINEFEED)
770 next();
771 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
772 asm_parse_directive(s1);
773 } else if (tok == TOK_PPNUM) {
774 const char *p;
775 int n;
776 p = tokc.str.data;
777 n = strtoul(p, (char **)&p, 10);
778 if (*p != '\0')
779 expect("':'");
780 /* new local label */
781 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
782 next();
783 skip(':');
784 goto redo;
785 } else if (tok >= TOK_IDENT) {
786 /* instruction or label */
787 opcode = tok;
788 next();
789 if (tok == ':') {
790 /* new label */
791 asm_new_label(s1, opcode, 0);
792 next();
793 goto redo;
794 } else if (tok == '=') {
795 int n;
796 next();
797 n = asm_int_expr(s1);
798 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
799 goto redo;
800 } else {
801 asm_opcode(s1, opcode);
804 /* end of line */
805 if (tok != ';' && tok != TOK_LINEFEED){
806 expect("end of line");
808 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
809 next();
812 asm_free_labels(s1);
814 return 0;
817 /* Assemble the current file */
818 ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
820 Sym *define_start;
821 int ret;
823 preprocess_init(s1);
825 /* default section is text */
826 cur_text_section = text_section;
827 ind = cur_text_section->data_offset;
829 define_start = define_stack;
831 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
832 symbols can be safely used */
833 put_elf_sym(symtab_section, 0, 0,
834 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
835 SHN_ABS, file->filename);
837 ret = tcc_assemble_internal(s1, do_preprocess);
839 cur_text_section->data_offset = ind;
841 free_defines(define_start);
843 return ret;
846 /********************************************************************/
847 /* GCC inline asm support */
849 /* assemble the string 'str' in the current C compilation unit without
850 C preprocessing. NOTE: str is modified by modifying the '\0' at the
851 end */
852 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
854 int saved_parse_flags;
855 const int *saved_macro_ptr;
857 saved_parse_flags = parse_flags;
858 saved_macro_ptr = macro_ptr;
860 tcc_open_bf(s1, ":asm:", len);
861 memcpy(file->buffer, str, len);
863 macro_ptr = NULL;
864 tcc_assemble_internal(s1, 0);
865 tcc_close();
867 parse_flags = saved_parse_flags;
868 macro_ptr = saved_macro_ptr;
871 /* find a constraint by its number or id (gcc 3 extended
872 syntax). return -1 if not found. Return in *pp in char after the
873 constraint */
874 ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
875 const char *name, const char **pp)
877 int index;
878 TokenSym *ts;
879 const char *p;
881 if (isnum(*name)) {
882 index = 0;
883 while (isnum(*name)) {
884 index = (index * 10) + (*name) - '0';
885 name++;
887 if ((unsigned)index >= nb_operands)
888 index = -1;
889 } else if (*name == '[') {
890 name++;
891 p = strchr(name, ']');
892 if (p) {
893 ts = tok_alloc(name, p - name);
894 for(index = 0; index < nb_operands; index++) {
895 if (operands[index].id == ts->tok)
896 goto found;
898 index = -1;
899 found:
900 name = p + 1;
901 } else {
902 index = -1;
904 } else {
905 index = -1;
907 if (pp)
908 *pp = name;
909 return index;
912 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
913 int nb_outputs,
914 CString *out_str, CString *in_str)
916 int c, index, modifier;
917 const char *str;
918 ASMOperand *op;
919 SValue sv;
921 cstr_new(out_str);
922 str = in_str->data;
923 for(;;) {
924 c = *str++;
925 if (c == '%') {
926 if (*str == '%') {
927 str++;
928 goto add_char;
930 modifier = 0;
931 if (*str == 'c' || *str == 'n' ||
932 *str == 'b' || *str == 'w' || *str == 'h')
933 modifier = *str++;
934 index = find_constraint(operands, nb_operands, str, &str);
935 if (index < 0)
936 tcc_error("invalid operand reference after %%");
937 op = &operands[index];
938 sv = *op->vt;
939 if (op->reg >= 0) {
940 sv.r = op->reg;
941 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
942 sv.r |= VT_LVAL;
944 subst_asm_operand(out_str, &sv, modifier);
945 } else {
946 add_char:
947 cstr_ccat(out_str, c);
948 if (c == '\0')
949 break;
955 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
956 int is_output)
958 ASMOperand *op;
959 int nb_operands;
961 if (tok != ':') {
962 nb_operands = *nb_operands_ptr;
963 for(;;) {
964 if (nb_operands >= MAX_ASM_OPERANDS)
965 tcc_error("too many asm operands");
966 op = &operands[nb_operands++];
967 op->id = 0;
968 if (tok == '[') {
969 next();
970 if (tok < TOK_IDENT)
971 expect("identifier");
972 op->id = tok;
973 next();
974 skip(']');
976 if (tok != TOK_STR)
977 expect("string constant");
978 op->constraint = tcc_malloc(tokc.str.size);
979 strcpy(op->constraint, tokc.str.data);
980 next();
981 skip('(');
982 gexpr();
983 if (is_output) {
984 test_lvalue();
985 } else {
986 /* we want to avoid LLOCAL case, except when the 'm'
987 constraint is used. Note that it may come from
988 register storage, so we need to convert (reg)
989 case */
990 if ((vtop->r & VT_LVAL) &&
991 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
992 (vtop->r & VT_VALMASK) < VT_CONST) &&
993 !strchr(op->constraint, 'm')) {
994 gv(RC_INT);
997 op->vt = vtop;
998 skip(')');
999 if (tok == ',') {
1000 next();
1001 } else {
1002 break;
1005 *nb_operands_ptr = nb_operands;
1009 /* parse the GCC asm() instruction */
1010 ST_FUNC void asm_instr(void)
1012 CString astr, astr1;
1013 ASMOperand operands[MAX_ASM_OPERANDS];
1014 int nb_outputs, nb_operands, i, must_subst, out_reg;
1015 uint8_t clobber_regs[NB_ASM_REGS];
1017 next();
1018 /* since we always generate the asm() instruction, we can ignore
1019 volatile */
1020 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1021 next();
1023 parse_asm_str(&astr);
1024 nb_operands = 0;
1025 nb_outputs = 0;
1026 must_subst = 0;
1027 memset(clobber_regs, 0, sizeof(clobber_regs));
1028 if (tok == ':') {
1029 next();
1030 must_subst = 1;
1031 /* output args */
1032 parse_asm_operands(operands, &nb_operands, 1);
1033 nb_outputs = nb_operands;
1034 if (tok == ':') {
1035 next();
1036 if (tok != ')') {
1037 /* input args */
1038 parse_asm_operands(operands, &nb_operands, 0);
1039 if (tok == ':') {
1040 /* clobber list */
1041 /* XXX: handle registers */
1042 next();
1043 for(;;) {
1044 if (tok != TOK_STR)
1045 expect("string constant");
1046 asm_clobber(clobber_regs, tokc.str.data);
1047 next();
1048 if (tok == ',') {
1049 next();
1050 } else {
1051 break;
1058 skip(')');
1059 /* NOTE: we do not eat the ';' so that we can restore the current
1060 token after the assembler parsing */
1061 if (tok != ';')
1062 expect("';'");
1064 /* save all values in the memory */
1065 save_regs(0);
1067 /* compute constraints */
1068 asm_compute_constraints(operands, nb_operands, nb_outputs,
1069 clobber_regs, &out_reg);
1071 /* substitute the operands in the asm string. No substitution is
1072 done if no operands (GCC behaviour) */
1073 #ifdef ASM_DEBUG
1074 printf("asm: \"%s\"\n", (char *)astr.data);
1075 #endif
1076 if (must_subst) {
1077 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1078 cstr_free(&astr);
1079 } else {
1080 astr1 = astr;
1082 #ifdef ASM_DEBUG
1083 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1084 #endif
1086 /* generate loads */
1087 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1088 clobber_regs, out_reg);
1090 /* assemble the string with tcc internal assembler */
1091 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1093 /* restore the current C token */
1094 next();
1096 /* store the output values if needed */
1097 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1098 clobber_regs, out_reg);
1100 /* free everything */
1101 for(i=0;i<nb_operands;i++) {
1102 ASMOperand *op;
1103 op = &operands[i];
1104 tcc_free(op->constraint);
1105 vpop();
1107 cstr_free(&astr1);
1110 ST_FUNC void asm_global_instr(void)
1112 CString astr;
1114 next();
1115 parse_asm_str(&astr);
1116 skip(')');
1117 /* NOTE: we do not eat the ';' so that we can restore the current
1118 token after the assembler parsing */
1119 if (tok != ';')
1120 expect("';'");
1122 #ifdef ASM_DEBUG
1123 printf("asm_global: \"%s\"\n", (char *)astr.data);
1124 #endif
1125 cur_text_section = text_section;
1126 ind = cur_text_section->data_offset;
1128 /* assemble the string with tcc internal assembler */
1129 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1131 cur_text_section->data_offset = ind;
1133 /* restore the current C token */
1134 next();
1136 cstr_free(&astr);
1138 #endif /* CONFIG_TCC_ASM */