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 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 error("invalid number syntax");
78 asm_expr_unary(s1
, pe
);
84 asm_expr_unary(s1
, pe
);
86 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 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 error("invalid operation with label");
150 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 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 substracted to NULL */
237 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 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 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 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 error("repeat < 0; .fill ignored");
441 size
= asm_int_expr(s1
);
443 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 error("attempt to .org backwards");
488 sym
= label_find(tok
);
490 sym
= label_push(&s1
->asm_labels
, tok
, 0);
491 sym
->type
.t
= VT_VOID
;
493 sym
->type
.t
&= ~VT_STATIC
;
508 expect("string constant");
510 size
= tokc
.cstr
->size
;
511 if (t
== TOK_ASM_ascii
&& size
> 0)
513 for(i
= 0; i
< size
; i
++)
518 } else if (tok
!= TOK_STR
) {
532 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
533 n
= asm_int_expr(s1
);
536 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
537 use_section(s1
, sname
);
548 pstrcat(filename
, sizeof(filename
), tokc
.cstr
->data
);
550 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
552 if (s1
->warn_unsupported
)
553 warning("ignoring .file %s", filename
);
566 pstrcat(ident
, sizeof(ident
), tokc
.cstr
->data
);
568 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
570 if (s1
->warn_unsupported
)
571 warning("ignoring .ident %s", ident
);
581 sym
= label_find(tok
);
583 error("label not found: %s", get_tok_str(tok
, NULL
));
588 /* XXX .size name,label2-label1 */
589 if (s1
->warn_unsupported
)
590 warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
592 while (tok
!= '\n' && tok
!= CH_EOF
) {
604 sym
= label_find(tok
);
606 sym
= label_push(&s1
->asm_labels
, tok
, 0);
607 sym
->type
.t
= VT_VOID
;
614 pstrcat(newtype
, sizeof(newtype
), tokc
.cstr
->data
);
616 pstrcat(newtype
, sizeof(newtype
), get_tok_str(tok
, NULL
));
618 if (!strcmp(newtype
, "function")) {
619 sym
->type
.t
= VT_FUNC
;
621 else if (s1
->warn_unsupported
)
622 warning("change type of '%s' from 0x%x to '%s' ignored",
623 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
632 /* XXX: support more options */
635 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
637 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
639 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
643 /* skip section options */
646 expect("string constant");
649 last_text_section
= cur_text_section
;
650 use_section(s1
, sname
);
653 case TOK_ASM_previous
:
657 if (!last_text_section
)
658 error("no previous section referenced");
659 sec
= cur_text_section
;
660 use_section1(s1
, last_text_section
);
661 last_text_section
= sec
;
664 #ifdef TCC_TARGET_I386
678 #ifdef TCC_TARGET_X86_64
679 /* added for compatibility with GAS */
685 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
691 /* assemble a file */
692 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
697 /* print stats about opcodes */
702 int nb_op_vals
, i
, j
;
705 memset(freq
, 0, sizeof(freq
));
706 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
708 for(i
=0;i
<pa
->nb_ops
;i
++) {
709 for(j
=0;j
<nb_op_vals
;j
++) {
710 if (pa
->op_type
[i
] == op_vals
[j
])
713 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
717 for(i
=0;i
<nb_op_vals
;i
++) {
719 if ((v
& (v
- 1)) != 0)
720 printf("%3d: %08x\n", i
, v
);
722 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
723 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
724 freq
[0], freq
[1], freq
[2], freq
[3]);
728 /* XXX: undefine C labels */
730 ch
= file
->buf_ptr
[0];
731 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
732 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
734 parse_flags
|= PARSE_FLAG_PREPROCESS
;
739 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
742 /* horrible gas comment */
743 while (tok
!= TOK_LINEFEED
)
745 } else if (tok
== '.') {
746 asm_parse_directive(s1
);
747 } else if (tok
== TOK_PPNUM
) {
751 n
= strtoul(p
, (char **)&p
, 10);
754 /* new local label */
755 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
759 } else if (tok
>= TOK_IDENT
) {
760 /* instruction or label */
764 char * label
= get_tok_str(opcode
, NULL
);
767 asm_new_label(s1
, opcode
,
768 (label
&& label
[0] == '.' && label
[1] == 'L') ? 1 : 0);
771 } else if (tok
== '=') {
774 n
= asm_int_expr(s1
);
775 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
778 asm_opcode(s1
, opcode
);
782 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
783 expect("end of line");
785 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
794 /* Assemble the current file */
795 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
802 /* default section is text */
803 cur_text_section
= text_section
;
804 ind
= cur_text_section
->data_offset
;
806 define_start
= define_stack
;
808 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
809 symbols can be safely used */
810 put_elf_sym(symtab_section
, 0, 0,
811 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
812 SHN_ABS
, file
->filename
);
814 ret
= tcc_assemble_internal(s1
, do_preprocess
);
816 cur_text_section
->data_offset
= ind
;
818 free_defines(define_start
);
823 /********************************************************************/
824 /* GCC inline asm support */
826 /* assemble the string 'str' in the current C compilation unit without
827 C preprocessing. NOTE: str is modified by modifying the '\0' at the
829 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
831 BufferedFile
*bf
, *saved_file
;
832 int saved_parse_flags
;
833 const int *saved_macro_ptr
;
835 bf
= tcc_malloc(sizeof(BufferedFile
));
836 memset(bf
, 0, sizeof(BufferedFile
));
839 bf
->buf_end
= str
+ len
;
841 /* same name as current file so that errors are correctly
843 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
844 bf
->line_num
= file
->line_num
;
847 saved_parse_flags
= parse_flags
;
848 saved_macro_ptr
= macro_ptr
;
851 tcc_assemble_internal(s1
, 0);
853 parse_flags
= saved_parse_flags
;
854 macro_ptr
= saved_macro_ptr
;
859 /* find a constraint by its number or id (gcc 3 extended
860 syntax). return -1 if not found. Return in *pp in char after the
862 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
863 const char *name
, const char **pp
)
871 while (isnum(*name
)) {
872 index
= (index
* 10) + (*name
) - '0';
875 if ((unsigned)index
>= nb_operands
)
877 } else if (*name
== '[') {
879 p
= strchr(name
, ']');
881 ts
= tok_alloc(name
, p
- name
);
882 for(index
= 0; index
< nb_operands
; index
++) {
883 if (operands
[index
].id
== ts
->tok
)
900 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
902 CString
*out_str
, CString
*in_str
)
904 int c
, index
, modifier
;
919 if (*str
== 'c' || *str
== 'n' ||
920 *str
== 'b' || *str
== 'w' || *str
== 'h')
922 index
= find_constraint(operands
, nb_operands
, str
, &str
);
924 error("invalid operand reference after %%");
925 op
= &operands
[index
];
929 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
932 subst_asm_operand(out_str
, &sv
, modifier
);
935 cstr_ccat(out_str
, c
);
943 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
950 nb_operands
= *nb_operands_ptr
;
952 if (nb_operands
>= MAX_ASM_OPERANDS
)
953 error("too many asm operands");
954 op
= &operands
[nb_operands
++];
959 expect("identifier");
965 expect("string constant");
966 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
967 strcpy(op
->constraint
, tokc
.cstr
->data
);
974 /* we want to avoid LLOCAL case, except when the 'm'
975 constraint is used. Note that it may come from
976 register storage, so we need to convert (reg)
978 if ((vtop
->r
& VT_LVAL
) &&
979 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
980 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
981 !strchr(op
->constraint
, 'm')) {
993 *nb_operands_ptr
= nb_operands
;
997 static void parse_asm_str(CString
*astr
)
1000 /* read the string */
1002 expect("string constant");
1004 while (tok
== TOK_STR
) {
1005 /* XXX: add \0 handling too ? */
1006 cstr_cat(astr
, tokc
.cstr
->data
);
1009 cstr_ccat(astr
, '\0');
1012 /* parse the GCC asm() instruction */
1013 ST_FUNC
void asm_instr(void)
1015 CString astr
, astr1
;
1016 ASMOperand operands
[MAX_ASM_OPERANDS
];
1017 int nb_inputs
, nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1018 uint8_t clobber_regs
[NB_ASM_REGS
];
1021 /* since we always generate the asm() instruction, we can ignore
1023 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1026 parse_asm_str(&astr
);
1030 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1035 parse_asm_operands(operands
, &nb_operands
, 1);
1036 nb_outputs
= nb_operands
;
1041 parse_asm_operands(operands
, &nb_operands
, 0);
1044 /* XXX: handle registers */
1048 expect("string constant");
1049 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
1062 /* NOTE: we do not eat the ';' so that we can restore the current
1063 token after the assembler parsing */
1066 nb_inputs
= nb_operands
- nb_outputs
;
1068 /* save all values in the memory */
1071 /* compute constraints */
1072 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1073 clobber_regs
, &out_reg
);
1075 /* substitute the operands in the asm string. No substitution is
1076 done if no operands (GCC behaviour) */
1078 printf("asm: \"%s\"\n", (char *)astr
.data
);
1081 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1087 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1090 /* generate loads */
1091 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1092 clobber_regs
, out_reg
);
1094 /* assemble the string with tcc internal assembler */
1095 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1097 /* restore the current C token */
1100 /* store the output values if needed */
1101 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1102 clobber_regs
, out_reg
);
1104 /* free everything */
1105 for(i
=0;i
<nb_operands
;i
++) {
1108 tcc_free(op
->constraint
);
1114 ST_FUNC
void asm_global_instr(void)
1119 parse_asm_str(&astr
);
1121 /* NOTE: we do not eat the ';' so that we can restore the current
1122 token after the assembler parsing */
1127 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1129 cur_text_section
= text_section
;
1130 ind
= cur_text_section
->data_offset
;
1132 /* assemble the string with tcc internal assembler */
1133 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1135 cur_text_section
->data_offset
= ind
;
1137 /* restore the current C token */