2 * GAS like assembler for TCC
4 * Copyright (c) 2001, 2002 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
)
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");
95 if (tok
>= TOK_IDENT
) {
96 /* label case : if the label was not found, add one */
97 sym
= label_find(tok
);
99 sym
= label_push(&s1
->asm_labels
, tok
, 0);
100 /* NOTE: by default, the symbol is global */
101 sym
->type
.t
= VT_VOID
;
107 error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
113 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
118 asm_expr_unary(s1
, pe
);
121 if (op
!= '*' && op
!= '/' && op
!= '%' &&
122 op
!= TOK_SHL
&& op
!= TOK_SAR
)
125 asm_expr_unary(s1
, &e2
);
126 if (pe
->sym
|| e2
.sym
)
127 error("invalid operation with label");
135 error("division by zero");
155 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
160 asm_expr_prod(s1
, pe
);
163 if (op
!= '&' && op
!= '|' && op
!= '^')
166 asm_expr_prod(s1
, &e2
);
167 if (pe
->sym
|| e2
.sym
)
168 error("invalid operation with label");
184 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
189 asm_expr_logic(s1
, pe
);
192 if (op
!= '+' && op
!= '-')
195 asm_expr_logic(s1
, &e2
);
197 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
198 goto cannot_relocate
;
200 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
204 /* NOTE: we are less powerful than gas in that case
205 because we store only one symbol in the expression */
206 if (!pe
->sym
&& !e2
.sym
) {
208 } else if (pe
->sym
&& !e2
.sym
) {
210 } else if (pe
->sym
&& e2
.sym
) {
211 if (pe
->sym
== e2
.sym
) {
213 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
214 /* we also accept defined symbols in the same section */
215 pe
->v
+= (long)pe
->sym
->next
- (long)e2
.sym
->next
;
217 goto cannot_relocate
;
219 pe
->sym
= NULL
; /* same symbols can be substracted to NULL */
222 error("invalid operation with label");
228 static void asm_expr(TCCState
*s1
, ExprValue
*pe
)
230 asm_expr_sum(s1
, pe
);
233 static int asm_int_expr(TCCState
*s1
)
242 /* NOTE: the same name space as C labels is used to avoid using too
243 much memory when storing labels in TokenStrings */
244 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
248 sym
= label_find(label
);
251 /* the label is already defined */
253 error("assembler label '%s' already defined",
254 get_tok_str(label
, NULL
));
256 /* redefinition of local labels is possible */
262 sym
= label_push(&s1
->asm_labels
, label
, 0);
263 sym
->type
.t
= VT_STATIC
| VT_VOID
;
265 sym
->r
= cur_text_section
->sh_num
;
266 sym
->next
= (void *)ind
;
269 static void asm_free_labels(TCCState
*st
)
272 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
274 /* define symbol value in object file */
276 put_extern_sym(s
, st
->sections
[s
->r
], (long)s
->next
, 0);
279 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
282 st
->asm_labels
= NULL
;
285 static void use_section(TCCState
*s1
, const char *name
)
288 sec
= find_section(s1
, name
);
289 cur_text_section
->data_offset
= ind
;
290 cur_text_section
= sec
;
291 ind
= cur_text_section
->data_offset
;
294 static void asm_parse_directive(TCCState
*s1
)
296 int n
, offset
, v
, size
, tok1
;
300 /* assembler directive */
302 sec
= cur_text_section
;
309 n
= asm_int_expr(s1
);
310 if (tok1
== TOK_ASM_align
) {
311 if (n
< 0 || (n
& (n
-1)) != 0)
312 error("alignment must be a positive power of two");
313 offset
= (ind
+ n
- 1) & -n
;
321 v
= asm_int_expr(s1
);
323 if (sec
->sh_type
!= SHT_NOBITS
) {
324 sec
->data_offset
= ind
;
325 ptr
= section_ptr_add(sec
, size
);
326 memset(ptr
, v
, size
);
345 if (sec
->sh_type
!= SHT_NOBITS
) {
370 sym
= label_find(tok
);
372 sym
= label_push(&s1
->asm_labels
, tok
, 0);
373 sym
->type
.t
= VT_VOID
;
375 sym
->type
.t
&= ~VT_STATIC
;
386 expect("string constant");
388 for(i
= 0; i
< tokc
.cstr
->size
; i
++)
401 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
402 n
= asm_int_expr(s1
);
405 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
406 use_section(s1
, sname
);
413 /* XXX: support more options */
416 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
418 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
420 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
423 use_section(s1
, sname
);
427 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
433 /* assemble a file */
434 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
439 /* print stats about opcodes */
444 int nb_op_vals
, i
, j
;
447 memset(freq
, 0, sizeof(freq
));
448 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
450 for(i
=0;i
<pa
->nb_ops
;i
++) {
451 for(j
=0;j
<nb_op_vals
;j
++) {
452 if (pa
->op_type
[i
] == op_vals
[j
])
455 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
459 for(i
=0;i
<nb_op_vals
;i
++) {
461 if ((v
& (v
- 1)) != 0)
462 printf("%3d: %08x\n", i
, v
);
464 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
465 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
466 freq
[0], freq
[1], freq
[2], freq
[3]);
470 /* XXX: undefine C labels */
472 ch
= file
->buf_ptr
[0];
473 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
476 parse_flags
|= PARSE_FLAG_PREPROCESS
;
481 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
484 /* horrible gas comment */
485 while (tok
!= TOK_LINEFEED
)
487 } else if (tok
== '.') {
488 asm_parse_directive(s1
);
489 } else if (tok
== TOK_PPNUM
) {
493 n
= strtoul(p
, (char **)&p
, 10);
496 /* new local label */
497 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
501 } else if (tok
>= TOK_IDENT
) {
502 /* instruction or label */
507 asm_new_label(s1
, opcode
, 0);
511 asm_opcode(s1
, opcode
);
515 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
516 expect("end of line");
518 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
527 /* Assemble the current file */
528 static int tcc_assemble(TCCState
*s1
, int do_preprocess
)
534 /* default section is text */
535 cur_text_section
= text_section
;
536 ind
= cur_text_section
->data_offset
;
538 ret
= tcc_assemble_internal(s1
, do_preprocess
);
540 cur_text_section
->data_offset
= ind
;
544 /********************************************************************/
545 /* GCC inline asm support */
547 /* assemble the string 'str' in the current C compilation unit without
548 C preprocessing. NOTE: str is modified by modifying the '\0' at the
550 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
552 BufferedFile
*bf
, *saved_file
;
553 int saved_parse_flags
, *saved_macro_ptr
;
555 bf
= tcc_malloc(sizeof(BufferedFile
));
556 memset(bf
, 0, sizeof(BufferedFile
));
559 bf
->buf_end
= str
+ len
;
561 /* same name as current file so that errors are correctly
563 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
564 bf
->line_num
= file
->line_num
;
567 saved_parse_flags
= parse_flags
;
568 saved_macro_ptr
= macro_ptr
;
571 tcc_assemble_internal(s1
, 0);
573 parse_flags
= saved_parse_flags
;
574 macro_ptr
= saved_macro_ptr
;
579 /* find a constraint by its number or id (gcc 3 extended
580 syntax). return -1 if not found. Return in *pp in char after the
582 static int find_constraint(ASMOperand
*operands
, int nb_operands
,
583 const char *name
, const char **pp
)
591 while (isnum(*name
)) {
592 index
= (index
* 10) + (*name
) - '0';
595 if ((unsigned)index
>= nb_operands
)
597 } else if (*name
== '[') {
599 p
= strchr(name
, ']');
601 ts
= tok_alloc(name
, p
- name
);
602 for(index
= 0; index
< nb_operands
; index
++) {
603 if (operands
[index
].id
== ts
->tok
)
620 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
622 CString
*out_str
, CString
*in_str
)
624 int c
, index
, modifier
;
639 if (*str
== 'c' || *str
== 'n' ||
640 *str
== 'b' || *str
== 'w' || *str
== 'h')
642 index
= find_constraint(operands
, nb_operands
, str
, &str
);
644 error("invalid operand reference after %%");
645 op
= &operands
[index
];
649 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
)
652 subst_asm_operand(out_str
, &sv
, modifier
);
655 cstr_ccat(out_str
, c
);
663 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
670 nb_operands
= *nb_operands_ptr
;
672 if (nb_operands
>= MAX_ASM_OPERANDS
)
673 error("too many asm operands");
674 op
= &operands
[nb_operands
++];
679 expect("identifier");
685 expect("string constant");
686 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
687 strcpy(op
->constraint
, tokc
.cstr
->data
);
694 /* we want to avoid LLOCAL case. note that it may come
695 from register storage, so we need to convert (reg)
697 if ((vtop
->r
& VT_LVAL
) &&
698 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
699 (vtop
->r
& VT_VALMASK
) < VT_CONST
)) {
711 *nb_operands_ptr
= nb_operands
;
715 /* parse the GCC asm() instruction */
716 static void asm_instr(void)
719 ASMOperand operands
[MAX_ASM_OPERANDS
];
720 int nb_inputs
, nb_outputs
, nb_operands
, i
;
721 uint8_t input_regs_allocated
[NB_ASM_REGS
];
722 uint8_t output_regs_allocated
[NB_ASM_REGS
];
723 uint8_t clobber_regs
[NB_ASM_REGS
];
726 /* since we always generate the asm() instruction, we can ignore
728 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
732 /* read the string */
734 expect("string constant");
736 while (tok
== TOK_STR
) {
737 /* XXX: add \0 handling too ? */
738 cstr_cat(&astr
, tokc
.cstr
->data
);
741 cstr_ccat(&astr
, '\0');
744 memset(clobber_regs
, 0, sizeof(clobber_regs
));
748 parse_asm_operands(operands
, &nb_operands
, 1);
749 nb_outputs
= nb_operands
;
753 parse_asm_operands(operands
, &nb_operands
, 0);
756 /* XXX: handle registers */
760 expect("string constant");
761 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
773 /* NOTE: we do not eat the ';' so that we can restore the current
774 token after the assembler parsing */
777 nb_inputs
= nb_operands
- nb_outputs
;
779 /* save all values in the memory */
782 /* compute constraints */
783 asm_compute_constraints(input_regs_allocated
,
784 operands
, nb_operands
, nb_outputs
, 0,
786 asm_compute_constraints(output_regs_allocated
,
787 operands
, nb_operands
, nb_outputs
, 1,
788 input_regs_allocated
);
790 /* substitute the operands in the asm string. No substitution is
791 done if no operands (GCC behaviour) */
793 printf("asm: \"%s\"\n", (char *)astr
.data
);
795 if (nb_operands
> 0) {
796 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
802 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
806 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0, clobber_regs
);
808 /* assemble the string with tcc internal assembler */
809 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
811 /* restore the current C token */
814 /* store the output values if needed */
815 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1, clobber_regs
);
817 /* free everything */
818 for(i
=0;i
<nb_operands
;i
++) {
821 tcc_free(op
->constraint
);