2 * GAS like assembler for TCC
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
)
26 snprintf(buf
, sizeof(buf
), "L..%u", n
);
27 ts
= tok_alloc(buf
, strlen(buf
));
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
)
43 n
= strtoul(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
);
49 /* backward : find the last corresponding defined label */
50 if (sym
&& sym
->r
== 0)
53 error("local label '%d' not found backward", n
);
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
;
64 } else if (*p
== '\0') {
68 error("invalid number syntax");
74 asm_expr_unary(s1
, pe
);
80 asm_expr_unary(s1
, pe
);
82 error("invalid operation with label");
89 if (tok
>= TOK_IDENT
) {
90 /* label case : if the label was not found, add one */
91 sym
= label_find(tok
);
93 sym
= label_push(&s1
->asm_labels
, tok
, 0);
94 /* NOTE: by default, the symbol is global */
95 sym
->type
.t
= VT_VOID
;
101 error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
107 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
112 asm_expr_unary(s1
, pe
);
115 if (op
!= '*' && op
!= '/' && op
!= '%' &&
116 op
!= TOK_SHL
&& op
!= TOK_SAR
)
119 asm_expr_unary(s1
, &e2
);
120 if (pe
->sym
|| e2
.sym
)
121 error("invalid operation with label");
129 error("division by zero");
149 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
154 asm_expr_prod(s1
, pe
);
157 if (op
!= '&' && op
!= '|' && op
!= '^')
160 asm_expr_prod(s1
, &e2
);
161 if (pe
->sym
|| e2
.sym
)
162 error("invalid operation with label");
178 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
183 asm_expr_logic(s1
, pe
);
186 if (op
!= '+' && op
!= '-')
189 asm_expr_logic(s1
, &e2
);
191 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
192 goto cannot_relocate
;
194 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
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
) {
202 } else if (pe
->sym
&& !e2
.sym
) {
204 } else if (pe
->sym
&& e2
.sym
) {
205 if (pe
->sym
== e2
.sym
) {
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
;
211 goto cannot_relocate
;
213 pe
->sym
= NULL
; /* same symbols can be substracted to NULL */
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
)
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
)
242 sym
= label_find(label
);
245 /* the label is already defined */
247 error("assembler label '%s' already defined",
248 get_tok_str(label
, NULL
));
250 /* redefinition of local labels is possible */
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
)
266 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
268 /* define symbol value in object file */
270 put_extern_sym(s
, st
->sections
[s
->r
], (long)s
->next
, 0);
273 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
276 st
->asm_labels
= NULL
;
279 static void asm_parse_directive(TCCState
*s1
)
281 int n
, offset
, v
, size
, tok1
;
285 /* assembler directive */
287 sec
= cur_text_section
;
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
;
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
);
330 if (sec
->sh_type
!= SHT_NOBITS
) {
350 error("unknown assembler directive .%s", get_tok_str(tok
, NULL
));
356 /* assemble a file */
357 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
362 /* print stats about opcodes */
367 int nb_op_vals
, i
, j
;
370 memset(freq
, 0, sizeof(freq
));
371 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
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
])
378 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
382 for(i
=0;i
<nb_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]);
393 /* XXX: undefine C labels */
395 ch
= file
->buf_ptr
[0];
396 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
399 parse_flags
|= PARSE_FLAG_PREPROCESS
;
404 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
407 /* horrible gas comment */
408 while (tok
!= TOK_LINEFEED
)
410 } else if (tok
== '.') {
411 asm_parse_directive(s1
);
412 } else if (tok
== TOK_PPNUM
) {
416 n
= strtoul(p
, (char **)&p
, 10);
419 /* new local label */
420 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
424 } else if (tok
>= TOK_IDENT
) {
425 /* instruction or label */
430 asm_new_label(s1
, opcode
, 0);
434 asm_opcode(s1
, opcode
);
438 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
439 expect("end of line");
441 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
450 /* Assemble the current file */
451 static int tcc_assemble(TCCState
*s1
, int do_preprocess
)
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
;
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
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
));
482 bf
->buf_end
= str
+ len
;
484 /* same name as current file so that errors are correctly
486 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
487 bf
->line_num
= file
->line_num
;
490 saved_parse_flags
= parse_flags
;
491 saved_macro_ptr
= macro_ptr
;
494 tcc_assemble_internal(s1
, 0);
496 parse_flags
= saved_parse_flags
;
497 macro_ptr
= saved_macro_ptr
;
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
505 static int find_constraint(ASMOperand
*operands
, int nb_operands
,
506 const char *name
, const char **pp
)
514 while (isnum(*name
)) {
515 index
= (index
* 10) + (*name
) - '0';
518 if ((unsigned)index
>= nb_operands
)
520 } else if (*name
== '[') {
522 p
= strchr(name
, ']');
524 ts
= tok_alloc(name
, p
- name
);
525 for(index
= 0; index
< nb_operands
; index
++) {
526 if (operands
[index
].id
== ts
->tok
)
543 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
545 CString
*out_str
, CString
*in_str
)
547 int c
, index
, modifier
;
562 if (*str
== 'c' || *str
== 'n' ||
563 *str
== 'b' || *str
== 'w' || *str
== 'h')
565 index
= find_constraint(operands
, nb_operands
, str
, &str
);
567 error("invalid operand reference after %%");
568 op
= &operands
[index
];
572 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
)
575 subst_asm_operand(out_str
, &sv
, modifier
);
578 cstr_ccat(out_str
, c
);
586 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
593 nb_operands
= *nb_operands_ptr
;
595 if (nb_operands
>= MAX_ASM_OPERANDS
)
596 error("too many asm operands");
597 op
= &operands
[nb_operands
++];
602 expect("identifier");
608 expect("string constant");
609 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
610 strcpy(op
->constraint
, tokc
.cstr
->data
);
617 /* we want to avoid LLOCAL case. note that it may come
618 from register storage, so we need to convert (reg)
620 if ((vtop
->r
& VT_LVAL
) &&
621 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
622 (vtop
->r
& VT_VALMASK
) < VT_CONST
)) {
634 *nb_operands_ptr
= nb_operands
;
638 /* parse the GCC asm() instruction */
639 static void asm_instr(void)
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
];
649 /* since we always generate the asm() instruction, we can ignore
651 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
655 /* read the string */
657 expect("string constant");
659 while (tok
== TOK_STR
) {
660 /* XXX: add \0 handling too ? */
661 cstr_cat(&astr
, tokc
.cstr
->data
);
664 cstr_ccat(&astr
, '\0');
667 memset(clobber_regs
, 0, sizeof(clobber_regs
));
671 parse_asm_operands(operands
, &nb_operands
, 1);
672 nb_outputs
= nb_operands
;
676 parse_asm_operands(operands
, &nb_operands
, 0);
679 /* XXX: handle registers */
683 expect("string constant");
684 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
696 /* NOTE: we do not eat the ';' so that we can restore the current
697 token after the assembler parsing */
700 nb_inputs
= nb_operands
- nb_outputs
;
702 /* save all values in the memory */
705 /* compute constraints */
706 asm_compute_constraints(input_regs_allocated
,
707 operands
, nb_operands
, nb_outputs
, 0,
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. No substitution is
714 done if no operands (GCC behaviour) */
716 printf("asm: \"%s\"\n", (char *)astr
.data
);
718 if (nb_operands
> 0) {
719 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
725 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
729 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0, clobber_regs
);
731 /* assemble the string with tcc internal assembler */
732 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
734 /* restore the current C token */
737 /* store the output values if needed */
738 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1, clobber_regs
);
740 /* free everything */
741 for(i
=0;i
<nb_operands
;i
++) {
744 tcc_free(op
->constraint
);