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
24 ST_FUNC
int asm_get_local_label_name(TCCState
*s1
, unsigned int n
)
29 snprintf(buf
, sizeof(buf
), "L..%u", n
);
30 ts
= tok_alloc(buf
, strlen(buf
));
34 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
);
36 /* We do not use the C expression parser to handle symbols. Maybe the
37 C expression parser could be tweaked to do so. */
39 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
48 n
= strtoul(p
, (char **)&p
, 0);
49 if (*p
== 'b' || *p
== 'f') {
50 /* backward or forward label */
51 label
= asm_get_local_label_name(s1
, n
);
52 sym
= label_find(label
);
54 /* backward : find the last corresponding defined label */
55 if (sym
&& sym
->r
== 0)
58 tcc_error("local label '%d' not found backward", n
);
62 /* if the last label is defined, then define a new one */
63 sym
= label_push(&s1
->asm_labels
, label
, 0);
64 sym
->type
.t
= VT_STATIC
| VT_VOID
;
69 } else if (*p
== '\0') {
73 tcc_error("invalid number syntax");
79 asm_expr_unary(s1
, pe
);
85 asm_expr_unary(s1
, pe
);
87 tcc_error("invalid operation with label");
105 if (tok
>= TOK_IDENT
) {
106 /* label case : if the label was not found, add one */
107 sym
= label_find(tok
);
109 sym
= label_push(&s1
->asm_labels
, tok
, 0);
110 /* NOTE: by default, the symbol is global */
111 sym
->type
.t
= VT_VOID
;
113 if (sym
->r
== SHN_ABS
) {
114 /* if absolute symbol, no need to put a symbol value */
123 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
129 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
134 asm_expr_unary(s1
, pe
);
137 if (op
!= '*' && op
!= '/' && op
!= '%' &&
138 op
!= TOK_SHL
&& op
!= TOK_SAR
)
141 asm_expr_unary(s1
, &e2
);
142 if (pe
->sym
|| e2
.sym
)
143 tcc_error("invalid operation with label");
151 tcc_error("division by zero");
171 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
176 asm_expr_prod(s1
, pe
);
179 if (op
!= '&' && op
!= '|' && op
!= '^')
182 asm_expr_prod(s1
, &e2
);
183 if (pe
->sym
|| e2
.sym
)
184 tcc_error("invalid operation with label");
200 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
205 asm_expr_logic(s1
, pe
);
208 if (op
!= '+' && op
!= '-')
211 asm_expr_logic(s1
, &e2
);
213 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
214 goto cannot_relocate
;
216 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
220 /* NOTE: we are less powerful than gas in that case
221 because we store only one symbol in the expression */
222 if (!pe
->sym
&& !e2
.sym
) {
224 } else if (pe
->sym
&& !e2
.sym
) {
226 } else if (pe
->sym
&& e2
.sym
) {
227 if (pe
->sym
== e2
.sym
) {
229 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
230 /* we also accept defined symbols in the same section */
231 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
233 goto cannot_relocate
;
235 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
238 tcc_error("invalid operation with label");
244 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
246 asm_expr_sum(s1
, pe
);
249 ST_FUNC
int asm_int_expr(TCCState
*s1
)
258 /* NOTE: the same name space as C labels is used to avoid using too
259 much memory when storing labels in TokenStrings */
260 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
261 int sh_num
, int value
)
265 sym
= label_find(label
);
268 /* the label is already defined */
270 tcc_error("assembler label '%s' already defined",
271 get_tok_str(label
, NULL
));
273 /* redefinition of local labels is possible */
279 sym
= label_push(&s1
->asm_labels
, label
, 0);
280 sym
->type
.t
= VT_STATIC
| VT_VOID
;
286 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
288 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
291 static void asm_free_labels(TCCState
*st
)
296 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
298 /* define symbol value in object file */
303 sec
= st
->sections
[s
->r
];
304 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
307 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
310 st
->asm_labels
= NULL
;
313 static void use_section1(TCCState
*s1
, Section
*sec
)
315 cur_text_section
->data_offset
= ind
;
316 cur_text_section
= sec
;
317 ind
= cur_text_section
->data_offset
;
320 static void use_section(TCCState
*s1
, const char *name
)
323 sec
= find_section(s1
, name
);
324 use_section1(s1
, sec
);
327 static void asm_parse_directive(TCCState
*s1
)
329 int n
, offset
, v
, size
, tok1
;
333 /* assembler directive */
335 sec
= cur_text_section
;
338 case TOK_ASM_p2align
:
343 n
= asm_int_expr(s1
);
344 if (tok1
== TOK_ASM_p2align
)
347 tcc_error("invalid p2align, must be between 0 and 30");
349 tok1
= TOK_ASM_align
;
351 if (tok1
== TOK_ASM_align
) {
352 if (n
< 0 || (n
& (n
-1)) != 0)
353 tcc_error("alignment must be a positive power of two");
354 offset
= (ind
+ n
- 1) & -n
;
356 /* the section must have a compatible alignment */
357 if (sec
->sh_addralign
< n
)
358 sec
->sh_addralign
= n
;
365 v
= asm_int_expr(s1
);
368 if (sec
->sh_type
!= SHT_NOBITS
) {
369 sec
->data_offset
= ind
;
370 ptr
= section_ptr_add(sec
, size
);
371 memset(ptr
, v
, size
);
382 if (tok
!= TOK_PPNUM
) {
384 tcc_error("64 bit constant");
386 vl
= strtoll(p
, (char **)&p
, 0);
390 if (sec
->sh_type
!= SHT_NOBITS
) {
391 /* XXX: endianness */
417 if (sec
->sh_type
!= SHT_NOBITS
) {
438 int repeat
, size
, val
, i
, j
;
439 uint8_t repeat_buf
[8];
441 repeat
= asm_int_expr(s1
);
443 tcc_error("repeat < 0; .fill ignored");
450 size
= asm_int_expr(s1
);
452 tcc_error("size < 0; .fill ignored");
459 val
= asm_int_expr(s1
);
462 /* XXX: endianness */
464 repeat_buf
[1] = val
>> 8;
465 repeat_buf
[2] = val
>> 16;
466 repeat_buf
[3] = val
>> 24;
471 for(i
= 0; i
< repeat
; i
++) {
472 for(j
= 0; j
< size
; j
++) {
482 /* XXX: handle section symbols too */
483 n
= asm_int_expr(s1
);
485 tcc_error("attempt to .org backwards");
500 sym
= label_find(tok
);
502 sym
= label_push(&s1
->asm_labels
, tok
, 0);
503 sym
->type
.t
= VT_VOID
;
505 if (tok1
!= TOK_ASM_hidden
)
506 sym
->type
.t
&= ~VT_STATIC
;
507 if (tok1
== TOK_ASM_weak
)
508 sym
->type
.t
|= VT_WEAK
;
509 else if (tok1
== TOK_ASM_hidden
)
510 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
512 } while (tok
== ',');
525 expect("string constant");
527 size
= tokc
.cstr
->size
;
528 if (t
== TOK_ASM_ascii
&& size
> 0)
530 for(i
= 0; i
< size
; i
++)
535 } else if (tok
!= TOK_STR
) {
549 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
550 n
= asm_int_expr(s1
);
553 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
554 use_section(s1
, sname
);
565 pstrcat(filename
, sizeof(filename
), tokc
.cstr
->data
);
567 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
569 if (s1
->warn_unsupported
)
570 tcc_warning("ignoring .file %s", filename
);
583 pstrcat(ident
, sizeof(ident
), tokc
.cstr
->data
);
585 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
587 if (s1
->warn_unsupported
)
588 tcc_warning("ignoring .ident %s", ident
);
598 sym
= label_find(tok
);
600 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
603 /* XXX .size name,label2-label1 */
604 if (s1
->warn_unsupported
)
605 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
609 while (tok
!= '\n' && tok
!= CH_EOF
) {
620 sym
= label_find(tok
);
622 sym
= label_push(&s1
->asm_labels
, tok
, 0);
623 sym
->type
.t
= VT_VOID
;
628 if (tok
== TOK_STR
) {
629 newtype
= tokc
.cstr
->data
;
631 if (tok
== '@' || tok
== '%')
633 newtype
= get_tok_str(tok
, NULL
);
636 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
637 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
639 else if (s1
->warn_unsupported
)
640 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
641 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
650 /* XXX: support more options */
653 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
655 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
657 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
661 /* skip section options */
664 expect("string constant");
667 last_text_section
= cur_text_section
;
668 use_section(s1
, sname
);
671 case TOK_ASM_previous
:
675 if (!last_text_section
)
676 tcc_error("no previous section referenced");
677 sec
= cur_text_section
;
678 use_section1(s1
, last_text_section
);
679 last_text_section
= sec
;
682 #ifdef TCC_TARGET_I386
696 #ifdef TCC_TARGET_X86_64
697 /* added for compatibility with GAS */
703 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
709 /* assemble a file */
710 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
715 /* print stats about opcodes */
720 int nb_op_vals
, i
, j
;
723 memset(freq
, 0, sizeof(freq
));
724 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
726 for(i
=0;i
<pa
->nb_ops
;i
++) {
727 for(j
=0;j
<nb_op_vals
;j
++) {
728 if (pa
->op_type
[i
] == op_vals
[j
])
731 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
735 for(i
=0;i
<nb_op_vals
;i
++) {
737 if ((v
& (v
- 1)) != 0)
738 printf("%3d: %08x\n", i
, v
);
740 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
741 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
742 freq
[0], freq
[1], freq
[2], freq
[3]);
746 /* XXX: undefine C labels */
748 ch
= file
->buf_ptr
[0];
749 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
750 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
752 parse_flags
|= PARSE_FLAG_PREPROCESS
;
757 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
760 /* horrible gas comment */
761 while (tok
!= TOK_LINEFEED
)
763 } else if (tok
== '.') {
764 asm_parse_directive(s1
);
765 } else if (tok
== TOK_PPNUM
) {
769 n
= strtoul(p
, (char **)&p
, 10);
772 /* new local label */
773 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
777 } else if (tok
>= TOK_IDENT
) {
778 /* instruction or label */
783 asm_new_label(s1
, opcode
, 0);
786 } else if (tok
== '=') {
789 n
= asm_int_expr(s1
);
790 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
793 asm_opcode(s1
, opcode
);
797 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
798 expect("end of line");
800 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
809 /* Assemble the current file */
810 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
817 /* default section is text */
818 cur_text_section
= text_section
;
819 ind
= cur_text_section
->data_offset
;
821 define_start
= define_stack
;
823 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
824 symbols can be safely used */
825 put_elf_sym(symtab_section
, 0, 0,
826 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
827 SHN_ABS
, file
->filename
);
829 ret
= tcc_assemble_internal(s1
, do_preprocess
);
831 cur_text_section
->data_offset
= ind
;
833 free_defines(define_start
);
838 /********************************************************************/
839 /* GCC inline asm support */
841 /* assemble the string 'str' in the current C compilation unit without
842 C preprocessing. NOTE: str is modified by modifying the '\0' at the
844 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
846 int saved_parse_flags
;
847 const int *saved_macro_ptr
;
849 saved_parse_flags
= parse_flags
;
850 saved_macro_ptr
= macro_ptr
;
852 tcc_open_bf(s1
, ":asm:", len
);
853 memcpy(file
->buffer
, str
, len
);
856 tcc_assemble_internal(s1
, 0);
859 parse_flags
= saved_parse_flags
;
860 macro_ptr
= saved_macro_ptr
;
863 /* find a constraint by its number or id (gcc 3 extended
864 syntax). return -1 if not found. Return in *pp in char after the
866 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
867 const char *name
, const char **pp
)
875 while (isnum(*name
)) {
876 index
= (index
* 10) + (*name
) - '0';
879 if ((unsigned)index
>= nb_operands
)
881 } else if (*name
== '[') {
883 p
= strchr(name
, ']');
885 ts
= tok_alloc(name
, p
- name
);
886 for(index
= 0; index
< nb_operands
; index
++) {
887 if (operands
[index
].id
== ts
->tok
)
904 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
906 CString
*out_str
, CString
*in_str
)
908 int c
, index
, modifier
;
923 if (*str
== 'c' || *str
== 'n' ||
924 *str
== 'b' || *str
== 'w' || *str
== 'h')
926 index
= find_constraint(operands
, nb_operands
, str
, &str
);
928 tcc_error("invalid operand reference after %%");
929 op
= &operands
[index
];
933 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
936 subst_asm_operand(out_str
, &sv
, modifier
);
939 cstr_ccat(out_str
, c
);
947 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
954 nb_operands
= *nb_operands_ptr
;
956 if (nb_operands
>= MAX_ASM_OPERANDS
)
957 tcc_error("too many asm operands");
958 op
= &operands
[nb_operands
++];
963 expect("identifier");
969 expect("string constant");
970 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
971 strcpy(op
->constraint
, tokc
.cstr
->data
);
978 /* we want to avoid LLOCAL case, except when the 'm'
979 constraint is used. Note that it may come from
980 register storage, so we need to convert (reg)
982 if ((vtop
->r
& VT_LVAL
) &&
983 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
984 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
985 !strchr(op
->constraint
, 'm')) {
997 *nb_operands_ptr
= nb_operands
;
1001 /* parse the GCC asm() instruction */
1002 ST_FUNC
void asm_instr(void)
1004 CString astr
, astr1
;
1005 ASMOperand operands
[MAX_ASM_OPERANDS
];
1006 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1007 uint8_t clobber_regs
[NB_ASM_REGS
];
1010 /* since we always generate the asm() instruction, we can ignore
1012 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1015 parse_asm_str(&astr
);
1019 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1024 parse_asm_operands(operands
, &nb_operands
, 1);
1025 nb_outputs
= nb_operands
;
1030 parse_asm_operands(operands
, &nb_operands
, 0);
1033 /* XXX: handle registers */
1037 expect("string constant");
1038 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
1051 /* NOTE: we do not eat the ';' so that we can restore the current
1052 token after the assembler parsing */
1056 /* save all values in the memory */
1059 /* compute constraints */
1060 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1061 clobber_regs
, &out_reg
);
1063 /* substitute the operands in the asm string. No substitution is
1064 done if no operands (GCC behaviour) */
1066 printf("asm: \"%s\"\n", (char *)astr
.data
);
1069 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1075 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1078 /* generate loads */
1079 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1080 clobber_regs
, out_reg
);
1082 /* assemble the string with tcc internal assembler */
1083 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1085 /* restore the current C token */
1088 /* store the output values if needed */
1089 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1090 clobber_regs
, out_reg
);
1092 /* free everything */
1093 for(i
=0;i
<nb_operands
;i
++) {
1096 tcc_free(op
->constraint
);
1102 ST_FUNC
void asm_global_instr(void)
1107 parse_asm_str(&astr
);
1109 /* NOTE: we do not eat the ';' so that we can restore the current
1110 token after the assembler parsing */
1115 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1117 cur_text_section
= text_section
;
1118 ind
= cur_text_section
->data_offset
;
1120 /* assemble the string with tcc internal assembler */
1121 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1123 cur_text_section
->data_offset
= ind
;
1125 /* restore the current C token */
1130 #endif /* CONFIG_TCC_ASM */