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
;
338 case TOK_ASM_p2align
:
343 n
= asm_int_expr(s1
);
344 if (tok1
== TOK_ASM_p2align
)
347 tcc_error("invalid p2align, must be between 0 and 30");
349 tok1
= TOK_ASM_align
;
351 if (tok1
== TOK_ASM_align
) {
352 if (n
< 0 || (n
& (n
-1)) != 0)
353 tcc_error("alignment must be a positive power of two");
354 offset
= (ind
+ n
- 1) & -n
;
356 /* the section must have a compatible alignment */
357 if (sec
->sh_addralign
< n
)
358 sec
->sh_addralign
= n
;
365 v
= asm_int_expr(s1
);
368 if (sec
->sh_type
!= SHT_NOBITS
) {
369 sec
->data_offset
= ind
;
370 ptr
= section_ptr_add(sec
, size
);
371 memset(ptr
, v
, size
);
382 if (tok
!= TOK_PPNUM
) {
384 tcc_error("64 bit constant");
386 vl
= strtoll(p
, (char **)&p
, 0);
390 if (sec
->sh_type
!= SHT_NOBITS
) {
391 /* XXX: endianness */
417 if (sec
->sh_type
!= SHT_NOBITS
) {
438 int repeat
, size
, val
, i
, j
;
439 uint8_t repeat_buf
[8];
441 repeat
= asm_int_expr(s1
);
443 tcc_error("repeat < 0; .fill ignored");
450 size
= asm_int_expr(s1
);
452 tcc_error("size < 0; .fill ignored");
459 val
= asm_int_expr(s1
);
462 /* XXX: endianness */
464 repeat_buf
[1] = val
>> 8;
465 repeat_buf
[2] = val
>> 16;
466 repeat_buf
[3] = val
>> 24;
471 for(i
= 0; i
< repeat
; i
++) {
472 for(j
= 0; j
< size
; j
++) {
482 /* XXX: handle section symbols too */
483 n
= asm_int_expr(s1
);
485 tcc_error("attempt to .org backwards");
500 sym
= label_find(tok
);
502 sym
= label_push(&s1
->asm_labels
, tok
, 0);
503 sym
->type
.t
= VT_VOID
;
505 if (tok1
!= TOK_ASM_hidden
)
506 sym
->type
.t
&= ~VT_STATIC
;
507 if (tok1
== TOK_ASM_weak
)
508 sym
->type
.t
|= VT_WEAK
;
509 else if (tok1
== TOK_ASM_hidden
)
510 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
512 } while (tok
== ',');
525 expect("string constant");
527 size
= tokc
.cstr
->size
;
528 if (t
== TOK_ASM_ascii
&& size
> 0)
530 for(i
= 0; i
< size
; i
++)
535 } else if (tok
!= TOK_STR
) {
549 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
550 n
= asm_int_expr(s1
);
554 sprintf(sname
, ".%s%d", get_tok_str(tok1
, NULL
), n
);
556 sprintf(sname
, ".%s", get_tok_str(tok1
, NULL
));
557 use_section(s1
, sname
);
568 pstrcat(filename
, sizeof(filename
), tokc
.cstr
->data
);
570 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
572 if (s1
->warn_unsupported
)
573 tcc_warning("ignoring .file %s", filename
);
586 pstrcat(ident
, sizeof(ident
), tokc
.cstr
->data
);
588 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
590 if (s1
->warn_unsupported
)
591 tcc_warning("ignoring .ident %s", ident
);
601 sym
= label_find(tok
);
603 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
606 /* XXX .size name,label2-label1 */
607 if (s1
->warn_unsupported
)
608 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
612 while (tok
!= '\n' && tok
!= CH_EOF
) {
623 sym
= label_find(tok
);
625 sym
= label_push(&s1
->asm_labels
, tok
, 0);
626 sym
->type
.t
= VT_VOID
;
631 if (tok
== TOK_STR
) {
632 newtype
= tokc
.cstr
->data
;
634 if (tok
== '@' || tok
== '%')
636 newtype
= get_tok_str(tok
, NULL
);
639 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
640 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
642 else if (s1
->warn_unsupported
)
643 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
644 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
653 /* XXX: support more options */
656 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
658 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
660 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
664 /* skip section options */
667 expect("string constant");
670 last_text_section
= cur_text_section
;
671 use_section(s1
, sname
);
674 case TOK_ASM_previous
:
678 if (!last_text_section
)
679 tcc_error("no previous section referenced");
680 sec
= cur_text_section
;
681 use_section1(s1
, last_text_section
);
682 last_text_section
= sec
;
685 #ifdef TCC_TARGET_I386
699 #ifdef TCC_TARGET_X86_64
700 /* added for compatibility with GAS */
706 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
712 /* assemble a file */
713 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
718 /* print stats about opcodes */
723 int nb_op_vals
, i
, j
;
726 memset(freq
, 0, sizeof(freq
));
727 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
729 for(i
=0;i
<pa
->nb_ops
;i
++) {
730 for(j
=0;j
<nb_op_vals
;j
++) {
731 if (pa
->op_type
[i
] == op_vals
[j
])
734 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
738 for(i
=0;i
<nb_op_vals
;i
++) {
740 if ((v
& (v
- 1)) != 0)
741 printf("%3d: %08x\n", i
, v
);
743 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
744 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
745 freq
[0], freq
[1], freq
[2], freq
[3]);
749 /* XXX: undefine C labels */
751 ch
= file
->buf_ptr
[0];
752 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
753 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
755 parse_flags
|= PARSE_FLAG_PREPROCESS
;
760 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
763 /* horrible gas comment */
764 while (tok
!= TOK_LINEFEED
)
766 } else if (tok
== '.') {
767 asm_parse_directive(s1
);
768 } else if (tok
== TOK_PPNUM
) {
772 n
= strtoul(p
, (char **)&p
, 10);
775 /* new local label */
776 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
780 } else if (tok
>= TOK_IDENT
) {
781 /* instruction or label */
786 asm_new_label(s1
, opcode
, 0);
789 } else if (tok
== '=') {
792 n
= asm_int_expr(s1
);
793 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
796 asm_opcode(s1
, opcode
);
800 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
801 expect("end of line");
803 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
812 /* Assemble the current file */
813 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
820 /* default section is text */
821 cur_text_section
= text_section
;
822 ind
= cur_text_section
->data_offset
;
824 define_start
= define_stack
;
826 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
827 symbols can be safely used */
828 put_elf_sym(symtab_section
, 0, 0,
829 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
830 SHN_ABS
, file
->filename
);
832 ret
= tcc_assemble_internal(s1
, do_preprocess
);
834 cur_text_section
->data_offset
= ind
;
836 free_defines(define_start
);
841 /********************************************************************/
842 /* GCC inline asm support */
844 /* assemble the string 'str' in the current C compilation unit without
845 C preprocessing. NOTE: str is modified by modifying the '\0' at the
847 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
849 int saved_parse_flags
;
850 const int *saved_macro_ptr
;
852 saved_parse_flags
= parse_flags
;
853 saved_macro_ptr
= macro_ptr
;
855 tcc_open_bf(s1
, ":asm:", len
);
856 memcpy(file
->buffer
, str
, len
);
859 tcc_assemble_internal(s1
, 0);
862 parse_flags
= saved_parse_flags
;
863 macro_ptr
= saved_macro_ptr
;
866 /* find a constraint by its number or id (gcc 3 extended
867 syntax). return -1 if not found. Return in *pp in char after the
869 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
870 const char *name
, const char **pp
)
878 while (isnum(*name
)) {
879 index
= (index
* 10) + (*name
) - '0';
882 if ((unsigned)index
>= nb_operands
)
884 } else if (*name
== '[') {
886 p
= strchr(name
, ']');
888 ts
= tok_alloc(name
, p
- name
);
889 for(index
= 0; index
< nb_operands
; index
++) {
890 if (operands
[index
].id
== ts
->tok
)
907 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
909 CString
*out_str
, CString
*in_str
)
911 int c
, index
, modifier
;
926 if (*str
== 'c' || *str
== 'n' ||
927 *str
== 'b' || *str
== 'w' || *str
== 'h')
929 index
= find_constraint(operands
, nb_operands
, str
, &str
);
931 tcc_error("invalid operand reference after %%");
932 op
= &operands
[index
];
936 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
939 subst_asm_operand(out_str
, &sv
, modifier
);
942 cstr_ccat(out_str
, c
);
950 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
957 nb_operands
= *nb_operands_ptr
;
959 if (nb_operands
>= MAX_ASM_OPERANDS
)
960 tcc_error("too many asm operands");
961 op
= &operands
[nb_operands
++];
966 expect("identifier");
972 expect("string constant");
973 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
974 strcpy(op
->constraint
, tokc
.cstr
->data
);
981 /* we want to avoid LLOCAL case, except when the 'm'
982 constraint is used. Note that it may come from
983 register storage, so we need to convert (reg)
985 if ((vtop
->r
& VT_LVAL
) &&
986 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
987 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
988 !strchr(op
->constraint
, 'm')) {
1000 *nb_operands_ptr
= nb_operands
;
1004 /* parse the GCC asm() instruction */
1005 ST_FUNC
void asm_instr(void)
1007 CString astr
, astr1
;
1008 ASMOperand operands
[MAX_ASM_OPERANDS
];
1009 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1010 uint8_t clobber_regs
[NB_ASM_REGS
];
1013 /* since we always generate the asm() instruction, we can ignore
1015 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1018 parse_asm_str(&astr
);
1022 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1027 parse_asm_operands(operands
, &nb_operands
, 1);
1028 nb_outputs
= nb_operands
;
1033 parse_asm_operands(operands
, &nb_operands
, 0);
1036 /* XXX: handle registers */
1040 expect("string constant");
1041 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
1054 /* NOTE: we do not eat the ';' so that we can restore the current
1055 token after the assembler parsing */
1059 /* save all values in the memory */
1062 /* compute constraints */
1063 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1064 clobber_regs
, &out_reg
);
1066 /* substitute the operands in the asm string. No substitution is
1067 done if no operands (GCC behaviour) */
1069 printf("asm: \"%s\"\n", (char *)astr
.data
);
1072 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1078 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1081 /* generate loads */
1082 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1083 clobber_regs
, out_reg
);
1085 /* assemble the string with tcc internal assembler */
1086 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1088 /* restore the current C token */
1091 /* store the output values if needed */
1092 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1093 clobber_regs
, out_reg
);
1095 /* free everything */
1096 for(i
=0;i
<nb_operands
;i
++) {
1099 tcc_free(op
->constraint
);
1105 ST_FUNC
void asm_global_instr(void)
1110 parse_asm_str(&astr
);
1112 /* NOTE: we do not eat the ';' so that we can restore the current
1113 token after the assembler parsing */
1118 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1120 cur_text_section
= text_section
;
1121 ind
= cur_text_section
->data_offset
;
1123 /* assemble the string with tcc internal assembler */
1124 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1126 cur_text_section
->data_offset
= ind
;
1128 /* restore the current C token */
1133 #endif /* CONFIG_TCC_ASM */