asm support
[tinycc.git] / tccasm.c
blob5b3b3595302f6ecfc303a508d030986b860735a5
1 /*
2 * GAS like assembler for TCC
3 *
4 * Copyright (c) 2001, 2002 Fabrice Bellard
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 /* We do not use the C expression parser to handle symbols. Maybe the
32 C expression parser could be tweaked to do so. */
34 static void asm_expr_unary(TCCState *s1, ExprValue *pe)
36 Sym *sym;
37 int op, n, label;
38 const char *p;
40 switch(tok) {
41 case TOK_PPNUM:
42 p = tokc.cstr->data;
43 n = strtol(p, (char **)&p, 0);
44 if (*p == 'b' || *p == 'f') {
45 /* backward or forward label */
46 label = asm_get_local_label_name(s1, n);
47 sym = label_find(label);
48 if (*p == 'b') {
49 /* backward : find the last corresponding defined label */
50 if (sym && sym->r == 0)
51 sym = sym->prev_tok;
52 if (!sym)
53 error("local label '%d' not found backward", n);
54 } else {
55 /* forward */
56 if (!sym || sym->r) {
57 /* if the last label is defined, then define a new one */
58 sym = label_push(&s1->asm_labels, label, 0);
59 sym->type.t = VT_STATIC | VT_VOID;
62 pe->v = 0;
63 pe->sym = sym;
64 } else if (*p == '\0') {
65 pe->v = n;
66 pe->sym = NULL;
67 } else {
68 error("invalid number syntax");
70 next();
71 break;
72 case '+':
73 next();
74 asm_expr_unary(s1, pe);
75 break;
76 case '-':
77 case '~':
78 op = tok;
79 next();
80 asm_expr_unary(s1, pe);
81 if (pe->sym)
82 error("invalid operation with label");
83 if (op == '-')
84 pe->v = -pe->v;
85 else
86 pe->v = ~pe->v;
87 break;
88 default:
89 if (tok >= TOK_IDENT) {
90 /* label case : if the label was not found, add one */
91 sym = label_find(tok);
92 if (!sym) {
93 sym = label_push(&s1->asm_labels, tok, 0);
94 /* NOTE: by default, the symbol is global */
95 sym->type.t = VT_VOID;
97 pe->v = 0;
98 pe->sym = sym;
99 next();
100 } else {
101 error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
103 break;
107 static void asm_expr_prod(TCCState *s1, ExprValue *pe)
109 int op;
110 ExprValue e2;
112 asm_expr_unary(s1, pe);
113 for(;;) {
114 op = tok;
115 if (op != '*' && op != '/' && op != '%' &&
116 op != TOK_SHL && op != TOK_SAR)
117 break;
118 next();
119 asm_expr_unary(s1, &e2);
120 if (pe->sym || e2.sym)
121 error("invalid operation with label");
122 switch(op) {
123 case '*':
124 pe->v *= e2.v;
125 break;
126 case '/':
127 if (e2.v == 0) {
128 div_error:
129 error("division by zero");
131 pe->v /= e2.v;
132 break;
133 case '%':
134 if (e2.v == 0)
135 goto div_error;
136 pe->v %= e2.v;
137 break;
138 case TOK_SHL:
139 pe->v <<= e2.v;
140 break;
141 default:
142 case TOK_SAR:
143 pe->v >>= e2.v;
144 break;
149 static void asm_expr_logic(TCCState *s1, ExprValue *pe)
151 int op;
152 ExprValue e2;
154 asm_expr_prod(s1, pe);
155 for(;;) {
156 op = tok;
157 if (op != '&' && op != '|' && op != '^')
158 break;
159 next();
160 asm_expr_prod(s1, &e2);
161 if (pe->sym || e2.sym)
162 error("invalid operation with label");
163 switch(op) {
164 case '&':
165 pe->v &= e2.v;
166 break;
167 case '|':
168 pe->v |= e2.v;
169 break;
170 default:
171 case '^':
172 pe->v ^= e2.v;
173 break;
178 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
180 int op;
181 ExprValue e2;
183 asm_expr_logic(s1, pe);
184 for(;;) {
185 op = tok;
186 if (op != '+' && op != '-')
187 break;
188 next();
189 asm_expr_logic(s1, &e2);
190 if (op == '+') {
191 if (pe->sym != NULL && e2.sym != NULL)
192 goto cannot_relocate;
193 pe->v += e2.v;
194 if (pe->sym == NULL && e2.sym != NULL)
195 pe->sym = e2.sym;
196 } else {
197 pe->v -= e2.v;
198 /* NOTE: we are less powerful than gas in that case
199 because we store only one symbol in the expression */
200 if (!pe->sym && !e2.sym) {
201 /* OK */
202 } else if (pe->sym && !e2.sym) {
203 /* OK */
204 } else if (pe->sym && e2.sym) {
205 if (pe->sym == e2.sym) {
206 /* OK */
207 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
208 /* we also accept defined symbols in the same section */
209 pe->v += (long)pe->sym->next - (long)e2.sym->next;
210 } else {
211 goto cannot_relocate;
213 pe->sym = NULL; /* same symbols can be substracted to NULL */
214 } else {
215 cannot_relocate:
216 error("invalid operation with label");
222 static void asm_expr(TCCState *s1, ExprValue *pe)
224 asm_expr_sum(s1, pe);
227 static int asm_int_expr(TCCState *s1)
229 ExprValue e;
230 asm_expr(s1, &e);
231 if (e.sym)
232 expect("constant");
233 return e.v;
236 /* NOTE: the same name space as C labels is used to avoid using too
237 much memory when storing labels in TokenStrings */
238 static void asm_new_label(TCCState *s1, int label, int is_local)
240 Sym *sym;
242 sym = label_find(label);
243 if (sym) {
244 if (sym->r) {
245 /* the label is already defined */
246 if (!is_local) {
247 error("assembler label '%s' already defined",
248 get_tok_str(label, NULL));
249 } else {
250 /* redefinition of local labels is possible */
251 goto new_label;
254 } else {
255 new_label:
256 sym = label_push(&s1->asm_labels, label, 0);
257 sym->type.t = VT_STATIC | VT_VOID;
259 sym->r = cur_text_section->sh_num;
260 sym->next = (void *)ind;
263 static void asm_free_labels(TCCState *st)
265 Sym *s, *s1;
266 for(s = st->asm_labels; s != NULL; s = s1) {
267 s1 = s->prev;
268 /* define symbol value in object file */
269 if (s->r) {
270 put_extern_sym(s, st->sections[s->r], (long)s->next, 0);
272 /* remove label */
273 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
274 tcc_free(s);
276 st->asm_labels = NULL;
279 static void asm_parse_directive(TCCState *s1)
281 int n, offset, v, size, tok1;
282 Section *sec;
283 uint8_t *ptr;
285 /* assembler directive */
286 next();
287 sec = cur_text_section;
288 switch(tok) {
289 case TOK_ASM_align:
290 case TOK_ASM_skip:
291 case TOK_ASM_space:
292 tok1 = tok;
293 next();
294 n = asm_int_expr(s1);
295 if (tok1 == TOK_ASM_align) {
296 if (n < 0 || (n & (n-1)) != 0)
297 error("alignment must be a positive power of two");
298 offset = (ind + n - 1) & -n;
299 size = offset - ind;
300 } else {
301 size = n;
303 v = 0;
304 if (tok == ',') {
305 next();
306 v = asm_int_expr(s1);
308 if (sec->sh_type != SHT_NOBITS) {
309 sec->data_offset = ind;
310 ptr = section_ptr_add(sec, size);
311 memset(ptr, v, size);
313 ind += size;
314 break;
315 case TOK_ASM_byte:
316 size = 1;
317 goto asm_data;
318 case TOK_ASM_word:
319 case TOK_SHORT:
320 size = 2;
321 goto asm_data;
322 case TOK_LONG:
323 case TOK_INT:
324 size = 4;
325 asm_data:
326 next();
327 for(;;) {
328 ExprValue e;
329 asm_expr(s1, &e);
330 if (sec->sh_type != SHT_NOBITS) {
331 if (size == 4) {
332 gen_expr32(&e);
333 } else {
334 if (e.sym)
335 expect("constant");
336 if (size == 1)
337 g(e.v);
338 else
339 gen_le16(e.v);
341 } else {
342 ind += size;
344 if (tok != ',')
345 break;
346 next();
348 break;
349 default:
350 error("unknown assembler directive .%s", get_tok_str(tok, NULL));
351 break;
356 /* assemble a file */
357 static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
359 int opcode;
361 #if 0
362 /* print stats about opcodes */
364 const ASMInstr *pa;
365 int freq[4];
366 int op_vals[500];
367 int nb_op_vals, i, j;
369 nb_op_vals = 0;
370 memset(freq, 0, sizeof(freq));
371 for(pa = asm_instrs; pa->sym != 0; pa++) {
372 freq[pa->nb_ops]++;
373 for(i=0;i<pa->nb_ops;i++) {
374 for(j=0;j<nb_op_vals;j++) {
375 if (pa->op_type[i] == op_vals[j])
376 goto found;
378 op_vals[nb_op_vals++] = pa->op_type[i];
379 found: ;
382 for(i=0;i<nb_op_vals;i++) {
383 int v = op_vals[i];
384 if ((v & (v - 1)) != 0)
385 printf("%3d: %08x\n", i, v);
387 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
388 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
389 freq[0], freq[1], freq[2], freq[3]);
391 #endif
393 /* XXX: undefine C labels */
395 ch = file->buf_ptr[0];
396 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
397 parse_flags = 0;
398 if (do_preprocess)
399 parse_flags |= PARSE_FLAG_PREPROCESS;
400 next();
401 for(;;) {
402 if (tok == TOK_EOF)
403 break;
404 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
405 redo:
406 if (tok == '#') {
407 /* horrible gas comment */
408 while (tok != TOK_LINEFEED)
409 next();
410 } else if (tok == '.') {
411 asm_parse_directive(s1);
412 } else if (tok == TOK_PPNUM) {
413 const char *p;
414 int n;
415 p = tokc.cstr->data;
416 n = strtol(p, (char **)&p, 10);
417 if (*p != '\0')
418 expect("':'");
419 /* new local label */
420 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
421 next();
422 skip(':');
423 goto redo;
424 } else if (tok >= TOK_IDENT) {
425 /* instruction or label */
426 opcode = tok;
427 next();
428 if (tok == ':') {
429 /* new label */
430 asm_new_label(s1, opcode, 0);
431 next();
432 goto redo;
433 } else {
434 asm_opcode(s1, opcode);
437 /* end of line */
438 if (tok != ';' && tok != TOK_LINEFEED){
439 expect("end of line");
441 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
442 next();
445 asm_free_labels(s1);
447 return 0;
450 /* Assemble the current file */
451 static int tcc_assemble(TCCState *s1, int do_preprocess)
453 int ret;
455 preprocess_init(s1);
457 /* default section is text */
458 cur_text_section = text_section;
459 ind = cur_text_section->data_offset;
461 ret = tcc_assemble_internal(s1, do_preprocess);
463 cur_text_section->data_offset = ind;
464 return ret;
467 /********************************************************************/
468 /* GCC inline asm support */
470 /* assemble the string 'str' in the current C compilation unit without
471 C preprocessing. NOTE: str is modified by modifying the '\0' at the
472 end */
473 static void tcc_assemble_inline(TCCState *s1, char *str, int len)
475 BufferedFile *bf, *saved_file;
476 int saved_parse_flags, *saved_macro_ptr;
478 bf = tcc_malloc(sizeof(BufferedFile));
479 memset(bf, 0, sizeof(BufferedFile));
480 bf->fd = -1;
481 bf->buf_ptr = str;
482 bf->buf_end = str + len;
483 str[len] = CH_EOB;
484 /* same name as current file so that errors are correctly
485 reported */
486 pstrcpy(bf->filename, sizeof(bf->filename), file->filename);
487 bf->line_num = file->line_num;
488 saved_file = file;
489 file = bf;
490 saved_parse_flags = parse_flags;
491 saved_macro_ptr = macro_ptr;
492 macro_ptr = NULL;
494 tcc_assemble_internal(s1, 0);
496 parse_flags = saved_parse_flags;
497 macro_ptr = saved_macro_ptr;
498 file = saved_file;
499 tcc_free(bf);
502 /* find a constraint by its number or id (gcc 3 extended
503 syntax). return -1 if not found. Return in *pp in char after the
504 constraint */
505 static int find_constraint(ASMOperand *operands, int nb_operands,
506 const char *name, const char **pp)
508 int index;
509 TokenSym *ts;
510 const char *p;
512 if (isnum(*name)) {
513 index = 0;
514 while (isnum(*name)) {
515 index = (index * 10) + (*name) - '0';
516 name++;
518 if ((unsigned)index >= nb_operands)
519 index = -1;
520 } else if (*name == '[') {
521 name++;
522 p = strchr(name, ']');
523 if (p) {
524 ts = tok_alloc(name, p - name);
525 for(index = 0; index < nb_operands; index++) {
526 if (operands[index].id == ts->tok)
527 goto found;
529 index = -1;
530 found:
531 name = p + 1;
532 } else {
533 index = -1;
535 } else {
536 index = -1;
538 if (pp)
539 *pp = name;
540 return index;
543 static void subst_asm_operands(ASMOperand *operands, int nb_operands,
544 int nb_outputs,
545 CString *out_str, CString *in_str)
547 int c, index, modifier;
548 const char *str;
549 ASMOperand *op;
550 SValue sv;
552 cstr_new(out_str);
553 str = in_str->data;
554 for(;;) {
555 c = *str++;
556 if (c == '%') {
557 if (*str == '%') {
558 str++;
559 goto add_char;
561 modifier = 0;
562 if (*str == 'c' || *str == 'n' ||
563 *str == 'b' || *str == 'w' || *str == 'h')
564 modifier = *str++;
565 index = find_constraint(operands, nb_operands, str, &str);
566 if (index < 0)
567 error("invalid operand reference after %%");
568 op = &operands[index];
569 sv = *op->vt;
570 if (op->reg >= 0) {
571 sv.r = op->reg;
572 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL)
573 sv.r |= VT_LVAL;
575 subst_asm_operand(out_str, &sv, modifier);
576 } else {
577 add_char:
578 cstr_ccat(out_str, c);
579 if (c == '\0')
580 break;
586 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
587 int is_output)
589 ASMOperand *op;
590 int nb_operands;
592 if (tok != ':') {
593 nb_operands = *nb_operands_ptr;
594 for(;;) {
595 if (nb_operands >= MAX_ASM_OPERANDS)
596 error("too many asm operands");
597 op = &operands[nb_operands++];
598 op->id = 0;
599 if (tok == '[') {
600 next();
601 if (tok < TOK_IDENT)
602 expect("identifier");
603 op->id = tok;
604 next();
605 skip(']');
607 if (tok != TOK_STR)
608 expect("string constant");
609 op->constraint = tcc_malloc(tokc.cstr->size);
610 strcpy(op->constraint, tokc.cstr->data);
611 next();
612 skip('(');
613 gexpr();
614 if (is_output) {
615 test_lvalue();
616 } else {
617 /* we want to avoid LLOCAL case. note that it may come
618 from register storage, so we need to convert (reg)
619 case */
620 if ((vtop->r & VT_LVAL) &&
621 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
622 (vtop->r & VT_VALMASK) < VT_CONST)) {
623 gv(RC_INT);
626 op->vt = vtop;
627 skip(')');
628 if (tok == ',') {
629 next();
630 } else {
631 break;
634 *nb_operands_ptr = nb_operands;
638 /* parse the GCC asm() instruction */
639 static void asm_instr(void)
641 CString astr, astr1;
642 ASMOperand operands[MAX_ASM_OPERANDS];
643 int nb_inputs, nb_outputs, nb_operands, i;
644 uint8_t input_regs_allocated[NB_ASM_REGS];
645 uint8_t output_regs_allocated[NB_ASM_REGS];
646 uint8_t clobber_regs[NB_ASM_REGS];
648 next();
649 /* since we always generate the asm() instruction, we can ignore
650 volatile */
651 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
652 next();
654 skip('(');
655 /* read the string */
656 if (tok != TOK_STR)
657 expect("string constant");
658 cstr_new(&astr);
659 while (tok == TOK_STR) {
660 /* XXX: add \0 handling too ? */
661 cstr_cat(&astr, tokc.cstr->data);
662 next();
664 cstr_ccat(&astr, '\0');
665 nb_operands = 0;
666 nb_outputs = 0;
667 memset(clobber_regs, 0, sizeof(clobber_regs));
668 if (tok == ':') {
669 next();
670 /* output args */
671 parse_asm_operands(operands, &nb_operands, 1);
672 nb_outputs = nb_operands;
673 if (tok == ':') {
674 next();
675 /* input args */
676 parse_asm_operands(operands, &nb_operands, 0);
677 if (tok == ':') {
678 /* clobber list */
679 /* XXX: handle registers */
680 next();
681 for(;;) {
682 if (tok != TOK_STR)
683 expect("string constant");
684 asm_clobber(clobber_regs, tokc.cstr->data);
685 next();
686 if (tok == ',') {
687 next();
688 } else {
689 break;
695 skip(')');
696 /* NOTE: we do not eat the ';' so that we can restore the current
697 token after the assembler parsing */
698 if (tok != ';')
699 expect("';'");
700 nb_inputs = nb_operands - nb_outputs;
702 /* save all values in the memory */
703 save_regs(0);
705 /* compute constraints */
706 asm_compute_constraints(input_regs_allocated,
707 operands, nb_operands, nb_outputs, 0,
708 NULL);
709 asm_compute_constraints(output_regs_allocated,
710 operands, nb_operands, nb_outputs, 1,
711 input_regs_allocated);
713 /* substitute the operands in the asm string */
714 #ifdef ASM_DEBUG
715 printf("asm: \"%s\"\n", (char *)astr.data);
716 #endif
717 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
718 cstr_free(&astr);
719 #ifdef ASM_DEBUG
720 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
721 #endif
723 /* generate loads */
724 asm_gen_code(operands, nb_operands, nb_outputs, 0, clobber_regs);
726 /* assemble the string with tcc internal assembler */
727 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
729 /* restore the current C token */
730 next();
732 /* store the output values if needed */
733 asm_gen_code(operands, nb_operands, nb_outputs, 1, clobber_regs);
735 /* free everything */
736 for(i=0;i<nb_operands;i++) {
737 ASMOperand *op;
738 op = &operands[i];
739 tcc_free(op->constraint);
740 vpop();
742 cstr_free(&astr1);