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 substracted 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
;
342 n
= asm_int_expr(s1
);
343 if (tok1
== TOK_ASM_align
) {
344 if (n
< 0 || (n
& (n
-1)) != 0)
345 tcc_error("alignment must be a positive power of two");
346 offset
= (ind
+ n
- 1) & -n
;
348 /* the section must have a compatible alignment */
349 if (sec
->sh_addralign
< n
)
350 sec
->sh_addralign
= n
;
357 v
= asm_int_expr(s1
);
360 if (sec
->sh_type
!= SHT_NOBITS
) {
361 sec
->data_offset
= ind
;
362 ptr
= section_ptr_add(sec
, size
);
363 memset(ptr
, v
, size
);
374 if (tok
!= TOK_PPNUM
) {
376 tcc_error("64 bit constant");
378 vl
= strtoll(p
, (char **)&p
, 0);
382 if (sec
->sh_type
!= SHT_NOBITS
) {
383 /* XXX: endianness */
409 if (sec
->sh_type
!= SHT_NOBITS
) {
430 int repeat
, size
, val
, i
, j
;
431 uint8_t repeat_buf
[8];
433 repeat
= asm_int_expr(s1
);
435 tcc_error("repeat < 0; .fill ignored");
442 size
= asm_int_expr(s1
);
444 tcc_error("size < 0; .fill ignored");
451 val
= asm_int_expr(s1
);
454 /* XXX: endianness */
456 repeat_buf
[1] = val
>> 8;
457 repeat_buf
[2] = val
>> 16;
458 repeat_buf
[3] = val
>> 24;
463 for(i
= 0; i
< repeat
; i
++) {
464 for(j
= 0; j
< size
; j
++) {
474 /* XXX: handle section symbols too */
475 n
= asm_int_expr(s1
);
477 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 sym
->type
.t
&= ~VT_STATIC
;
497 if (tok1
== TOK_ASM_weak
)
498 sym
->type
.t
|= VT_WEAK
;
500 } while (tok
== ',');
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
);
553 pstrcat(filename
, sizeof(filename
), tokc
.cstr
->data
);
555 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
557 if (s1
->warn_unsupported
)
558 tcc_warning("ignoring .file %s", filename
);
571 pstrcat(ident
, sizeof(ident
), tokc
.cstr
->data
);
573 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
575 if (s1
->warn_unsupported
)
576 tcc_warning("ignoring .ident %s", ident
);
586 sym
= label_find(tok
);
588 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
593 /* XXX .size name,label2-label1 */
594 if (s1
->warn_unsupported
)
595 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
597 while (tok
!= '\n' && tok
!= CH_EOF
) {
608 sym
= label_find(tok
);
610 sym
= label_push(&s1
->asm_labels
, tok
, 0);
611 sym
->type
.t
= VT_VOID
;
616 if (tok
== TOK_STR
) {
617 newtype
= tokc
.cstr
->data
;
619 if (tok
== '@' || tok
== '%')
621 newtype
= get_tok_str(tok
, NULL
);
624 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
625 sym
->type
.t
= VT_FUNC
;
627 else if (s1
->warn_unsupported
)
628 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
629 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
638 /* XXX: support more options */
641 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
643 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
645 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
649 /* skip section options */
652 expect("string constant");
655 last_text_section
= cur_text_section
;
656 use_section(s1
, sname
);
659 case TOK_ASM_previous
:
663 if (!last_text_section
)
664 tcc_error("no previous section referenced");
665 sec
= cur_text_section
;
666 use_section1(s1
, last_text_section
);
667 last_text_section
= sec
;
670 #ifdef TCC_TARGET_I386
684 #ifdef TCC_TARGET_X86_64
685 /* added for compatibility with GAS */
691 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
697 /* assemble a file */
698 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
703 /* print stats about opcodes */
708 int nb_op_vals
, i
, j
;
711 memset(freq
, 0, sizeof(freq
));
712 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
714 for(i
=0;i
<pa
->nb_ops
;i
++) {
715 for(j
=0;j
<nb_op_vals
;j
++) {
716 if (pa
->op_type
[i
] == op_vals
[j
])
719 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
723 for(i
=0;i
<nb_op_vals
;i
++) {
725 if ((v
& (v
- 1)) != 0)
726 printf("%3d: %08x\n", i
, v
);
728 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
729 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
730 freq
[0], freq
[1], freq
[2], freq
[3]);
734 /* XXX: undefine C labels */
736 ch
= file
->buf_ptr
[0];
737 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
738 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
740 parse_flags
|= PARSE_FLAG_PREPROCESS
;
745 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
748 /* horrible gas comment */
749 while (tok
!= TOK_LINEFEED
)
751 } else if (tok
== '.') {
752 asm_parse_directive(s1
);
753 } else if (tok
== TOK_PPNUM
) {
757 n
= strtoul(p
, (char **)&p
, 10);
760 /* new local label */
761 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
765 } else if (tok
>= TOK_IDENT
) {
766 /* instruction or label */
771 asm_new_label(s1
, opcode
, 0);
774 } else if (tok
== '=') {
777 n
= asm_int_expr(s1
);
778 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
781 asm_opcode(s1
, opcode
);
785 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
786 expect("end of line");
788 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
797 /* Assemble the current file */
798 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
805 /* default section is text */
806 cur_text_section
= text_section
;
807 ind
= cur_text_section
->data_offset
;
809 define_start
= define_stack
;
811 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
812 symbols can be safely used */
813 put_elf_sym(symtab_section
, 0, 0,
814 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
815 SHN_ABS
, file
->filename
);
817 ret
= tcc_assemble_internal(s1
, do_preprocess
);
819 cur_text_section
->data_offset
= ind
;
821 free_defines(define_start
);
826 /********************************************************************/
827 /* GCC inline asm support */
829 /* assemble the string 'str' in the current C compilation unit without
830 C preprocessing. NOTE: str is modified by modifying the '\0' at the
832 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
834 int saved_parse_flags
;
835 const int *saved_macro_ptr
;
837 saved_parse_flags
= parse_flags
;
838 saved_macro_ptr
= macro_ptr
;
840 tcc_open_bf(s1
, file
->filename
, len
);
841 file
->line_num
= file
->prev
->line_num
;
842 memcpy(file
->buffer
, str
, len
);
845 tcc_assemble_internal(s1
, 0);
848 parse_flags
= saved_parse_flags
;
849 macro_ptr
= saved_macro_ptr
;
852 /* find a constraint by its number or id (gcc 3 extended
853 syntax). return -1 if not found. Return in *pp in char after the
855 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
856 const char *name
, const char **pp
)
864 while (isnum(*name
)) {
865 index
= (index
* 10) + (*name
) - '0';
868 if ((unsigned)index
>= nb_operands
)
870 } else if (*name
== '[') {
872 p
= strchr(name
, ']');
874 ts
= tok_alloc(name
, p
- name
);
875 for(index
= 0; index
< nb_operands
; index
++) {
876 if (operands
[index
].id
== ts
->tok
)
893 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
895 CString
*out_str
, CString
*in_str
)
897 int c
, index
, modifier
;
912 if (*str
== 'c' || *str
== 'n' ||
913 *str
== 'b' || *str
== 'w' || *str
== 'h')
915 index
= find_constraint(operands
, nb_operands
, str
, &str
);
917 tcc_error("invalid operand reference after %%");
918 op
= &operands
[index
];
922 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
925 subst_asm_operand(out_str
, &sv
, modifier
);
928 cstr_ccat(out_str
, c
);
936 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
943 nb_operands
= *nb_operands_ptr
;
945 if (nb_operands
>= MAX_ASM_OPERANDS
)
946 tcc_error("too many asm operands");
947 op
= &operands
[nb_operands
++];
952 expect("identifier");
958 expect("string constant");
959 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
960 strcpy(op
->constraint
, tokc
.cstr
->data
);
967 /* we want to avoid LLOCAL case, except when the 'm'
968 constraint is used. Note that it may come from
969 register storage, so we need to convert (reg)
971 if ((vtop
->r
& VT_LVAL
) &&
972 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
973 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
974 !strchr(op
->constraint
, 'm')) {
986 *nb_operands_ptr
= nb_operands
;
990 /* parse the GCC asm() instruction */
991 ST_FUNC
void asm_instr(void)
994 ASMOperand operands
[MAX_ASM_OPERANDS
];
995 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
996 uint8_t clobber_regs
[NB_ASM_REGS
];
999 /* since we always generate the asm() instruction, we can ignore
1001 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1004 parse_asm_str(&astr
);
1008 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1013 parse_asm_operands(operands
, &nb_operands
, 1);
1014 nb_outputs
= nb_operands
;
1019 parse_asm_operands(operands
, &nb_operands
, 0);
1022 /* XXX: handle registers */
1026 expect("string constant");
1027 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
1040 /* NOTE: we do not eat the ';' so that we can restore the current
1041 token after the assembler parsing */
1045 /* save all values in the memory */
1048 /* compute constraints */
1049 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1050 clobber_regs
, &out_reg
);
1052 /* substitute the operands in the asm string. No substitution is
1053 done if no operands (GCC behaviour) */
1055 printf("asm: \"%s\"\n", (char *)astr
.data
);
1058 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1064 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1067 /* generate loads */
1068 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1069 clobber_regs
, out_reg
);
1071 /* assemble the string with tcc internal assembler */
1072 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1074 /* restore the current C token */
1077 /* store the output values if needed */
1078 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1079 clobber_regs
, out_reg
);
1081 /* free everything */
1082 for(i
=0;i
<nb_operands
;i
++) {
1085 tcc_free(op
->constraint
);
1091 ST_FUNC
void asm_global_instr(void)
1096 parse_asm_str(&astr
);
1098 /* NOTE: we do not eat the ';' so that we can restore the current
1099 token after the assembler parsing */
1104 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1106 cur_text_section
= text_section
;
1107 ind
= cur_text_section
->data_offset
;
1109 /* assemble the string with tcc internal assembler */
1110 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1112 cur_text_section
->data_offset
= ind
;
1114 /* restore the current C token */
1119 #endif /* CONFIG_TCC_ASM */