first support of x86_64 assembly
[tinycc.git] / tccasm.c
blob6862ef50b1ab10d4ba617c544ad4084a298529e6
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 static int asm_get_local_label_name(TCCState *s1, unsigned int n)
23 char buf[64];
24 TokenSym *ts;
26 snprintf(buf, sizeof(buf), "L..%u", n);
27 ts = tok_alloc(buf, strlen(buf));
28 return ts->tok;
31 static void asm_expr(TCCState *s1, ExprValue *pe);
33 /* We do not use the C expression parser to handle symbols. Maybe the
34 C expression parser could be tweaked to do so. */
36 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
38 Sym *sym;
39 int op, n, label;
40 const char *p;
42 switch(tok) {
43 case TOK_PPNUM:
44 p = tokc.cstr->data;
45 n = strtoul(p, (char **)&p, 0);
46 if (*p == 'b' || *p == 'f') {
47 /* backward or forward label */
48 label = asm_get_local_label_name(s1, n);
49 sym = label_find(label);
50 if (*p == 'b') {
51 /* backward : find the last corresponding defined label */
52 if (sym && sym->r == 0)
53 sym = sym->prev_tok;
54 if (!sym)
55 error("local label '%d' not found backward", n);
56 } else {
57 /* forward */
58 if (!sym || sym->r) {
59 /* if the last label is defined, then define a new one */
60 sym = label_push(&s1->asm_labels, label, 0);
61 sym->type.t = VT_STATIC | VT_VOID;
64 pe->v = 0;
65 pe->sym = sym;
66 } else if (*p == '\0') {
67 pe->v = n;
68 pe->sym = NULL;
69 } else {
70 error("invalid number syntax");
72 next();
73 break;
74 case '+':
75 next();
76 asm_expr_unary(s1, pe);
77 break;
78 case '-':
79 case '~':
80 op = tok;
81 next();
82 asm_expr_unary(s1, pe);
83 if (pe->sym)
84 error("invalid operation with label");
85 if (op == '-')
86 pe->v = -pe->v;
87 else
88 pe->v = ~pe->v;
89 break;
90 case TOK_CCHAR:
91 case TOK_LCHAR:
92 pe->v = tokc.i;
93 pe->sym = NULL;
94 next();
95 break;
96 case '(':
97 next();
98 asm_expr(s1, pe);
99 skip(')');
100 break;
101 default:
102 if (tok >= TOK_IDENT) {
103 /* label case : if the label was not found, add one */
104 sym = label_find(tok);
105 if (!sym) {
106 sym = label_push(&s1->asm_labels, tok, 0);
107 /* NOTE: by default, the symbol is global */
108 sym->type.t = VT_VOID;
110 if (sym->r == SHN_ABS) {
111 /* if absolute symbol, no need to put a symbol value */
112 pe->v = sym->jnext;
113 pe->sym = NULL;
114 } else {
115 pe->v = 0;
116 pe->sym = sym;
118 next();
119 } else {
120 error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
122 break;
126 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
128 int op;
129 ExprValue e2;
131 asm_expr_unary(s1, pe);
132 for(;;) {
133 op = tok;
134 if (op != '*' && op != '/' && op != '%' &&
135 op != TOK_SHL && op != TOK_SAR)
136 break;
137 next();
138 asm_expr_unary(s1, &e2);
139 if (pe->sym || e2.sym)
140 error("invalid operation with label");
141 switch(op) {
142 case '*':
143 pe->v *= e2.v;
144 break;
145 case '/':
146 if (e2.v == 0) {
147 div_error:
148 error("division by zero");
150 pe->v /= e2.v;
151 break;
152 case '%':
153 if (e2.v == 0)
154 goto div_error;
155 pe->v %= e2.v;
156 break;
157 case TOK_SHL:
158 pe->v <<= e2.v;
159 break;
160 default:
161 case TOK_SAR:
162 pe->v >>= e2.v;
163 break;
168 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
170 int op;
171 ExprValue e2;
173 asm_expr_prod(s1, pe);
174 for(;;) {
175 op = tok;
176 if (op != '&' && op != '|' && op != '^')
177 break;
178 next();
179 asm_expr_prod(s1, &e2);
180 if (pe->sym || e2.sym)
181 error("invalid operation with label");
182 switch(op) {
183 case '&':
184 pe->v &= e2.v;
185 break;
186 case '|':
187 pe->v |= e2.v;
188 break;
189 default:
190 case '^':
191 pe->v ^= e2.v;
192 break;
197 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
199 int op;
200 ExprValue e2;
202 asm_expr_logic(s1, pe);
203 for(;;) {
204 op = tok;
205 if (op != '+' && op != '-')
206 break;
207 next();
208 asm_expr_logic(s1, &e2);
209 if (op == '+') {
210 if (pe->sym != NULL && e2.sym != NULL)
211 goto cannot_relocate;
212 pe->v += e2.v;
213 if (pe->sym == NULL && e2.sym != NULL)
214 pe->sym = e2.sym;
215 } else {
216 pe->v -= e2.v;
217 /* NOTE: we are less powerful than gas in that case
218 because we store only one symbol in the expression */
219 if (!pe->sym && !e2.sym) {
220 /* OK */
221 } else if (pe->sym && !e2.sym) {
222 /* OK */
223 } else if (pe->sym && e2.sym) {
224 if (pe->sym == e2.sym) {
225 /* OK */
226 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
227 /* we also accept defined symbols in the same section */
228 pe->v += pe->sym->jnext - e2.sym->jnext;
229 } else {
230 goto cannot_relocate;
232 pe->sym = NULL; /* same symbols can be substracted to NULL */
233 } else {
234 cannot_relocate:
235 error("invalid operation with label");
241 static void asm_expr(TCCState *s1, ExprValue *pe)
243 asm_expr_sum(s1, pe);
246 static int asm_int_expr(TCCState *s1)
248 ExprValue e;
249 asm_expr(s1, &e);
250 if (e.sym)
251 expect("constant");
252 return e.v;
255 /* NOTE: the same name space as C labels is used to avoid using too
256 much memory when storing labels in TokenStrings */
257 static void asm_new_label1(TCCState *s1, int label, int is_local,
258 int sh_num, int value)
260 Sym *sym;
262 sym = label_find(label);
263 if (sym) {
264 if (sym->r) {
265 /* the label is already defined */
266 if (!is_local) {
267 error("assembler label '%s' already defined",
268 get_tok_str(label, NULL));
269 } else {
270 /* redefinition of local labels is possible */
271 goto new_label;
274 } else {
275 new_label:
276 sym = label_push(&s1->asm_labels, label, 0);
277 sym->type.t = VT_STATIC | VT_VOID;
279 sym->r = sh_num;
280 sym->jnext = value;
283 static void asm_new_label(TCCState *s1, int label, int is_local)
285 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
288 static void asm_free_labels(TCCState *st)
290 Sym *s, *s1;
291 Section *sec;
293 for(s = st->asm_labels; s != NULL; s = s1) {
294 s1 = s->prev;
295 /* define symbol value in object file */
296 if (s->r) {
297 if (s->r == SHN_ABS)
298 sec = SECTION_ABS;
299 else
300 sec = st->sections[s->r];
301 put_extern_sym2(s, sec, s->jnext, 0, 0);
303 /* remove label */
304 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
305 sym_free(s);
307 st->asm_labels = NULL;
310 static void use_section1(TCCState *s1, Section *sec)
312 cur_text_section->data_offset = ind;
313 cur_text_section = sec;
314 ind = cur_text_section->data_offset;
317 static void use_section(TCCState *s1, const char *name)
319 Section *sec;
320 sec = find_section(s1, name);
321 use_section1(s1, sec);
324 static void asm_parse_directive(TCCState *s1)
326 int n, offset, v, size, tok1;
327 Section *sec;
328 uint8_t *ptr;
330 /* assembler directive */
331 next();
332 sec = cur_text_section;
333 switch(tok) {
334 case TOK_ASM_align:
335 case TOK_ASM_skip:
336 case TOK_ASM_space:
337 tok1 = tok;
338 next();
339 n = asm_int_expr(s1);
340 if (tok1 == TOK_ASM_align) {
341 if (n < 0 || (n & (n-1)) != 0)
342 error("alignment must be a positive power of two");
343 offset = (ind + n - 1) & -n;
344 size = offset - ind;
345 /* the section must have a compatible alignment */
346 if (sec->sh_addralign < n)
347 sec->sh_addralign = n;
348 } else {
349 size = n;
351 v = 0;
352 if (tok == ',') {
353 next();
354 v = asm_int_expr(s1);
356 zero_pad:
357 if (sec->sh_type != SHT_NOBITS) {
358 sec->data_offset = ind;
359 ptr = section_ptr_add(sec, size);
360 memset(ptr, v, size);
362 ind += size;
363 break;
364 case TOK_ASM_quad:
365 next();
366 for(;;) {
367 uint64_t vl;
368 const char *p;
370 p = tokc.cstr->data;
371 if (tok != TOK_PPNUM) {
372 error_constant:
373 error("64 bit constant");
375 vl = strtoll(p, (char **)&p, 0);
376 if (*p != '\0')
377 goto error_constant;
378 next();
379 if (sec->sh_type != SHT_NOBITS) {
380 /* XXX: endianness */
381 gen_le32(vl);
382 gen_le32(vl >> 32);
383 } else {
384 ind += 8;
386 if (tok != ',')
387 break;
388 next();
390 break;
391 case TOK_ASM_byte:
392 size = 1;
393 goto asm_data;
394 case TOK_ASM_word:
395 case TOK_SHORT:
396 size = 2;
397 goto asm_data;
398 case TOK_LONG:
399 case TOK_INT:
400 size = 4;
401 asm_data:
402 next();
403 for(;;) {
404 ExprValue e;
405 asm_expr(s1, &e);
406 if (sec->sh_type != SHT_NOBITS) {
407 if (size == 4) {
408 gen_expr32(&e);
409 } else {
410 if (e.sym)
411 expect("constant");
412 if (size == 1)
413 g(e.v);
414 else
415 gen_le16(e.v);
417 } else {
418 ind += size;
420 if (tok != ',')
421 break;
422 next();
424 break;
425 case TOK_ASM_fill:
427 int repeat, size, val, i, j;
428 uint8_t repeat_buf[8];
429 next();
430 repeat = asm_int_expr(s1);
431 if (repeat < 0) {
432 error("repeat < 0; .fill ignored");
433 break;
435 size = 1;
436 val = 0;
437 if (tok == ',') {
438 next();
439 size = asm_int_expr(s1);
440 if (size < 0) {
441 error("size < 0; .fill ignored");
442 break;
444 if (size > 8)
445 size = 8;
446 if (tok == ',') {
447 next();
448 val = asm_int_expr(s1);
451 /* XXX: endianness */
452 repeat_buf[0] = val;
453 repeat_buf[1] = val >> 8;
454 repeat_buf[2] = val >> 16;
455 repeat_buf[3] = val >> 24;
456 repeat_buf[4] = 0;
457 repeat_buf[5] = 0;
458 repeat_buf[6] = 0;
459 repeat_buf[7] = 0;
460 for(i = 0; i < repeat; i++) {
461 for(j = 0; j < size; j++) {
462 g(repeat_buf[j]);
466 break;
467 case TOK_ASM_org:
469 unsigned long n;
470 next();
471 /* XXX: handle section symbols too */
472 n = asm_int_expr(s1);
473 if (n < ind)
474 error("attempt to .org backwards");
475 v = 0;
476 size = n - ind;
477 goto zero_pad;
479 break;
480 case TOK_ASM_globl:
481 case TOK_ASM_global:
483 Sym *sym;
485 next();
486 sym = label_find(tok);
487 if (!sym) {
488 sym = label_push(&s1->asm_labels, tok, 0);
489 sym->type.t = VT_VOID;
491 sym->type.t &= ~VT_STATIC;
492 next();
494 break;
495 case TOK_ASM_string:
496 case TOK_ASM_ascii:
497 case TOK_ASM_asciz:
499 const uint8_t *p;
500 int i, size, t;
502 t = tok;
503 next();
504 for(;;) {
505 if (tok != TOK_STR)
506 expect("string constant");
507 p = tokc.cstr->data;
508 size = tokc.cstr->size;
509 if (t == TOK_ASM_ascii && size > 0)
510 size--;
511 for(i = 0; i < size; i++)
512 g(p[i]);
513 next();
514 if (tok == ',') {
515 next();
516 } else if (tok != TOK_STR) {
517 break;
521 break;
522 case TOK_ASM_text:
523 case TOK_ASM_data:
524 case TOK_ASM_bss:
526 char sname[64];
527 tok1 = tok;
528 n = 0;
529 next();
530 if (tok != ';' && tok != TOK_LINEFEED) {
531 n = asm_int_expr(s1);
532 next();
534 sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
535 use_section(s1, sname);
537 break;
538 case TOK_SECTION1:
540 char sname[256];
542 /* XXX: support more options */
543 next();
544 sname[0] = '\0';
545 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
546 if (tok == TOK_STR)
547 pstrcat(sname, sizeof(sname), tokc.cstr->data);
548 else
549 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
550 next();
552 if (tok == ',') {
553 /* skip section options */
554 next();
555 if (tok != TOK_STR)
556 expect("string constant");
557 next();
559 last_text_section = cur_text_section;
560 use_section(s1, sname);
562 break;
563 case TOK_ASM_previous:
565 Section *sec;
566 next();
567 if (!last_text_section)
568 error("no previous section referenced");
569 sec = cur_text_section;
570 use_section1(s1, last_text_section);
571 last_text_section = sec;
573 break;
574 #ifdef TCC_TARGET_I386
575 case TOK_ASM_code16:
577 next();
578 s1->seg_size = 16;
580 break;
581 case TOK_ASM_code32:
583 next();
584 s1->seg_size = 32;
586 break;
587 #endif
588 #ifdef TCC_TARGET_X86_64
589 /* added for compatibility with GAS */
590 case TOK_ASM_code64:
591 next();
592 break;
593 #endif
594 default:
595 error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
596 break;
601 /* assemble a file */
602 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
604 int opcode;
606 #if 0
607 /* print stats about opcodes */
609 const ASMInstr *pa;
610 int freq[4];
611 int op_vals[500];
612 int nb_op_vals, i, j;
614 nb_op_vals = 0;
615 memset(freq, 0, sizeof(freq));
616 for(pa = asm_instrs; pa->sym != 0; pa++) {
617 freq[pa->nb_ops]++;
618 for(i=0;i<pa->nb_ops;i++) {
619 for(j=0;j<nb_op_vals;j++) {
620 if (pa->op_type[i] == op_vals[j])
621 goto found;
623 op_vals[nb_op_vals++] = pa->op_type[i];
624 found: ;
627 for(i=0;i<nb_op_vals;i++) {
628 int v = op_vals[i];
629 if ((v & (v - 1)) != 0)
630 printf("%3d: %08x\n", i, v);
632 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
633 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
634 freq[0], freq[1], freq[2], freq[3]);
636 #endif
638 /* XXX: undefine C labels */
640 ch = file->buf_ptr[0];
641 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
642 parse_flags = PARSE_FLAG_ASM_COMMENTS;
643 if (do_preprocess)
644 parse_flags |= PARSE_FLAG_PREPROCESS;
645 next();
646 for(;;) {
647 if (tok == TOK_EOF)
648 break;
649 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
650 redo:
651 if (tok == '#') {
652 /* horrible gas comment */
653 while (tok != TOK_LINEFEED)
654 next();
655 } else if (tok == '.') {
656 asm_parse_directive(s1);
657 } else if (tok == TOK_PPNUM) {
658 const char *p;
659 int n;
660 p = tokc.cstr->data;
661 n = strtoul(p, (char **)&p, 10);
662 if (*p != '\0')
663 expect("':'");
664 /* new local label */
665 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
666 next();
667 skip(':');
668 goto redo;
669 } else if (tok >= TOK_IDENT) {
670 /* instruction or label */
671 opcode = tok;
672 next();
673 if (tok == ':') {
674 /* new label */
675 asm_new_label(s1, opcode, 0);
676 next();
677 goto redo;
678 } else if (tok == '=') {
679 int n;
680 next();
681 n = asm_int_expr(s1);
682 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
683 goto redo;
684 } else {
685 asm_opcode(s1, opcode);
688 /* end of line */
689 if (tok != ';' && tok != TOK_LINEFEED){
690 expect("end of line");
692 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
693 next();
696 asm_free_labels(s1);
698 return 0;
701 /* Assemble the current file */
702 static int tcc_assemble(TCCState *s1, int do_preprocess)
704 Sym *define_start;
705 int ret;
707 preprocess_init(s1);
709 /* default section is text */
710 cur_text_section = text_section;
711 ind = cur_text_section->data_offset;
713 define_start = define_stack;
715 ret = tcc_assemble_internal(s1, do_preprocess);
717 cur_text_section->data_offset = ind;
719 free_defines(define_start);
721 return ret;
724 /********************************************************************/
725 /* GCC inline asm support */
727 /* assemble the string 'str' in the current C compilation unit without
728 C preprocessing. NOTE: str is modified by modifying the '\0' at the
729 end */
730 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
732 BufferedFile *bf, *saved_file;
733 int saved_parse_flags, *saved_macro_ptr;
735 bf = tcc_malloc(sizeof(BufferedFile));
736 memset(bf, 0, sizeof(BufferedFile));
737 bf->fd = -1;
738 bf->buf_ptr = str;
739 bf->buf_end = str + len;
740 str[len] = CH_EOB;
741 /* same name as current file so that errors are correctly
742 reported */
743 pstrcpy(bf->filename, sizeof(bf->filename), file->filename);
744 bf->line_num = file->line_num;
745 saved_file = file;
746 file = bf;
747 saved_parse_flags = parse_flags;
748 saved_macro_ptr = macro_ptr;
749 macro_ptr = NULL;
751 tcc_assemble_internal(s1, 0);
753 parse_flags = saved_parse_flags;
754 macro_ptr = saved_macro_ptr;
755 file = saved_file;
756 tcc_free(bf);
759 /* find a constraint by its number or id (gcc 3 extended
760 syntax). return -1 if not found. Return in *pp in char after the
761 constraint */
762 static int find_constraint(ASMOperand *operands, int nb_operands,
763 const char *name, const char **pp)
765 int index;
766 TokenSym *ts;
767 const char *p;
769 if (isnum(*name)) {
770 index = 0;
771 while (isnum(*name)) {
772 index = (index * 10) + (*name) - '0';
773 name++;
775 if ((unsigned)index >= nb_operands)
776 index = -1;
777 } else if (*name == '[') {
778 name++;
779 p = strchr(name, ']');
780 if (p) {
781 ts = tok_alloc(name, p - name);
782 for(index = 0; index < nb_operands; index++) {
783 if (operands[index].id == ts->tok)
784 goto found;
786 index = -1;
787 found:
788 name = p + 1;
789 } else {
790 index = -1;
792 } else {
793 index = -1;
795 if (pp)
796 *pp = name;
797 return index;
800 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
801 int nb_outputs,
802 CString *out_str, CString *in_str)
804 int c, index, modifier;
805 const char *str;
806 ASMOperand *op;
807 SValue sv;
809 cstr_new(out_str);
810 str = in_str->data;
811 for(;;) {
812 c = *str++;
813 if (c == '%') {
814 if (*str == '%') {
815 str++;
816 goto add_char;
818 modifier = 0;
819 if (*str == 'c' || *str == 'n' ||
820 *str == 'b' || *str == 'w' || *str == 'h')
821 modifier = *str++;
822 index = find_constraint(operands, nb_operands, str, &str);
823 if (index < 0)
824 error("invalid operand reference after %%");
825 op = &operands[index];
826 sv = *op->vt;
827 if (op->reg >= 0) {
828 sv.r = op->reg;
829 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
830 sv.r |= VT_LVAL;
832 subst_asm_operand(out_str, &sv, modifier);
833 } else {
834 add_char:
835 cstr_ccat(out_str, c);
836 if (c == '\0')
837 break;
843 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
844 int is_output)
846 ASMOperand *op;
847 int nb_operands;
849 if (tok != ':') {
850 nb_operands = *nb_operands_ptr;
851 for(;;) {
852 if (nb_operands >= MAX_ASM_OPERANDS)
853 error("too many asm operands");
854 op = &operands[nb_operands++];
855 op->id = 0;
856 if (tok == '[') {
857 next();
858 if (tok < TOK_IDENT)
859 expect("identifier");
860 op->id = tok;
861 next();
862 skip(']');
864 if (tok != TOK_STR)
865 expect("string constant");
866 op->constraint = tcc_malloc(tokc.cstr->size);
867 strcpy(op->constraint, tokc.cstr->data);
868 next();
869 skip('(');
870 gexpr();
871 if (is_output) {
872 test_lvalue();
873 } else {
874 /* we want to avoid LLOCAL case, except when the 'm'
875 constraint is used. Note that it may come from
876 register storage, so we need to convert (reg)
877 case */
878 if ((vtop->r & VT_LVAL) &&
879 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
880 (vtop->r & VT_VALMASK) < VT_CONST) &&
881 !strchr(op->constraint, 'm')) {
882 gv(RC_INT);
885 op->vt = vtop;
886 skip(')');
887 if (tok == ',') {
888 next();
889 } else {
890 break;
893 *nb_operands_ptr = nb_operands;
897 static void parse_asm_str(CString *astr)
899 skip('(');
900 /* read the string */
901 if (tok != TOK_STR)
902 expect("string constant");
903 cstr_new(astr);
904 while (tok == TOK_STR) {
905 /* XXX: add \0 handling too ? */
906 cstr_cat(astr, tokc.cstr->data);
907 next();
909 cstr_ccat(astr, '\0');
912 /* parse the GCC asm() instruction */
913 static void asm_instr(void)
915 CString astr, astr1;
916 ASMOperand operands[MAX_ASM_OPERANDS];
917 int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg;
918 uint8_t clobber_regs[NB_ASM_REGS];
920 next();
921 /* since we always generate the asm() instruction, we can ignore
922 volatile */
923 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
924 next();
926 parse_asm_str(&astr);
927 nb_operands = 0;
928 nb_outputs = 0;
929 must_subst = 0;
930 memset(clobber_regs, 0, sizeof(clobber_regs));
931 if (tok == ':') {
932 next();
933 must_subst = 1;
934 /* output args */
935 parse_asm_operands(operands, &nb_operands, 1);
936 nb_outputs = nb_operands;
937 if (tok == ':') {
938 next();
939 if (tok != ')') {
940 /* input args */
941 parse_asm_operands(operands, &nb_operands, 0);
942 if (tok == ':') {
943 /* clobber list */
944 /* XXX: handle registers */
945 next();
946 for(;;) {
947 if (tok != TOK_STR)
948 expect("string constant");
949 asm_clobber(clobber_regs, tokc.cstr->data);
950 next();
951 if (tok == ',') {
952 next();
953 } else {
954 break;
961 skip(')');
962 /* NOTE: we do not eat the ';' so that we can restore the current
963 token after the assembler parsing */
964 if (tok != ';')
965 expect("';'");
966 nb_inputs = nb_operands - nb_outputs;
968 /* save all values in the memory */
969 save_regs(0);
971 /* compute constraints */
972 asm_compute_constraints(operands, nb_operands, nb_outputs,
973 clobber_regs, &out_reg);
975 /* substitute the operands in the asm string. No substitution is
976 done if no operands (GCC behaviour) */
977 #ifdef ASM_DEBUG
978 printf("asm: \"%s\"\n", (char *)astr.data);
979 #endif
980 if (must_subst) {
981 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
982 cstr_free(&astr);
983 } else {
984 astr1 = astr;
986 #ifdef ASM_DEBUG
987 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
988 #endif
990 /* generate loads */
991 asm_gen_code(operands, nb_operands, nb_outputs, 0,
992 clobber_regs, out_reg);
994 /* assemble the string with tcc internal assembler */
995 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
997 /* restore the current C token */
998 next();
1000 /* store the output values if needed */
1001 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1002 clobber_regs, out_reg);
1004 /* free everything */
1005 for(i=0;i<nb_operands;i++) {
1006 ASMOperand *op;
1007 op = &operands[i];
1008 tcc_free(op->constraint);
1009 vpop();
1011 cstr_free(&astr1);
1014 static void asm_global_instr(void)
1016 CString astr;
1018 next();
1019 parse_asm_str(&astr);
1020 skip(')');
1021 /* NOTE: we do not eat the ';' so that we can restore the current
1022 token after the assembler parsing */
1023 if (tok != ';')
1024 expect("';'");
1026 #ifdef ASM_DEBUG
1027 printf("asm_global: \"%s\"\n", (char *)astr.data);
1028 #endif
1029 cur_text_section = text_section;
1030 ind = cur_text_section->data_offset;
1032 /* assemble the string with tcc internal assembler */
1033 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1035 cur_text_section->data_offset = ind;
1037 /* restore the current C token */
1038 next();
1040 cstr_free(&astr);