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
22 #ifdef TCC_TARGET_I386
23 #include "i386-asm-config.h"
26 #ifdef TCC_TARGET_X86_64
27 #include "x86_64-asm-config.h"
30 static int asm_get_local_label_name(TCCState
*s1
, unsigned int n
)
35 snprintf(buf
, sizeof(buf
), "L..%u", n
);
36 ts
= tok_alloc(buf
, strlen(buf
));
40 /* We do not use the C expression parser to handle symbols. Maybe the
41 C expression parser could be tweaked to do so. */
43 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
52 n
= strtoul(p
, (char **)&p
, 0);
53 if (*p
== 'b' || *p
== 'f') {
54 /* backward or forward label */
55 label
= asm_get_local_label_name(s1
, n
);
56 sym
= label_find(label
);
58 /* backward : find the last corresponding defined label */
59 if (sym
&& sym
->r
== 0)
62 error("local label '%d' not found backward", n
);
66 /* if the last label is defined, then define a new one */
67 sym
= label_push(&s1
->asm_labels
, label
, 0);
68 sym
->type
.t
= VT_STATIC
| VT_VOID
;
73 } else if (*p
== '\0') {
77 error("invalid number syntax");
83 asm_expr_unary(s1
, pe
);
89 asm_expr_unary(s1
, pe
);
91 error("invalid operation with label");
109 if (tok
>= TOK_IDENT
) {
110 /* label case : if the label was not found, add one */
111 sym
= label_find(tok
);
113 sym
= label_push(&s1
->asm_labels
, tok
, 0);
114 /* NOTE: by default, the symbol is global */
115 sym
->type
.t
= VT_VOID
;
117 if (sym
->r
== SHN_ABS
) {
118 /* if absolute symbol, no need to put a symbol value */
127 error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
133 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
138 asm_expr_unary(s1
, pe
);
141 if (op
!= '*' && op
!= '/' && op
!= '%' &&
142 op
!= TOK_SHL
&& op
!= TOK_SAR
)
145 asm_expr_unary(s1
, &e2
);
146 if (pe
->sym
|| e2
.sym
)
147 error("invalid operation with label");
155 error("division by zero");
175 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
180 asm_expr_prod(s1
, pe
);
183 if (op
!= '&' && op
!= '|' && op
!= '^')
186 asm_expr_prod(s1
, &e2
);
187 if (pe
->sym
|| e2
.sym
)
188 error("invalid operation with label");
204 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
209 asm_expr_logic(s1
, pe
);
212 if (op
!= '+' && op
!= '-')
215 asm_expr_logic(s1
, &e2
);
217 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
218 goto cannot_relocate
;
220 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
224 /* NOTE: we are less powerful than gas in that case
225 because we store only one symbol in the expression */
226 if (!pe
->sym
&& !e2
.sym
) {
228 } else if (pe
->sym
&& !e2
.sym
) {
230 } else if (pe
->sym
&& e2
.sym
) {
231 if (pe
->sym
== e2
.sym
) {
233 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
234 /* we also accept defined symbols in the same section */
235 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
237 goto cannot_relocate
;
239 pe
->sym
= NULL
; /* same symbols can be substracted to NULL */
242 error("invalid operation with label");
248 void asm_expr(TCCState
*s1
, ExprValue
*pe
)
250 asm_expr_sum(s1
, pe
);
253 int asm_int_expr(TCCState
*s1
)
262 /* NOTE: the same name space as C labels is used to avoid using too
263 much memory when storing labels in TokenStrings */
264 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
265 int sh_num
, int value
)
269 sym
= label_find(label
);
272 /* the label is already defined */
274 error("assembler label '%s' already defined",
275 get_tok_str(label
, NULL
));
277 /* redefinition of local labels is possible */
283 sym
= label_push(&s1
->asm_labels
, label
, 0);
284 sym
->type
.t
= VT_STATIC
| VT_VOID
;
290 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
292 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
295 static void asm_free_labels(TCCState
*st
)
300 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
302 /* define symbol value in object file */
307 sec
= st
->sections
[s
->r
];
308 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
311 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
314 st
->asm_labels
= NULL
;
317 static void use_section1(TCCState
*s1
, Section
*sec
)
319 cur_text_section
->data_offset
= ind
;
320 cur_text_section
= sec
;
321 ind
= cur_text_section
->data_offset
;
324 static void use_section(TCCState
*s1
, const char *name
)
327 sec
= find_section(s1
, name
);
328 use_section1(s1
, sec
);
331 static void asm_parse_directive(TCCState
*s1
)
333 int n
, offset
, v
, size
, tok1
;
337 /* assembler directive */
339 sec
= cur_text_section
;
346 n
= asm_int_expr(s1
);
347 if (tok1
== TOK_ASM_align
) {
348 if (n
< 0 || (n
& (n
-1)) != 0)
349 error("alignment must be a positive power of two");
350 offset
= (ind
+ n
- 1) & -n
;
352 /* the section must have a compatible alignment */
353 if (sec
->sh_addralign
< n
)
354 sec
->sh_addralign
= n
;
361 v
= asm_int_expr(s1
);
364 if (sec
->sh_type
!= SHT_NOBITS
) {
365 sec
->data_offset
= ind
;
366 ptr
= section_ptr_add(sec
, size
);
367 memset(ptr
, v
, size
);
378 if (tok
!= TOK_PPNUM
) {
380 error("64 bit constant");
382 vl
= strtoll(p
, (char **)&p
, 0);
386 if (sec
->sh_type
!= SHT_NOBITS
) {
387 /* XXX: endianness */
413 if (sec
->sh_type
!= SHT_NOBITS
) {
434 int repeat
, size
, val
, i
, j
;
435 uint8_t repeat_buf
[8];
437 repeat
= asm_int_expr(s1
);
439 error("repeat < 0; .fill ignored");
446 size
= asm_int_expr(s1
);
448 error("size < 0; .fill ignored");
455 val
= asm_int_expr(s1
);
458 /* XXX: endianness */
460 repeat_buf
[1] = val
>> 8;
461 repeat_buf
[2] = val
>> 16;
462 repeat_buf
[3] = val
>> 24;
467 for(i
= 0; i
< repeat
; i
++) {
468 for(j
= 0; j
< size
; j
++) {
478 /* XXX: handle section symbols too */
479 n
= asm_int_expr(s1
);
481 error("attempt to .org backwards");
493 sym
= label_find(tok
);
495 sym
= label_push(&s1
->asm_labels
, tok
, 0);
496 sym
->type
.t
= VT_VOID
;
498 sym
->type
.t
&= ~VT_STATIC
;
513 expect("string constant");
515 size
= tokc
.cstr
->size
;
516 if (t
== TOK_ASM_ascii
&& size
> 0)
518 for(i
= 0; i
< size
; i
++)
523 } else if (tok
!= TOK_STR
) {
537 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
538 n
= asm_int_expr(s1
);
541 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
542 use_section(s1
, sname
);
549 /* XXX: support more options */
552 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
554 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
556 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
560 /* skip section options */
563 expect("string constant");
566 last_text_section
= cur_text_section
;
567 use_section(s1
, sname
);
570 case TOK_ASM_previous
:
574 if (!last_text_section
)
575 error("no previous section referenced");
576 sec
= cur_text_section
;
577 use_section1(s1
, last_text_section
);
578 last_text_section
= sec
;
581 #ifdef TCC_TARGET_I386
595 #ifdef TCC_TARGET_X86_64
596 /* added for compatibility with GAS */
602 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
608 /* assemble a file */
609 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
614 /* print stats about opcodes */
619 int nb_op_vals
, i
, j
;
622 memset(freq
, 0, sizeof(freq
));
623 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
625 for(i
=0;i
<pa
->nb_ops
;i
++) {
626 for(j
=0;j
<nb_op_vals
;j
++) {
627 if (pa
->op_type
[i
] == op_vals
[j
])
630 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
634 for(i
=0;i
<nb_op_vals
;i
++) {
636 if ((v
& (v
- 1)) != 0)
637 printf("%3d: %08x\n", i
, v
);
639 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
640 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
641 freq
[0], freq
[1], freq
[2], freq
[3]);
645 /* XXX: undefine C labels */
647 ch
= file
->buf_ptr
[0];
648 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
649 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
651 parse_flags
|= PARSE_FLAG_PREPROCESS
;
656 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
659 /* horrible gas comment */
660 while (tok
!= TOK_LINEFEED
)
662 } else if (tok
== '.') {
663 asm_parse_directive(s1
);
664 } else if (tok
== TOK_PPNUM
) {
668 n
= strtoul(p
, (char **)&p
, 10);
671 /* new local label */
672 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
676 } else if (tok
>= TOK_IDENT
) {
677 /* instruction or label */
682 asm_new_label(s1
, opcode
, 0);
685 } else if (tok
== '=') {
688 n
= asm_int_expr(s1
);
689 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
692 asm_opcode(s1
, opcode
);
696 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
697 expect("end of line");
699 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
708 /* Assemble the current file */
709 int tcc_assemble(TCCState
*s1
, int do_preprocess
)
716 /* default section is text */
717 cur_text_section
= text_section
;
718 ind
= cur_text_section
->data_offset
;
720 define_start
= define_stack
;
722 ret
= tcc_assemble_internal(s1
, do_preprocess
);
724 cur_text_section
->data_offset
= ind
;
726 free_defines(define_start
);
731 /********************************************************************/
732 /* GCC inline asm support */
734 /* assemble the string 'str' in the current C compilation unit without
735 C preprocessing. NOTE: str is modified by modifying the '\0' at the
737 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
739 BufferedFile
*bf
, *saved_file
;
740 int saved_parse_flags
, *saved_macro_ptr
;
742 bf
= tcc_malloc(sizeof(BufferedFile
));
743 memset(bf
, 0, sizeof(BufferedFile
));
746 bf
->buf_end
= str
+ len
;
748 /* same name as current file so that errors are correctly
750 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
751 bf
->line_num
= file
->line_num
;
754 saved_parse_flags
= parse_flags
;
755 saved_macro_ptr
= macro_ptr
;
758 tcc_assemble_internal(s1
, 0);
760 parse_flags
= saved_parse_flags
;
761 macro_ptr
= saved_macro_ptr
;
766 /* find a constraint by its number or id (gcc 3 extended
767 syntax). return -1 if not found. Return in *pp in char after the
769 int find_constraint(ASMOperand
*operands
, int nb_operands
,
770 const char *name
, const char **pp
)
778 while (isnum(*name
)) {
779 index
= (index
* 10) + (*name
) - '0';
782 if ((unsigned)index
>= nb_operands
)
784 } else if (*name
== '[') {
786 p
= strchr(name
, ']');
788 ts
= tok_alloc(name
, p
- name
);
789 for(index
= 0; index
< nb_operands
; index
++) {
790 if (operands
[index
].id
== ts
->tok
)
807 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
809 CString
*out_str
, CString
*in_str
)
811 int c
, index
, modifier
;
826 if (*str
== 'c' || *str
== 'n' ||
827 *str
== 'b' || *str
== 'w' || *str
== 'h')
829 index
= find_constraint(operands
, nb_operands
, str
, &str
);
831 error("invalid operand reference after %%");
832 op
= &operands
[index
];
836 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
839 subst_asm_operand(out_str
, &sv
, modifier
);
842 cstr_ccat(out_str
, c
);
850 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
857 nb_operands
= *nb_operands_ptr
;
859 if (nb_operands
>= MAX_ASM_OPERANDS
)
860 error("too many asm operands");
861 op
= &operands
[nb_operands
++];
866 expect("identifier");
872 expect("string constant");
873 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
874 strcpy(op
->constraint
, tokc
.cstr
->data
);
881 /* we want to avoid LLOCAL case, except when the 'm'
882 constraint is used. Note that it may come from
883 register storage, so we need to convert (reg)
885 if ((vtop
->r
& VT_LVAL
) &&
886 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
887 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
888 !strchr(op
->constraint
, 'm')) {
900 *nb_operands_ptr
= nb_operands
;
904 void parse_asm_str(CString
*astr
)
907 /* read the string */
909 expect("string constant");
911 while (tok
== TOK_STR
) {
912 /* XXX: add \0 handling too ? */
913 cstr_cat(astr
, tokc
.cstr
->data
);
916 cstr_ccat(astr
, '\0');
919 /* parse the GCC asm() instruction */
923 ASMOperand operands
[MAX_ASM_OPERANDS
];
924 int nb_inputs
, nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
925 uint8_t clobber_regs
[NB_ASM_REGS
];
928 /* since we always generate the asm() instruction, we can ignore
930 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
933 parse_asm_str(&astr
);
937 memset(clobber_regs
, 0, sizeof(clobber_regs
));
942 parse_asm_operands(operands
, &nb_operands
, 1);
943 nb_outputs
= nb_operands
;
948 parse_asm_operands(operands
, &nb_operands
, 0);
951 /* XXX: handle registers */
955 expect("string constant");
956 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
969 /* NOTE: we do not eat the ';' so that we can restore the current
970 token after the assembler parsing */
973 nb_inputs
= nb_operands
- nb_outputs
;
975 /* save all values in the memory */
978 /* compute constraints */
979 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
980 clobber_regs
, &out_reg
);
982 /* substitute the operands in the asm string. No substitution is
983 done if no operands (GCC behaviour) */
985 printf("asm: \"%s\"\n", (char *)astr
.data
);
988 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
994 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
998 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
999 clobber_regs
, out_reg
);
1001 /* assemble the string with tcc internal assembler */
1002 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1004 /* restore the current C token */
1007 /* store the output values if needed */
1008 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1009 clobber_regs
, out_reg
);
1011 /* free everything */
1012 for(i
=0;i
<nb_operands
;i
++) {
1015 tcc_free(op
->constraint
);
1021 void asm_global_instr(void)
1026 parse_asm_str(&astr
);
1028 /* NOTE: we do not eat the ';' so that we can restore the current
1029 token after the assembler parsing */
1034 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1036 cur_text_section
= text_section
;
1037 ind
= cur_text_section
->data_offset
;
1039 /* assemble the string with tcc internal assembler */
1040 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1042 cur_text_section
->data_offset
= ind
;
1044 /* restore the current C token */