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
23 ST_FUNC
int asm_get_local_label_name(TCCState
*s1
, unsigned int n
)
28 snprintf(buf
, sizeof(buf
), "L..%u", n
);
29 ts
= tok_alloc(buf
, strlen(buf
));
33 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
);
35 /* We do not use the C expression parser to handle symbols. Maybe the
36 C expression parser could be tweaked to do so. */
38 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
47 n
= strtoul(p
, (char **)&p
, 0);
48 if (*p
== 'b' || *p
== 'f') {
49 /* backward or forward label */
50 label
= asm_get_local_label_name(s1
, n
);
51 sym
= label_find(label
);
53 /* backward : find the last corresponding defined label */
54 if (sym
&& sym
->r
== 0)
57 tcc_error("local label '%d' not found backward", n
);
61 /* if the last label is defined, then define a new one */
62 sym
= label_push(&s1
->asm_labels
, label
, 0);
63 sym
->type
.t
= VT_STATIC
| VT_VOID
;
68 } else if (*p
== '\0') {
72 tcc_error("invalid number syntax");
78 asm_expr_unary(s1
, pe
);
84 asm_expr_unary(s1
, pe
);
86 tcc_error("invalid operation with label");
104 if (tok
>= TOK_IDENT
) {
105 /* label case : if the label was not found, add one */
106 sym
= label_find(tok
);
108 sym
= label_push(&s1
->asm_labels
, tok
, 0);
109 /* NOTE: by default, the symbol is global */
110 sym
->type
.t
= VT_VOID
;
112 if (sym
->r
== SHN_ABS
) {
113 /* if absolute symbol, no need to put a symbol value */
122 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
128 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
133 asm_expr_unary(s1
, pe
);
136 if (op
!= '*' && op
!= '/' && op
!= '%' &&
137 op
!= TOK_SHL
&& op
!= TOK_SAR
)
140 asm_expr_unary(s1
, &e2
);
141 if (pe
->sym
|| e2
.sym
)
142 tcc_error("invalid operation with label");
150 tcc_error("division by zero");
170 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
175 asm_expr_prod(s1
, pe
);
178 if (op
!= '&' && op
!= '|' && op
!= '^')
181 asm_expr_prod(s1
, &e2
);
182 if (pe
->sym
|| e2
.sym
)
183 tcc_error("invalid operation with label");
199 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
204 asm_expr_logic(s1
, pe
);
207 if (op
!= '+' && op
!= '-')
210 asm_expr_logic(s1
, &e2
);
212 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
213 goto cannot_relocate
;
215 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
219 /* NOTE: we are less powerful than gas in that case
220 because we store only one symbol in the expression */
221 if (!pe
->sym
&& !e2
.sym
) {
223 } else if (pe
->sym
&& !e2
.sym
) {
225 } else if (pe
->sym
&& e2
.sym
) {
226 if (pe
->sym
== e2
.sym
) {
228 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
229 /* we also accept defined symbols in the same section */
230 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
232 goto cannot_relocate
;
234 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
237 tcc_error("invalid operation with label");
243 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
245 asm_expr_sum(s1
, pe
);
248 ST_FUNC
int asm_int_expr(TCCState
*s1
)
257 /* NOTE: the same name space as C labels is used to avoid using too
258 much memory when storing labels in TokenStrings */
259 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
260 int sh_num
, int value
)
264 sym
= label_find(label
);
267 /* the label is already defined */
269 tcc_error("assembler label '%s' already defined",
270 get_tok_str(label
, NULL
));
272 /* redefinition of local labels is possible */
278 sym
= label_push(&s1
->asm_labels
, label
, 0);
279 sym
->type
.t
= VT_STATIC
| VT_VOID
;
285 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
287 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
290 static void asm_free_labels(TCCState
*st
)
295 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
297 /* define symbol value in object file */
302 sec
= st
->sections
[s
->r
];
303 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
306 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
309 st
->asm_labels
= NULL
;
312 static void use_section1(TCCState
*s1
, Section
*sec
)
314 cur_text_section
->data_offset
= ind
;
315 cur_text_section
= sec
;
316 ind
= cur_text_section
->data_offset
;
319 static void use_section(TCCState
*s1
, const char *name
)
322 sec
= find_section(s1
, name
);
323 use_section1(s1
, sec
);
326 static void asm_parse_directive(TCCState
*s1
)
328 int n
, offset
, v
, size
, tok1
;
332 /* assembler directive */
334 sec
= cur_text_section
;
341 n
= asm_int_expr(s1
);
342 if (tok1
== TOK_ASM_align
) {
343 if (n
< 0 || (n
& (n
-1)) != 0)
344 tcc_error("alignment must be a positive power of two");
345 offset
= (ind
+ n
- 1) & -n
;
347 /* the section must have a compatible alignment */
348 if (sec
->sh_addralign
< n
)
349 sec
->sh_addralign
= n
;
356 v
= asm_int_expr(s1
);
359 if (sec
->sh_type
!= SHT_NOBITS
) {
360 sec
->data_offset
= ind
;
361 ptr
= section_ptr_add(sec
, size
);
362 memset(ptr
, v
, size
);
373 if (tok
!= TOK_PPNUM
) {
375 tcc_error("64 bit constant");
377 vl
= strtoll(p
, (char **)&p
, 0);
381 if (sec
->sh_type
!= SHT_NOBITS
) {
382 /* XXX: endianness */
408 if (sec
->sh_type
!= SHT_NOBITS
) {
429 int repeat
, size
, val
, i
, j
;
430 uint8_t repeat_buf
[8];
432 repeat
= asm_int_expr(s1
);
434 tcc_error("repeat < 0; .fill ignored");
441 size
= asm_int_expr(s1
);
443 tcc_error("size < 0; .fill ignored");
450 val
= asm_int_expr(s1
);
453 /* XXX: endianness */
455 repeat_buf
[1] = val
>> 8;
456 repeat_buf
[2] = val
>> 16;
457 repeat_buf
[3] = val
>> 24;
462 for(i
= 0; i
< repeat
; i
++) {
463 for(j
= 0; j
< size
; j
++) {
473 /* XXX: handle section symbols too */
474 n
= asm_int_expr(s1
);
476 tcc_error("attempt to .org backwards");
491 sym
= label_find(tok
);
493 sym
= label_push(&s1
->asm_labels
, tok
, 0);
494 sym
->type
.t
= VT_VOID
;
496 if (tok1
!= TOK_ASM_hidden
)
497 sym
->type
.t
&= ~VT_STATIC
;
498 if (tok1
== TOK_ASM_weak
)
499 sym
->type
.t
|= VT_WEAK
;
500 else if (tok1
== TOK_ASM_hidden
)
501 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
503 } while (tok
== ',');
516 expect("string constant");
518 size
= tokc
.cstr
->size
;
519 if (t
== TOK_ASM_ascii
&& size
> 0)
521 for(i
= 0; i
< size
; i
++)
526 } else if (tok
!= TOK_STR
) {
540 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
541 n
= asm_int_expr(s1
);
544 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
545 use_section(s1
, sname
);
556 pstrcat(filename
, sizeof(filename
), tokc
.cstr
->data
);
558 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
560 if (s1
->warn_unsupported
)
561 tcc_warning("ignoring .file %s", filename
);
574 pstrcat(ident
, sizeof(ident
), tokc
.cstr
->data
);
576 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
578 if (s1
->warn_unsupported
)
579 tcc_warning("ignoring .ident %s", ident
);
589 sym
= label_find(tok
);
591 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
594 /* XXX .size name,label2-label1 */
595 if (s1
->warn_unsupported
)
596 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
600 while (tok
!= '\n' && tok
!= CH_EOF
) {
611 sym
= label_find(tok
);
613 sym
= label_push(&s1
->asm_labels
, tok
, 0);
614 sym
->type
.t
= VT_VOID
;
619 if (tok
== TOK_STR
) {
620 newtype
= tokc
.cstr
->data
;
622 if (tok
== '@' || tok
== '%')
624 newtype
= get_tok_str(tok
, NULL
);
627 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
628 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
630 else if (s1
->warn_unsupported
)
631 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
632 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
641 /* XXX: support more options */
644 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
646 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
648 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
652 /* skip section options */
655 expect("string constant");
658 last_text_section
= cur_text_section
;
659 use_section(s1
, sname
);
662 case TOK_ASM_previous
:
666 if (!last_text_section
)
667 tcc_error("no previous section referenced");
668 sec
= cur_text_section
;
669 use_section1(s1
, last_text_section
);
670 last_text_section
= sec
;
673 #ifdef TCC_TARGET_I386
687 #ifdef TCC_TARGET_X86_64
688 /* added for compatibility with GAS */
694 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
699 /* assemble a file */
700 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
704 #ifdef PRINTF_ASM_CODE
705 ST_FUNC
void printf_asm_opcode();
706 /* print stats about opcodes */
710 /* XXX: undefine C labels */
712 ch
= file
->buf_ptr
[0];
713 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
714 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
716 parse_flags
|= PARSE_FLAG_PREPROCESS
;
721 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
724 /* horrible gas comment */
725 while (tok
!= TOK_LINEFEED
)
727 } else if (tok
== '.') {
728 asm_parse_directive(s1
);
729 } else if (tok
== TOK_PPNUM
) {
733 n
= strtoul(p
, (char **)&p
, 10);
736 /* new local label */
737 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
741 } else if (tok
>= TOK_IDENT
) {
742 /* instruction or label */
747 asm_new_label(s1
, opcode
, 0);
750 } else if (tok
== '=') {
753 n
= asm_int_expr(s1
);
754 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
757 asm_opcode(s1
, opcode
);
761 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
762 expect("end of line");
764 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
773 /* Assemble the current file */
774 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
781 /* default section is text */
782 cur_text_section
= text_section
;
783 ind
= cur_text_section
->data_offset
;
785 define_start
= define_stack
;
787 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
788 symbols can be safely used */
789 put_elf_sym(symtab_section
, 0, 0, ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
790 SHN_ABS
, file
->filename
);
792 ret
= tcc_assemble_internal(s1
, do_preprocess
);
794 cur_text_section
->data_offset
= ind
;
796 free_defines(define_start
);
801 /********************************************************************/
802 /* GCC inline asm support */
804 /* assemble the string 'str' in the current C compilation unit without
805 C preprocessing. NOTE: str is modified by modifying the '\0' at the
807 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
809 int saved_parse_flags
;
810 const int *saved_macro_ptr
;
812 saved_parse_flags
= parse_flags
;
813 saved_macro_ptr
= macro_ptr
;
815 tcc_open_bf(s1
, ":asm:", len
);
816 memcpy(file
->buffer
, str
, len
);
819 tcc_assemble_internal(s1
, 0);
822 parse_flags
= saved_parse_flags
;
823 macro_ptr
= saved_macro_ptr
;
826 /* find a constraint by its number or id (gcc 3 extended
827 syntax). return -1 if not found. Return in *pp in char after the
829 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
830 const char *name
, const char **pp
)
838 while (isnum(*name
)) {
839 index
= (index
* 10) + (*name
) - '0';
842 if ((unsigned)index
>= nb_operands
)
844 } else if (*name
== '[') {
846 p
= strchr(name
, ']');
848 ts
= tok_alloc(name
, p
- name
);
849 for(index
= 0; index
< nb_operands
; index
++) {
850 if (operands
[index
].id
== ts
->tok
)
867 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
869 CString
*out_str
, CString
*in_str
)
871 int c
, index
, modifier
;
886 if (*str
== 'c' || *str
== 'n' ||
887 *str
== 'b' || *str
== 'w' || *str
== 'h')
889 index
= find_constraint(operands
, nb_operands
, str
, &str
);
891 tcc_error("invalid operand reference after %%");
892 op
= &operands
[index
];
896 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
899 subst_asm_operand(out_str
, &sv
, modifier
);
902 cstr_ccat(out_str
, c
);
910 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
917 nb_operands
= *nb_operands_ptr
;
919 if (nb_operands
>= MAX_ASM_OPERANDS
)
920 tcc_error("too many asm operands");
921 op
= &operands
[nb_operands
++];
926 expect("identifier");
932 expect("string constant");
933 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
934 strcpy(op
->constraint
, tokc
.cstr
->data
);
941 /* we want to avoid LLOCAL case, except when the 'm'
942 constraint is used. Note that it may come from
943 register storage, so we need to convert (reg)
945 if ((vtop
->r
& VT_LVAL
) &&
946 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
947 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
948 !strchr(op
->constraint
, 'm')) {
960 *nb_operands_ptr
= nb_operands
;
964 /* parse the GCC asm() instruction */
965 ST_FUNC
void asm_instr(void)
968 ASMOperand operands
[MAX_ASM_OPERANDS
];
969 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
970 uint8_t clobber_regs
[NB_ASM_REGS
];
973 /* since we always generate the asm() instruction, we can ignore
975 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
978 parse_asm_str(&astr
);
982 memset(clobber_regs
, 0, sizeof(clobber_regs
));
987 parse_asm_operands(operands
, &nb_operands
, 1);
988 nb_outputs
= nb_operands
;
993 parse_asm_operands(operands
, &nb_operands
, 0);
996 /* XXX: handle registers */
1000 expect("string constant");
1001 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
1014 /* NOTE: we do not eat the ';' so that we can restore the current
1015 token after the assembler parsing */
1019 /* save all values in the memory */
1022 /* compute constraints */
1023 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1024 clobber_regs
, &out_reg
);
1026 /* substitute the operands in the asm string. No substitution is
1027 done if no operands (GCC behaviour) */
1029 printf("asm: \"%s\"\n", (char *)astr
.data
);
1032 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1038 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1041 /* generate loads */
1042 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1043 clobber_regs
, out_reg
);
1045 /* assemble the string with tcc internal assembler */
1046 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1048 /* restore the current C token */
1051 /* store the output values if needed */
1052 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1053 clobber_regs
, out_reg
);
1055 /* free everything */
1056 for(i
=0;i
<nb_operands
;i
++) {
1059 tcc_free(op
->constraint
);
1065 ST_FUNC
void asm_global_instr(void)
1070 parse_asm_str(&astr
);
1072 /* NOTE: we do not eat the ';' so that we can restore the current
1073 token after the assembler parsing */
1078 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1080 cur_text_section
= text_section
;
1081 ind
= cur_text_section
->data_offset
;
1083 /* assemble the string with tcc internal assembler */
1084 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1086 cur_text_section
->data_offset
= ind
;
1088 /* restore the current C token */
1093 #endif /* CONFIG_TCC_ASM */