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
);
35 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
);
38 /* We do not use the C expression parser to handle symbols. Maybe the
39 C expression parser could be tweaked to do so. */
41 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
50 n
= strtoul(p
, (char **)&p
, 0);
51 if (*p
== 'b' || *p
== 'f') {
52 /* backward or forward label */
53 label
= asm_get_local_label_name(s1
, n
);
54 sym
= label_find(label
);
56 /* backward : find the last corresponding defined label */
57 if (sym
&& sym
->r
== 0)
60 tcc_error("local label '%d' not found backward", n
);
64 /* if the last label is defined, then define a new one */
65 sym
= label_push(&s1
->asm_labels
, label
, 0);
66 sym
->type
.t
= VT_STATIC
| VT_VOID
;
71 } else if (*p
== '\0') {
75 tcc_error("invalid number syntax");
81 asm_expr_unary(s1
, pe
);
87 asm_expr_unary(s1
, pe
);
89 tcc_error("invalid operation with label");
109 sym_dot
.type
.t
= VT_VOID
| VT_STATIC
;
110 sym_dot
.r
= cur_text_section
->sh_num
;
115 if (tok
>= TOK_IDENT
) {
116 /* label case : if the label was not found, add one */
117 sym
= label_find(tok
);
119 sym
= label_push(&s1
->asm_labels
, tok
, 0);
120 /* NOTE: by default, the symbol is global */
121 sym
->type
.t
= VT_VOID
;
123 if (sym
->r
== SHN_ABS
) {
124 /* if absolute symbol, no need to put a symbol value */
133 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
139 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
144 asm_expr_unary(s1
, pe
);
147 if (op
!= '*' && op
!= '/' && op
!= '%' &&
148 op
!= TOK_SHL
&& op
!= TOK_SAR
)
151 asm_expr_unary(s1
, &e2
);
152 if (pe
->sym
|| e2
.sym
)
153 tcc_error("invalid operation with label");
161 tcc_error("division by zero");
181 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
186 asm_expr_prod(s1
, pe
);
189 if (op
!= '&' && op
!= '|' && op
!= '^')
192 asm_expr_prod(s1
, &e2
);
193 if (pe
->sym
|| e2
.sym
)
194 tcc_error("invalid operation with label");
210 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
215 asm_expr_logic(s1
, pe
);
218 if (op
!= '+' && op
!= '-')
221 asm_expr_logic(s1
, &e2
);
223 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
224 goto cannot_relocate
;
226 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
230 /* NOTE: we are less powerful than gas in that case
231 because we store only one symbol in the expression */
232 if (!pe
->sym
&& !e2
.sym
) {
234 } else if (pe
->sym
&& !e2
.sym
) {
236 } else if (pe
->sym
&& e2
.sym
) {
237 if (pe
->sym
== e2
.sym
) {
239 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
240 /* we also accept defined symbols in the same section */
241 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
243 goto cannot_relocate
;
245 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
248 tcc_error("invalid operation with label");
254 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
256 asm_expr_sum(s1
, pe
);
259 ST_FUNC
int asm_int_expr(TCCState
*s1
)
268 /* NOTE: the same name space as C labels is used to avoid using too
269 much memory when storing labels in TokenStrings */
270 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
271 int sh_num
, int value
)
275 sym
= label_find(label
);
278 /* the label is already defined */
280 tcc_error("assembler label '%s' already defined",
281 get_tok_str(label
, NULL
));
283 /* redefinition of local labels is possible */
289 sym
= label_push(&s1
->asm_labels
, label
, 0);
290 sym
->type
.t
= VT_STATIC
| VT_VOID
;
296 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
298 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
301 static void asm_free_labels(TCCState
*st
)
306 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
308 /* define symbol value in object file */
313 sec
= st
->sections
[s
->r
];
314 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
317 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
320 st
->asm_labels
= NULL
;
323 static void use_section1(TCCState
*s1
, Section
*sec
)
325 cur_text_section
->data_offset
= ind
;
326 cur_text_section
= sec
;
327 ind
= cur_text_section
->data_offset
;
330 static void use_section(TCCState
*s1
, const char *name
)
333 sec
= find_section(s1
, name
);
334 use_section1(s1
, sec
);
337 static void asm_parse_directive(TCCState
*s1
)
339 int n
, offset
, v
, size
, tok1
;
343 /* assembler directive */
344 sec
= cur_text_section
;
346 case TOK_ASMDIR_align
:
347 case TOK_ASMDIR_p2align
:
348 case TOK_ASMDIR_skip
:
349 case TOK_ASMDIR_space
:
352 n
= asm_int_expr(s1
);
353 if (tok1
== TOK_ASMDIR_p2align
)
356 tcc_error("invalid p2align, must be between 0 and 30");
358 tok1
= TOK_ASMDIR_align
;
360 if (tok1
== TOK_ASMDIR_align
) {
361 if (n
< 0 || (n
& (n
-1)) != 0)
362 tcc_error("alignment must be a positive power of two");
363 offset
= (ind
+ n
- 1) & -n
;
365 /* the section must have a compatible alignment */
366 if (sec
->sh_addralign
< n
)
367 sec
->sh_addralign
= n
;
374 v
= asm_int_expr(s1
);
377 if (sec
->sh_type
!= SHT_NOBITS
) {
378 sec
->data_offset
= ind
;
379 ptr
= section_ptr_add(sec
, size
);
380 memset(ptr
, v
, size
);
384 case TOK_ASMDIR_quad
:
391 if (tok
!= TOK_PPNUM
) {
393 tcc_error("64 bit constant");
395 vl
= strtoll(p
, (char **)&p
, 0);
399 if (sec
->sh_type
!= SHT_NOBITS
) {
400 /* XXX: endianness */
411 case TOK_ASMDIR_byte
:
414 case TOK_ASMDIR_word
:
415 case TOK_ASMDIR_short
:
418 case TOK_ASMDIR_long
:
426 if (sec
->sh_type
!= SHT_NOBITS
) {
445 case TOK_ASMDIR_fill
:
447 int repeat
, size
, val
, i
, j
;
448 uint8_t repeat_buf
[8];
450 repeat
= asm_int_expr(s1
);
452 tcc_error("repeat < 0; .fill ignored");
459 size
= asm_int_expr(s1
);
461 tcc_error("size < 0; .fill ignored");
468 val
= asm_int_expr(s1
);
471 /* XXX: endianness */
473 repeat_buf
[1] = val
>> 8;
474 repeat_buf
[2] = val
>> 16;
475 repeat_buf
[3] = val
>> 24;
480 for(i
= 0; i
< repeat
; i
++) {
481 for(j
= 0; j
< size
; j
++) {
487 case TOK_ASMDIR_rept
:
490 TokenString init_str
;
491 ParseState saved_parse_state
= {0};
493 repeat
= asm_int_expr(s1
);
494 tok_str_new(&init_str
);
496 while ((tok
!= TOK_ASMDIR_endr
) && (tok
!= CH_EOF
)) {
497 tok_str_add_tok(&init_str
);
500 if (tok
== CH_EOF
) tcc_error("we at end of file, .endr not found");
502 tok_str_add(&init_str
, -1);
503 tok_str_add(&init_str
, 0);
504 save_parse_state(&saved_parse_state
);
505 begin_macro(&init_str
, 0);
506 while (repeat
-- > 0) {
507 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
));
508 macro_ptr
= init_str
.str
;
511 restore_parse_state(&saved_parse_state
);
518 /* XXX: handle section symbols too */
519 n
= asm_int_expr(s1
);
521 tcc_error("attempt to .org backwards");
527 case TOK_ASMDIR_globl
:
528 case TOK_ASMDIR_global
:
529 case TOK_ASMDIR_weak
:
530 case TOK_ASMDIR_hidden
:
536 sym
= label_find(tok
);
538 sym
= label_push(&s1
->asm_labels
, tok
, 0);
539 sym
->type
.t
= VT_VOID
;
541 if (tok1
!= TOK_ASMDIR_hidden
)
542 sym
->type
.t
&= ~VT_STATIC
;
543 if (tok1
== TOK_ASMDIR_weak
)
544 sym
->type
.t
|= VT_WEAK
;
545 else if (tok1
== TOK_ASMDIR_hidden
)
546 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
548 } while (tok
== ',');
550 case TOK_ASMDIR_string
:
551 case TOK_ASMDIR_ascii
:
552 case TOK_ASMDIR_asciz
:
561 expect("string constant");
563 size
= tokc
.str
.size
;
564 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
566 for(i
= 0; i
< size
; i
++)
571 } else if (tok
!= TOK_STR
) {
577 case TOK_ASMDIR_text
:
578 case TOK_ASMDIR_data
:
585 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
586 n
= asm_int_expr(s1
);
590 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
592 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
593 use_section(s1
, sname
);
596 case TOK_ASMDIR_file
:
604 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
606 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
608 if (s1
->warn_unsupported
)
609 tcc_warning("ignoring .file %s", filename
);
614 case TOK_ASMDIR_ident
:
622 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
624 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
626 if (s1
->warn_unsupported
)
627 tcc_warning("ignoring .ident %s", ident
);
632 case TOK_ASMDIR_size
:
637 sym
= label_find(tok
);
639 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
642 /* XXX .size name,label2-label1 */
643 if (s1
->warn_unsupported
)
644 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
648 while (tok
!= '\n' && tok
!= CH_EOF
) {
653 case TOK_ASMDIR_type
:
659 sym
= label_find(tok
);
661 sym
= label_push(&s1
->asm_labels
, tok
, 0);
662 sym
->type
.t
= VT_VOID
;
667 if (tok
== TOK_STR
) {
668 newtype
= tokc
.str
.data
;
670 if (tok
== '@' || tok
== '%')
672 newtype
= get_tok_str(tok
, NULL
);
675 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
676 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
678 else if (s1
->warn_unsupported
)
679 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
680 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
685 case TOK_ASMDIR_section
:
689 /* XXX: support more options */
692 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
694 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
696 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
700 /* skip section options */
703 expect("string constant");
707 if (tok
== '@' || tok
== '%')
712 last_text_section
= cur_text_section
;
713 use_section(s1
, sname
);
716 case TOK_ASMDIR_previous
:
720 if (!last_text_section
)
721 tcc_error("no previous section referenced");
722 sec
= cur_text_section
;
723 use_section1(s1
, last_text_section
);
724 last_text_section
= sec
;
727 #ifdef TCC_TARGET_I386
728 case TOK_ASMDIR_code16
:
734 case TOK_ASMDIR_code32
:
741 #ifdef TCC_TARGET_X86_64
742 /* added for compatibility with GAS */
743 case TOK_ASMDIR_code64
:
748 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
754 /* assemble a file */
755 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
760 /* print stats about opcodes */
765 int nb_op_vals
, i
, j
;
768 memset(freq
, 0, sizeof(freq
));
769 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
771 for(i
=0;i
<pa
->nb_ops
;i
++) {
772 for(j
=0;j
<nb_op_vals
;j
++) {
773 if (pa
->op_type
[i
] == op_vals
[j
])
776 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
780 for(i
=0;i
<nb_op_vals
;i
++) {
782 if ((v
& (v
- 1)) != 0)
783 printf("%3d: %08x\n", i
, v
);
785 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
786 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
787 freq
[0], freq
[1], freq
[2], freq
[3]);
791 /* XXX: undefine C labels */
793 ch
= file
->buf_ptr
[0];
794 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
795 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
797 parse_flags
|= PARSE_FLAG_PREPROCESS
;
802 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
805 /* horrible gas comment */
806 while (tok
!= TOK_LINEFEED
)
808 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
809 asm_parse_directive(s1
);
810 } else if (tok
== TOK_PPNUM
) {
814 n
= strtoul(p
, (char **)&p
, 10);
817 /* new local label */
818 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
822 } else if (tok
>= TOK_IDENT
) {
823 /* instruction or label */
827 /* handle "extern void vide(void); __asm__("vide: ret");" as
828 "__asm__("globl vide\nvide: ret");" */
829 Sym
*sym
= sym_find(opcode
);
830 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && nocode_wanted
) {
831 sym
= label_find(opcode
);
833 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
834 sym
->type
.t
= VT_VOID
;
838 asm_new_label(s1
, opcode
, 0);
841 } else if (tok
== '=') {
844 n
= asm_int_expr(s1
);
845 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
848 asm_opcode(s1
, opcode
);
852 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
853 expect("end of line");
855 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
864 /* Assemble the current file */
865 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
872 /* default section is text */
873 cur_text_section
= text_section
;
874 ind
= cur_text_section
->data_offset
;
876 define_start
= define_stack
;
878 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
879 symbols can be safely used */
880 put_elf_sym(symtab_section
, 0, 0,
881 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
882 SHN_ABS
, file
->filename
);
884 ret
= tcc_assemble_internal(s1
, do_preprocess
);
886 cur_text_section
->data_offset
= ind
;
888 free_defines(define_start
);
893 /********************************************************************/
894 /* GCC inline asm support */
896 /* assemble the string 'str' in the current C compilation unit without
897 C preprocessing. NOTE: str is modified by modifying the '\0' at the
899 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
901 int saved_parse_flags
;
902 const int *saved_macro_ptr
;
904 saved_parse_flags
= parse_flags
;
905 saved_macro_ptr
= macro_ptr
;
907 tcc_open_bf(s1
, ":asm:", len
);
908 memcpy(file
->buffer
, str
, len
);
911 tcc_assemble_internal(s1
, 0);
914 parse_flags
= saved_parse_flags
;
915 macro_ptr
= saved_macro_ptr
;
918 /* find a constraint by its number or id (gcc 3 extended
919 syntax). return -1 if not found. Return in *pp in char after the
921 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
922 const char *name
, const char **pp
)
930 while (isnum(*name
)) {
931 index
= (index
* 10) + (*name
) - '0';
934 if ((unsigned)index
>= nb_operands
)
936 } else if (*name
== '[') {
938 p
= strchr(name
, ']');
940 ts
= tok_alloc(name
, p
- name
);
941 for(index
= 0; index
< nb_operands
; index
++) {
942 if (operands
[index
].id
== ts
->tok
)
959 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
961 CString
*out_str
, CString
*in_str
)
963 int c
, index
, modifier
;
978 if (*str
== 'c' || *str
== 'n' ||
979 *str
== 'b' || *str
== 'w' || *str
== 'h')
981 index
= find_constraint(operands
, nb_operands
, str
, &str
);
983 tcc_error("invalid operand reference after %%");
984 op
= &operands
[index
];
988 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
991 subst_asm_operand(out_str
, &sv
, modifier
);
994 cstr_ccat(out_str
, c
);
1002 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1009 nb_operands
= *nb_operands_ptr
;
1011 if (nb_operands
>= MAX_ASM_OPERANDS
)
1012 tcc_error("too many asm operands");
1013 op
= &operands
[nb_operands
++];
1017 if (tok
< TOK_IDENT
)
1018 expect("identifier");
1024 expect("string constant");
1025 op
->constraint
= tcc_malloc(tokc
.str
.size
);
1026 strcpy(op
->constraint
, tokc
.str
.data
);
1033 /* we want to avoid LLOCAL case, except when the 'm'
1034 constraint is used. Note that it may come from
1035 register storage, so we need to convert (reg)
1037 if ((vtop
->r
& VT_LVAL
) &&
1038 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1039 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1040 !strchr(op
->constraint
, 'm')) {
1052 *nb_operands_ptr
= nb_operands
;
1056 /* parse the GCC asm() instruction */
1057 ST_FUNC
void asm_instr(void)
1059 CString astr
, astr1
;
1060 ASMOperand operands
[MAX_ASM_OPERANDS
];
1061 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1062 uint8_t clobber_regs
[NB_ASM_REGS
];
1065 /* since we always generate the asm() instruction, we can ignore
1067 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1070 parse_asm_str(&astr
);
1074 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1079 parse_asm_operands(operands
, &nb_operands
, 1);
1080 nb_outputs
= nb_operands
;
1085 parse_asm_operands(operands
, &nb_operands
, 0);
1088 /* XXX: handle registers */
1092 expect("string constant");
1093 asm_clobber(clobber_regs
, tokc
.str
.data
);
1106 /* NOTE: we do not eat the ';' so that we can restore the current
1107 token after the assembler parsing */
1111 /* save all values in the memory */
1114 /* compute constraints */
1115 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1116 clobber_regs
, &out_reg
);
1118 /* substitute the operands in the asm string. No substitution is
1119 done if no operands (GCC behaviour) */
1121 printf("asm: \"%s\"\n", (char *)astr
.data
);
1124 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1130 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1133 /* generate loads */
1134 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1135 clobber_regs
, out_reg
);
1137 /* assemble the string with tcc internal assembler */
1138 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1140 /* restore the current C token */
1143 /* store the output values if needed */
1144 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1145 clobber_regs
, out_reg
);
1147 /* free everything */
1148 for(i
=0;i
<nb_operands
;i
++) {
1151 tcc_free(op
->constraint
);
1157 ST_FUNC
void asm_global_instr(void)
1162 parse_asm_str(&astr
);
1164 /* NOTE: we do not eat the ';' so that we can restore the current
1165 token after the assembler parsing */
1170 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1172 cur_text_section
= text_section
;
1173 ind
= cur_text_section
->data_offset
;
1175 /* assemble the string with tcc internal assembler */
1176 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1178 cur_text_section
->data_offset
= ind
;
1180 /* restore the current C token */
1185 #endif /* CONFIG_TCC_ASM */