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
;
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");
492 sym
= label_find(tok
);
494 sym
= label_push(&s1
->asm_labels
, tok
, 0);
495 sym
->type
.t
= VT_VOID
;
497 if (tok1
!= TOK_ASM_hidden
)
498 sym
->type
.t
&= ~VT_STATIC
;
499 if (tok1
== TOK_ASM_weak
)
500 sym
->type
.t
|= VT_WEAK
;
501 else if (tok1
== TOK_ASM_hidden
)
502 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
504 } while (tok
== ',');
517 expect("string constant");
519 size
= tokc
.cstr
->size
;
520 if (t
== TOK_ASM_ascii
&& size
> 0)
522 for(i
= 0; i
< size
; i
++)
527 } else if (tok
!= TOK_STR
) {
541 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
542 n
= asm_int_expr(s1
);
545 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
546 use_section(s1
, sname
);
557 pstrcat(filename
, sizeof(filename
), tokc
.cstr
->data
);
559 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
561 if (s1
->warn_unsupported
)
562 tcc_warning("ignoring .file %s", filename
);
575 pstrcat(ident
, sizeof(ident
), tokc
.cstr
->data
);
577 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
579 if (s1
->warn_unsupported
)
580 tcc_warning("ignoring .ident %s", ident
);
590 sym
= label_find(tok
);
592 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
595 /* XXX .size name,label2-label1 */
596 if (s1
->warn_unsupported
)
597 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
601 while (tok
!= '\n' && tok
!= CH_EOF
) {
612 sym
= label_find(tok
);
614 sym
= label_push(&s1
->asm_labels
, tok
, 0);
615 sym
->type
.t
= VT_VOID
;
620 if (tok
== TOK_STR
) {
621 newtype
= tokc
.cstr
->data
;
623 if (tok
== '@' || tok
== '%')
625 newtype
= get_tok_str(tok
, NULL
);
628 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
629 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
631 else if (s1
->warn_unsupported
)
632 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
633 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
642 /* XXX: support more options */
645 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
647 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
649 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
653 /* skip section options */
656 expect("string constant");
659 last_text_section
= cur_text_section
;
660 use_section(s1
, sname
);
663 case TOK_ASM_previous
:
667 if (!last_text_section
)
668 tcc_error("no previous section referenced");
669 sec
= cur_text_section
;
670 use_section1(s1
, last_text_section
);
671 last_text_section
= sec
;
674 #ifdef TCC_TARGET_I386
688 #ifdef TCC_TARGET_X86_64
689 /* added for compatibility with GAS */
695 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
701 /* assemble a file */
702 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
707 /* print stats about opcodes */
712 int nb_op_vals
, i
, j
;
715 memset(freq
, 0, sizeof(freq
));
716 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
718 for(i
=0;i
<pa
->nb_ops
;i
++) {
719 for(j
=0;j
<nb_op_vals
;j
++) {
720 if (pa
->op_type
[i
] == op_vals
[j
])
723 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
727 for(i
=0;i
<nb_op_vals
;i
++) {
729 if ((v
& (v
- 1)) != 0)
730 printf("%3d: %08x\n", i
, v
);
732 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
733 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
734 freq
[0], freq
[1], freq
[2], freq
[3]);
738 /* XXX: undefine C labels */
740 ch
= file
->buf_ptr
[0];
741 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
742 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
744 parse_flags
|= PARSE_FLAG_PREPROCESS
;
749 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
752 /* horrible gas comment */
753 while (tok
!= TOK_LINEFEED
)
755 } else if (tok
== '.') {
756 asm_parse_directive(s1
);
757 } else if (tok
== TOK_PPNUM
) {
761 n
= strtoul(p
, (char **)&p
, 10);
764 /* new local label */
765 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
769 } else if (tok
>= TOK_IDENT
) {
770 /* instruction or label */
775 asm_new_label(s1
, opcode
, 0);
778 } else if (tok
== '=') {
781 n
= asm_int_expr(s1
);
782 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
785 asm_opcode(s1
, opcode
);
789 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
790 expect("end of line");
792 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
801 /* Assemble the current file */
802 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
809 /* default section is text */
810 cur_text_section
= text_section
;
811 ind
= cur_text_section
->data_offset
;
813 define_start
= define_stack
;
815 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
816 symbols can be safely used */
817 put_elf_sym(symtab_section
, 0, 0,
818 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
819 SHN_ABS
, file
->filename
);
821 ret
= tcc_assemble_internal(s1
, do_preprocess
);
823 cur_text_section
->data_offset
= ind
;
825 free_defines(define_start
);
830 /********************************************************************/
831 /* GCC inline asm support */
833 /* assemble the string 'str' in the current C compilation unit without
834 C preprocessing. NOTE: str is modified by modifying the '\0' at the
836 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
838 int saved_parse_flags
;
839 const int *saved_macro_ptr
;
841 saved_parse_flags
= parse_flags
;
842 saved_macro_ptr
= macro_ptr
;
844 tcc_open_bf(s1
, ":asm:", len
);
845 memcpy(file
->buffer
, str
, len
);
848 tcc_assemble_internal(s1
, 0);
851 parse_flags
= saved_parse_flags
;
852 macro_ptr
= saved_macro_ptr
;
855 /* find a constraint by its number or id (gcc 3 extended
856 syntax). return -1 if not found. Return in *pp in char after the
858 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
859 const char *name
, const char **pp
)
867 while (isnum(*name
)) {
868 index
= (index
* 10) + (*name
) - '0';
871 if ((unsigned)index
>= nb_operands
)
873 } else if (*name
== '[') {
875 p
= strchr(name
, ']');
877 ts
= tok_alloc(name
, p
- name
);
878 for(index
= 0; index
< nb_operands
; index
++) {
879 if (operands
[index
].id
== ts
->tok
)
896 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
898 CString
*out_str
, CString
*in_str
)
900 int c
, index
, modifier
;
915 if (*str
== 'c' || *str
== 'n' ||
916 *str
== 'b' || *str
== 'w' || *str
== 'h')
918 index
= find_constraint(operands
, nb_operands
, str
, &str
);
920 tcc_error("invalid operand reference after %%");
921 op
= &operands
[index
];
925 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
928 subst_asm_operand(out_str
, &sv
, modifier
);
931 cstr_ccat(out_str
, c
);
939 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
946 nb_operands
= *nb_operands_ptr
;
948 if (nb_operands
>= MAX_ASM_OPERANDS
)
949 tcc_error("too many asm operands");
950 op
= &operands
[nb_operands
++];
955 expect("identifier");
961 expect("string constant");
962 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
963 strcpy(op
->constraint
, tokc
.cstr
->data
);
970 /* we want to avoid LLOCAL case, except when the 'm'
971 constraint is used. Note that it may come from
972 register storage, so we need to convert (reg)
974 if ((vtop
->r
& VT_LVAL
) &&
975 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
976 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
977 !strchr(op
->constraint
, 'm')) {
989 *nb_operands_ptr
= nb_operands
;
993 /* parse the GCC asm() instruction */
994 ST_FUNC
void asm_instr(void)
997 ASMOperand operands
[MAX_ASM_OPERANDS
];
998 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
999 uint8_t clobber_regs
[NB_ASM_REGS
];
1002 /* since we always generate the asm() instruction, we can ignore
1004 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1007 parse_asm_str(&astr
);
1011 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1016 parse_asm_operands(operands
, &nb_operands
, 1);
1017 nb_outputs
= nb_operands
;
1022 parse_asm_operands(operands
, &nb_operands
, 0);
1025 /* XXX: handle registers */
1029 expect("string constant");
1030 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
1043 /* NOTE: we do not eat the ';' so that we can restore the current
1044 token after the assembler parsing */
1048 /* save all values in the memory */
1051 /* compute constraints */
1052 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1053 clobber_regs
, &out_reg
);
1055 /* substitute the operands in the asm string. No substitution is
1056 done if no operands (GCC behaviour) */
1058 printf("asm: \"%s\"\n", (char *)astr
.data
);
1061 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1067 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1070 /* generate loads */
1071 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1072 clobber_regs
, out_reg
);
1074 /* assemble the string with tcc internal assembler */
1075 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1077 /* restore the current C token */
1080 /* store the output values if needed */
1081 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1082 clobber_regs
, out_reg
);
1084 /* free everything */
1085 for(i
=0;i
<nb_operands
;i
++) {
1088 tcc_free(op
->constraint
);
1094 ST_FUNC
void asm_global_instr(void)
1099 parse_asm_str(&astr
);
1101 /* NOTE: we do not eat the ';' so that we can restore the current
1102 token after the assembler parsing */
1107 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1109 cur_text_section
= text_section
;
1110 ind
= cur_text_section
->data_offset
;
1112 /* assemble the string with tcc internal assembler */
1113 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1115 cur_text_section
->data_offset
= ind
;
1117 /* restore the current C token */
1122 #endif /* CONFIG_TCC_ASM */