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
25 ST_FUNC
int asm_get_local_label_name(TCCState
*s1
, unsigned int n
)
30 snprintf(buf
, sizeof(buf
), "L..%u", n
);
31 ts
= tok_alloc(buf
, strlen(buf
));
35 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
);
37 /* We do not use the C expression parser to handle symbols. Maybe the
38 C expression parser could be tweaked to do so. */
40 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
49 n
= strtoul(p
, (char **)&p
, 0);
50 if (*p
== 'b' || *p
== 'f') {
51 /* backward or forward label */
52 label
= asm_get_local_label_name(s1
, n
);
53 sym
= label_find(label
);
55 /* backward : find the last corresponding defined label */
56 if (sym
&& sym
->r
== 0)
59 error("local label '%d' not found backward", n
);
63 /* if the last label is defined, then define a new one */
64 sym
= label_push(&s1
->asm_labels
, label
, 0);
65 sym
->type
.t
= VT_STATIC
| VT_VOID
;
70 } else if (*p
== '\0') {
74 error("invalid number syntax");
80 asm_expr_unary(s1
, pe
);
86 asm_expr_unary(s1
, pe
);
88 error("invalid operation with label");
106 if (tok
>= TOK_IDENT
) {
107 /* label case : if the label was not found, add one */
108 sym
= label_find(tok
);
110 sym
= label_push(&s1
->asm_labels
, tok
, 0);
111 /* NOTE: by default, the symbol is global */
112 sym
->type
.t
= VT_VOID
;
114 if (sym
->r
== SHN_ABS
) {
115 /* if absolute symbol, no need to put a symbol value */
124 error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
130 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
135 asm_expr_unary(s1
, pe
);
138 if (op
!= '*' && op
!= '/' && op
!= '%' &&
139 op
!= TOK_SHL
&& op
!= TOK_SAR
)
142 asm_expr_unary(s1
, &e2
);
143 if (pe
->sym
|| e2
.sym
)
144 error("invalid operation with label");
152 error("division by zero");
172 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
177 asm_expr_prod(s1
, pe
);
180 if (op
!= '&' && op
!= '|' && op
!= '^')
183 asm_expr_prod(s1
, &e2
);
184 if (pe
->sym
|| e2
.sym
)
185 error("invalid operation with label");
201 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
206 asm_expr_logic(s1
, pe
);
209 if (op
!= '+' && op
!= '-')
212 asm_expr_logic(s1
, &e2
);
214 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
215 goto cannot_relocate
;
217 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
221 /* NOTE: we are less powerful than gas in that case
222 because we store only one symbol in the expression */
223 if (!pe
->sym
&& !e2
.sym
) {
225 } else if (pe
->sym
&& !e2
.sym
) {
227 } else if (pe
->sym
&& e2
.sym
) {
228 if (pe
->sym
== e2
.sym
) {
230 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
231 /* we also accept defined symbols in the same section */
232 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
234 goto cannot_relocate
;
236 pe
->sym
= NULL
; /* same symbols can be substracted to NULL */
239 error("invalid operation with label");
245 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
247 asm_expr_sum(s1
, pe
);
250 ST_FUNC
int asm_int_expr(TCCState
*s1
)
259 /* NOTE: the same name space as C labels is used to avoid using too
260 much memory when storing labels in TokenStrings */
261 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
262 int sh_num
, int value
)
266 sym
= label_find(label
);
269 /* the label is already defined */
271 error("assembler label '%s' already defined",
272 get_tok_str(label
, NULL
));
274 /* redefinition of local labels is possible */
280 sym
= label_push(&s1
->asm_labels
, label
, 0);
281 sym
->type
.t
= VT_STATIC
| VT_VOID
;
287 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
289 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
292 static void asm_free_labels(TCCState
*st
)
297 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
299 /* define symbol value in object file */
304 sec
= st
->sections
[s
->r
];
305 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
308 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
311 st
->asm_labels
= NULL
;
314 static void use_section1(TCCState
*s1
, Section
*sec
)
316 cur_text_section
->data_offset
= ind
;
317 cur_text_section
= sec
;
318 ind
= cur_text_section
->data_offset
;
321 static void use_section(TCCState
*s1
, const char *name
)
324 sec
= find_section(s1
, name
);
325 use_section1(s1
, sec
);
328 static void asm_parse_directive(TCCState
*s1
)
330 int n
, offset
, v
, size
, tok1
;
334 /* assembler directive */
336 sec
= cur_text_section
;
343 n
= asm_int_expr(s1
);
344 if (tok1
== TOK_ASM_align
) {
345 if (n
< 0 || (n
& (n
-1)) != 0)
346 error("alignment must be a positive power of two");
347 offset
= (ind
+ n
- 1) & -n
;
349 /* the section must have a compatible alignment */
350 if (sec
->sh_addralign
< n
)
351 sec
->sh_addralign
= n
;
358 v
= asm_int_expr(s1
);
361 if (sec
->sh_type
!= SHT_NOBITS
) {
362 sec
->data_offset
= ind
;
363 ptr
= section_ptr_add(sec
, size
);
364 memset(ptr
, v
, size
);
375 if (tok
!= TOK_PPNUM
) {
377 error("64 bit constant");
379 vl
= strtoll(p
, (char **)&p
, 0);
383 if (sec
->sh_type
!= SHT_NOBITS
) {
384 /* XXX: endianness */
410 if (sec
->sh_type
!= SHT_NOBITS
) {
431 int repeat
, size
, val
, i
, j
;
432 uint8_t repeat_buf
[8];
434 repeat
= asm_int_expr(s1
);
436 error("repeat < 0; .fill ignored");
443 size
= asm_int_expr(s1
);
445 error("size < 0; .fill ignored");
452 val
= asm_int_expr(s1
);
455 /* XXX: endianness */
457 repeat_buf
[1] = val
>> 8;
458 repeat_buf
[2] = val
>> 16;
459 repeat_buf
[3] = val
>> 24;
464 for(i
= 0; i
< repeat
; i
++) {
465 for(j
= 0; j
< size
; j
++) {
475 /* XXX: handle section symbols too */
476 n
= asm_int_expr(s1
);
478 error("attempt to .org backwards");
490 sym
= label_find(tok
);
492 sym
= label_push(&s1
->asm_labels
, tok
, 0);
493 sym
->type
.t
= VT_VOID
;
495 sym
->type
.t
&= ~VT_STATIC
;
510 expect("string constant");
512 size
= tokc
.cstr
->size
;
513 if (t
== TOK_ASM_ascii
&& size
> 0)
515 for(i
= 0; i
< size
; i
++)
520 } else if (tok
!= TOK_STR
) {
534 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
535 n
= asm_int_expr(s1
);
538 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
539 use_section(s1
, sname
);
550 pstrcat(filename
, sizeof(filename
), tokc
.cstr
->data
);
552 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
554 if (s1
->warn_unsupported
)
555 warning("ignoring .file %s", filename
);
568 pstrcat(ident
, sizeof(ident
), tokc
.cstr
->data
);
570 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
572 if (s1
->warn_unsupported
)
573 warning("ignoring .ident %s", ident
);
583 sym
= label_find(tok
);
585 error("label not found: %s", get_tok_str(tok
, NULL
));
590 /* XXX .size name,label2-label1 */
591 if (s1
->warn_unsupported
)
592 warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
594 while (tok
!= '\n' && tok
!= CH_EOF
) {
606 sym
= label_find(tok
);
608 sym
= label_push(&s1
->asm_labels
, tok
, 0);
609 sym
->type
.t
= VT_VOID
;
616 pstrcat(newtype
, sizeof(newtype
), tokc
.cstr
->data
);
618 pstrcat(newtype
, sizeof(newtype
), get_tok_str(tok
, NULL
));
620 if (!strcmp(newtype
, "function")) {
621 sym
->type
.t
= VT_FUNC
;
623 else if (s1
->warn_unsupported
)
624 warning("change type of '%s' from 0x%x to '%s' ignored",
625 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
634 /* XXX: support more options */
637 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
639 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
641 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
645 /* skip section options */
648 expect("string constant");
651 last_text_section
= cur_text_section
;
652 use_section(s1
, sname
);
655 case TOK_ASM_previous
:
659 if (!last_text_section
)
660 error("no previous section referenced");
661 sec
= cur_text_section
;
662 use_section1(s1
, last_text_section
);
663 last_text_section
= sec
;
666 #ifdef TCC_TARGET_I386
680 #ifdef TCC_TARGET_X86_64
681 /* added for compatibility with GAS */
687 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
693 /* assemble a file */
694 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
699 /* print stats about opcodes */
704 int nb_op_vals
, i
, j
;
707 memset(freq
, 0, sizeof(freq
));
708 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
710 for(i
=0;i
<pa
->nb_ops
;i
++) {
711 for(j
=0;j
<nb_op_vals
;j
++) {
712 if (pa
->op_type
[i
] == op_vals
[j
])
715 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
719 for(i
=0;i
<nb_op_vals
;i
++) {
721 if ((v
& (v
- 1)) != 0)
722 printf("%3d: %08x\n", i
, v
);
724 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
725 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
726 freq
[0], freq
[1], freq
[2], freq
[3]);
730 /* XXX: undefine C labels */
732 ch
= file
->buf_ptr
[0];
733 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
734 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
736 parse_flags
|= PARSE_FLAG_PREPROCESS
;
741 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
744 /* horrible gas comment */
745 while (tok
!= TOK_LINEFEED
)
747 } else if (tok
== '.') {
748 asm_parse_directive(s1
);
749 } else if (tok
== TOK_PPNUM
) {
753 n
= strtoul(p
, (char **)&p
, 10);
756 /* new local label */
757 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
761 } else if (tok
>= TOK_IDENT
) {
762 /* instruction or label */
767 asm_new_label(s1
, opcode
, 0);
770 } else if (tok
== '=') {
773 n
= asm_int_expr(s1
);
774 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
777 asm_opcode(s1
, opcode
);
781 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
782 expect("end of line");
784 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
793 /* Assemble the current file */
794 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
801 /* default section is text */
802 cur_text_section
= text_section
;
803 ind
= cur_text_section
->data_offset
;
805 define_start
= define_stack
;
807 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
808 symbols can be safely used */
809 put_elf_sym(symtab_section
, 0, 0,
810 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
811 SHN_ABS
, file
->filename
);
813 ret
= tcc_assemble_internal(s1
, do_preprocess
);
815 cur_text_section
->data_offset
= ind
;
817 free_defines(define_start
);
822 /********************************************************************/
823 /* GCC inline asm support */
825 /* assemble the string 'str' in the current C compilation unit without
826 C preprocessing. NOTE: str is modified by modifying the '\0' at the
828 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
830 BufferedFile
*bf
, *saved_file
;
831 int saved_parse_flags
;
832 const int *saved_macro_ptr
;
834 bf
= tcc_malloc(sizeof(BufferedFile
));
835 memset(bf
, 0, sizeof(BufferedFile
));
838 bf
->buf_end
= str
+ len
;
840 /* same name as current file so that errors are correctly
842 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
843 bf
->line_num
= file
->line_num
;
846 saved_parse_flags
= parse_flags
;
847 saved_macro_ptr
= macro_ptr
;
850 tcc_assemble_internal(s1
, 0);
852 parse_flags
= saved_parse_flags
;
853 macro_ptr
= saved_macro_ptr
;
858 /* find a constraint by its number or id (gcc 3 extended
859 syntax). return -1 if not found. Return in *pp in char after the
861 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
862 const char *name
, const char **pp
)
870 while (isnum(*name
)) {
871 index
= (index
* 10) + (*name
) - '0';
874 if ((unsigned)index
>= nb_operands
)
876 } else if (*name
== '[') {
878 p
= strchr(name
, ']');
880 ts
= tok_alloc(name
, p
- name
);
881 for(index
= 0; index
< nb_operands
; index
++) {
882 if (operands
[index
].id
== ts
->tok
)
899 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
901 CString
*out_str
, CString
*in_str
)
903 int c
, index
, modifier
;
918 if (*str
== 'c' || *str
== 'n' ||
919 *str
== 'b' || *str
== 'w' || *str
== 'h')
921 index
= find_constraint(operands
, nb_operands
, str
, &str
);
923 error("invalid operand reference after %%");
924 op
= &operands
[index
];
928 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
931 subst_asm_operand(out_str
, &sv
, modifier
);
934 cstr_ccat(out_str
, c
);
942 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
949 nb_operands
= *nb_operands_ptr
;
951 if (nb_operands
>= MAX_ASM_OPERANDS
)
952 error("too many asm operands");
953 op
= &operands
[nb_operands
++];
958 expect("identifier");
964 expect("string constant");
965 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
966 strcpy(op
->constraint
, tokc
.cstr
->data
);
973 /* we want to avoid LLOCAL case, except when the 'm'
974 constraint is used. Note that it may come from
975 register storage, so we need to convert (reg)
977 if ((vtop
->r
& VT_LVAL
) &&
978 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
979 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
980 !strchr(op
->constraint
, 'm')) {
992 *nb_operands_ptr
= nb_operands
;
998 static void parse_asm_str(CString
*astr
)
1001 /* read the string */
1003 expect("string constant");
1005 while (tok
== TOK_STR
) {
1006 /* XXX: add \0 handling too ? */
1007 cstr_cat(astr
, tokc
.cstr
->data
);
1010 cstr_ccat(astr
, '\0');
1013 /* Parse an asm label and return the label
1014 * Don't forget to free the CString in the caller! */
1015 static void asm_label_instr(CString
*astr
)
1018 parse_asm_str(astr
);
1021 printf("asm_alias: \"%s\"\n", (char *)astr
->data
);
1025 #ifdef CONFIG_TCC_ASM
1027 /* parse the GCC asm() instruction */
1028 ST_FUNC
void asm_instr(void)
1030 CString astr
, astr1
;
1031 ASMOperand operands
[MAX_ASM_OPERANDS
];
1032 int nb_inputs
, nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1033 uint8_t clobber_regs
[NB_ASM_REGS
];
1036 /* since we always generate the asm() instruction, we can ignore
1038 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1041 parse_asm_str(&astr
);
1045 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1050 parse_asm_operands(operands
, &nb_operands
, 1);
1051 nb_outputs
= nb_operands
;
1056 parse_asm_operands(operands
, &nb_operands
, 0);
1059 /* XXX: handle registers */
1063 expect("string constant");
1064 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
1077 /* NOTE: we do not eat the ';' so that we can restore the current
1078 token after the assembler parsing */
1081 nb_inputs
= nb_operands
- nb_outputs
;
1083 /* save all values in the memory */
1086 /* compute constraints */
1087 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1088 clobber_regs
, &out_reg
);
1090 /* substitute the operands in the asm string. No substitution is
1091 done if no operands (GCC behaviour) */
1093 printf("asm: \"%s\"\n", (char *)astr
.data
);
1096 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1102 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1105 /* generate loads */
1106 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1107 clobber_regs
, out_reg
);
1109 /* assemble the string with tcc internal assembler */
1110 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1112 /* restore the current C token */
1115 /* store the output values if needed */
1116 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1117 clobber_regs
, out_reg
);
1119 /* free everything */
1120 for(i
=0;i
<nb_operands
;i
++) {
1123 tcc_free(op
->constraint
);
1129 ST_FUNC
void asm_global_instr(void)
1134 parse_asm_str(&astr
);
1136 /* NOTE: we do not eat the ';' so that we can restore the current
1137 token after the assembler parsing */
1142 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1144 cur_text_section
= text_section
;
1145 ind
= cur_text_section
->data_offset
;
1147 /* assemble the string with tcc internal assembler */
1148 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1150 cur_text_section
->data_offset
= ind
;
1152 /* restore the current C token */