2 * GAS like assembler for TCC
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
)
26 snprintf(buf
, sizeof(buf
), "L..%u", n
);
27 ts
= tok_alloc(buf
, strlen(buf
));
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
)
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
);
51 /* backward : find the last corresponding defined label */
52 if (sym
&& sym
->r
== 0)
55 error("local label '%d' not found backward", n
);
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
;
66 } else if (*p
== '\0') {
70 error("invalid number syntax");
76 asm_expr_unary(s1
, pe
);
82 asm_expr_unary(s1
, pe
);
84 error("invalid operation with label");
102 if (tok
>= TOK_IDENT
) {
103 /* label case : if the label was not found, add one */
104 sym
= label_find(tok
);
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 */
120 error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
126 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
131 asm_expr_unary(s1
, pe
);
134 if (op
!= '*' && op
!= '/' && op
!= '%' &&
135 op
!= TOK_SHL
&& op
!= TOK_SAR
)
138 asm_expr_unary(s1
, &e2
);
139 if (pe
->sym
|| e2
.sym
)
140 error("invalid operation with label");
148 error("division by zero");
168 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
173 asm_expr_prod(s1
, pe
);
176 if (op
!= '&' && op
!= '|' && op
!= '^')
179 asm_expr_prod(s1
, &e2
);
180 if (pe
->sym
|| e2
.sym
)
181 error("invalid operation with label");
197 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
202 asm_expr_logic(s1
, pe
);
205 if (op
!= '+' && op
!= '-')
208 asm_expr_logic(s1
, &e2
);
210 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
211 goto cannot_relocate
;
213 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
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
) {
221 } else if (pe
->sym
&& !e2
.sym
) {
223 } else if (pe
->sym
&& e2
.sym
) {
224 if (pe
->sym
== e2
.sym
) {
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
;
230 goto cannot_relocate
;
232 pe
->sym
= NULL
; /* same symbols can be substracted to NULL */
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
)
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
)
262 sym
= label_find(label
);
265 /* the label is already defined */
267 error("assembler label '%s' already defined",
268 get_tok_str(label
, NULL
));
270 /* redefinition of local labels is possible */
276 sym
= label_push(&s1
->asm_labels
, label
, 0);
277 sym
->type
.t
= VT_STATIC
| VT_VOID
;
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
)
293 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
295 /* define symbol value in object file */
300 sec
= st
->sections
[s
->r
];
301 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
304 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
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
)
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
;
330 /* assembler directive */
332 sec
= cur_text_section
;
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
;
345 /* the section must have a compatible alignment */
346 if (sec
->sh_addralign
< n
)
347 sec
->sh_addralign
= n
;
354 v
= asm_int_expr(s1
);
357 if (sec
->sh_type
!= SHT_NOBITS
) {
358 sec
->data_offset
= ind
;
359 ptr
= section_ptr_add(sec
, size
);
360 memset(ptr
, v
, size
);
371 if (tok
!= TOK_PPNUM
) {
373 error("64 bit constant");
375 vl
= strtoll(p
, (char **)&p
, 0);
379 if (sec
->sh_type
!= SHT_NOBITS
) {
380 /* XXX: endianness */
406 if (sec
->sh_type
!= SHT_NOBITS
) {
427 int repeat
, size
, val
, i
, j
;
428 uint8_t repeat_buf
[8];
430 repeat
= asm_int_expr(s1
);
432 error("repeat < 0; .fill ignored");
439 size
= asm_int_expr(s1
);
441 error("size < 0; .fill ignored");
448 val
= asm_int_expr(s1
);
451 /* XXX: endianness */
453 repeat_buf
[1] = val
>> 8;
454 repeat_buf
[2] = val
>> 16;
455 repeat_buf
[3] = val
>> 24;
460 for(i
= 0; i
< repeat
; i
++) {
461 for(j
= 0; j
< size
; j
++) {
471 /* XXX: handle section symbols too */
472 n
= asm_int_expr(s1
);
474 error("attempt to .org backwards");
486 sym
= label_find(tok
);
488 sym
= label_push(&s1
->asm_labels
, tok
, 0);
489 sym
->type
.t
= VT_VOID
;
491 sym
->type
.t
&= ~VT_STATIC
;
506 expect("string constant");
508 size
= tokc
.cstr
->size
;
509 if (t
== TOK_ASM_ascii
&& size
> 0)
511 for(i
= 0; i
< size
; i
++)
516 } else if (tok
!= TOK_STR
) {
530 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
531 n
= asm_int_expr(s1
);
534 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
535 use_section(s1
, sname
);
542 /* XXX: support more options */
545 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
547 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
549 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
553 /* skip section options */
556 expect("string constant");
559 last_text_section
= cur_text_section
;
560 use_section(s1
, sname
);
563 case TOK_ASM_previous
:
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
;
574 #ifdef TCC_TARGET_I386
589 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
595 /* assemble a file */
596 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
601 /* print stats about opcodes */
606 int nb_op_vals
, i
, j
;
609 memset(freq
, 0, sizeof(freq
));
610 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
612 for(i
=0;i
<pa
->nb_ops
;i
++) {
613 for(j
=0;j
<nb_op_vals
;j
++) {
614 if (pa
->op_type
[i
] == op_vals
[j
])
617 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
621 for(i
=0;i
<nb_op_vals
;i
++) {
623 if ((v
& (v
- 1)) != 0)
624 printf("%3d: %08x\n", i
, v
);
626 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
627 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
628 freq
[0], freq
[1], freq
[2], freq
[3]);
632 /* XXX: undefine C labels */
634 ch
= file
->buf_ptr
[0];
635 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
636 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
638 parse_flags
|= PARSE_FLAG_PREPROCESS
;
643 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
646 /* horrible gas comment */
647 while (tok
!= TOK_LINEFEED
)
649 } else if (tok
== '.') {
650 asm_parse_directive(s1
);
651 } else if (tok
== TOK_PPNUM
) {
655 n
= strtoul(p
, (char **)&p
, 10);
658 /* new local label */
659 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
663 } else if (tok
>= TOK_IDENT
) {
664 /* instruction or label */
669 asm_new_label(s1
, opcode
, 0);
672 } else if (tok
== '=') {
675 n
= asm_int_expr(s1
);
676 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
679 asm_opcode(s1
, opcode
);
683 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
684 expect("end of line");
686 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
695 /* Assemble the current file */
696 static int tcc_assemble(TCCState
*s1
, int do_preprocess
)
703 /* default section is text */
704 cur_text_section
= text_section
;
705 ind
= cur_text_section
->data_offset
;
707 define_start
= define_stack
;
709 ret
= tcc_assemble_internal(s1
, do_preprocess
);
711 cur_text_section
->data_offset
= ind
;
713 free_defines(define_start
);
718 /********************************************************************/
719 /* GCC inline asm support */
721 /* assemble the string 'str' in the current C compilation unit without
722 C preprocessing. NOTE: str is modified by modifying the '\0' at the
724 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
726 BufferedFile
*bf
, *saved_file
;
727 int saved_parse_flags
, *saved_macro_ptr
;
729 bf
= tcc_malloc(sizeof(BufferedFile
));
730 memset(bf
, 0, sizeof(BufferedFile
));
733 bf
->buf_end
= str
+ len
;
735 /* same name as current file so that errors are correctly
737 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
738 bf
->line_num
= file
->line_num
;
741 saved_parse_flags
= parse_flags
;
742 saved_macro_ptr
= macro_ptr
;
745 tcc_assemble_internal(s1
, 0);
747 parse_flags
= saved_parse_flags
;
748 macro_ptr
= saved_macro_ptr
;
753 /* find a constraint by its number or id (gcc 3 extended
754 syntax). return -1 if not found. Return in *pp in char after the
756 static int find_constraint(ASMOperand
*operands
, int nb_operands
,
757 const char *name
, const char **pp
)
765 while (isnum(*name
)) {
766 index
= (index
* 10) + (*name
) - '0';
769 if ((unsigned)index
>= nb_operands
)
771 } else if (*name
== '[') {
773 p
= strchr(name
, ']');
775 ts
= tok_alloc(name
, p
- name
);
776 for(index
= 0; index
< nb_operands
; index
++) {
777 if (operands
[index
].id
== ts
->tok
)
794 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
796 CString
*out_str
, CString
*in_str
)
798 int c
, index
, modifier
;
813 if (*str
== 'c' || *str
== 'n' ||
814 *str
== 'b' || *str
== 'w' || *str
== 'h')
816 index
= find_constraint(operands
, nb_operands
, str
, &str
);
818 error("invalid operand reference after %%");
819 op
= &operands
[index
];
823 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
826 subst_asm_operand(out_str
, &sv
, modifier
);
829 cstr_ccat(out_str
, c
);
837 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
844 nb_operands
= *nb_operands_ptr
;
846 if (nb_operands
>= MAX_ASM_OPERANDS
)
847 error("too many asm operands");
848 op
= &operands
[nb_operands
++];
853 expect("identifier");
859 expect("string constant");
860 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
861 strcpy(op
->constraint
, tokc
.cstr
->data
);
868 /* we want to avoid LLOCAL case, except when the 'm'
869 constraint is used. Note that it may come from
870 register storage, so we need to convert (reg)
872 if ((vtop
->r
& VT_LVAL
) &&
873 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
874 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
875 !strchr(op
->constraint
, 'm')) {
887 *nb_operands_ptr
= nb_operands
;
891 static void parse_asm_str(CString
*astr
)
894 /* read the string */
896 expect("string constant");
898 while (tok
== TOK_STR
) {
899 /* XXX: add \0 handling too ? */
900 cstr_cat(astr
, tokc
.cstr
->data
);
903 cstr_ccat(astr
, '\0');
906 /* parse the GCC asm() instruction */
907 static void asm_instr(void)
910 ASMOperand operands
[MAX_ASM_OPERANDS
];
911 int nb_inputs
, nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
912 uint8_t clobber_regs
[NB_ASM_REGS
];
915 /* since we always generate the asm() instruction, we can ignore
917 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
920 parse_asm_str(&astr
);
924 memset(clobber_regs
, 0, sizeof(clobber_regs
));
929 parse_asm_operands(operands
, &nb_operands
, 1);
930 nb_outputs
= nb_operands
;
935 parse_asm_operands(operands
, &nb_operands
, 0);
938 /* XXX: handle registers */
942 expect("string constant");
943 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
956 /* NOTE: we do not eat the ';' so that we can restore the current
957 token after the assembler parsing */
960 nb_inputs
= nb_operands
- nb_outputs
;
962 /* save all values in the memory */
965 /* compute constraints */
966 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
967 clobber_regs
, &out_reg
);
969 /* substitute the operands in the asm string. No substitution is
970 done if no operands (GCC behaviour) */
972 printf("asm: \"%s\"\n", (char *)astr
.data
);
975 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
981 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
985 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
986 clobber_regs
, out_reg
);
988 /* assemble the string with tcc internal assembler */
989 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
991 /* restore the current C token */
994 /* store the output values if needed */
995 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
996 clobber_regs
, out_reg
);
998 /* free everything */
999 for(i
=0;i
<nb_operands
;i
++) {
1002 tcc_free(op
->constraint
);
1008 static void asm_global_instr(void)
1013 parse_asm_str(&astr
);
1015 /* NOTE: we do not eat the ';' so that we can restore the current
1016 token after the assembler parsing */
1021 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1023 cur_text_section
= text_section
;
1024 ind
= cur_text_section
->data_offset
;
1026 /* assemble the string with tcc internal assembler */
1027 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1029 cur_text_section
->data_offset
= ind
;
1031 /* restore the current C token */