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
)
51 n
= strtoul(p
, (char **)&p
, 0);
52 if (*p
== 'b' || *p
== 'f') {
53 /* backward or forward label */
54 label
= asm_get_local_label_name(s1
, n
);
55 sym
= label_find(label
);
57 /* backward : find the last corresponding defined label */
58 if (sym
&& sym
->r
== 0)
61 tcc_error("local label '%d' not found backward", n
);
65 /* if the last label is defined, then define a new one */
66 sym
= label_push(&s1
->asm_labels
, label
, 0);
67 sym
->type
.t
= VT_STATIC
| VT_VOID
;
72 } else if (*p
== '\0') {
76 tcc_error("invalid number syntax");
82 asm_expr_unary(s1
, pe
);
88 asm_expr_unary(s1
, pe
);
90 tcc_error("invalid operation with label");
110 sym_dot
.type
.t
= VT_VOID
| VT_STATIC
;
111 sym_dot
.r
= cur_text_section
->sh_num
;
116 if (tok
>= TOK_IDENT
) {
117 /* label case : if the label was not found, add one */
118 sym
= label_find(tok
);
120 sym
= label_push(&s1
->asm_labels
, tok
, 0);
121 /* NOTE: by default, the symbol is global */
122 sym
->type
.t
= VT_VOID
;
124 if (sym
->r
== SHN_ABS
) {
125 /* if absolute symbol, no need to put a symbol value */
134 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
140 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
145 asm_expr_unary(s1
, pe
);
148 if (op
!= '*' && op
!= '/' && op
!= '%' &&
149 op
!= TOK_SHL
&& op
!= TOK_SAR
)
152 asm_expr_unary(s1
, &e2
);
153 if (pe
->sym
|| e2
.sym
)
154 tcc_error("invalid operation with label");
162 tcc_error("division by zero");
182 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
187 asm_expr_prod(s1
, pe
);
190 if (op
!= '&' && op
!= '|' && op
!= '^')
193 asm_expr_prod(s1
, &e2
);
194 if (pe
->sym
|| e2
.sym
)
195 tcc_error("invalid operation with label");
211 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
216 asm_expr_logic(s1
, pe
);
219 if (op
!= '+' && op
!= '-')
222 asm_expr_logic(s1
, &e2
);
224 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
225 goto cannot_relocate
;
227 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
231 /* NOTE: we are less powerful than gas in that case
232 because we store only one symbol in the expression */
233 if (!pe
->sym
&& !e2
.sym
) {
235 } else if (pe
->sym
&& !e2
.sym
) {
237 } else if (pe
->sym
&& e2
.sym
) {
238 if (pe
->sym
== e2
.sym
) {
240 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
241 /* we also accept defined symbols in the same section */
242 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
244 goto cannot_relocate
;
246 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
249 tcc_error("invalid operation with label");
255 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
257 asm_expr_sum(s1
, pe
);
260 ST_FUNC
int asm_int_expr(TCCState
*s1
)
269 /* NOTE: the same name space as C labels is used to avoid using too
270 much memory when storing labels in TokenStrings */
271 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
272 int sh_num
, int value
)
276 sym
= label_find(label
);
279 /* the label is already defined */
281 tcc_error("assembler label '%s' already defined",
282 get_tok_str(label
, NULL
));
284 /* redefinition of local labels is possible */
290 sym
= label_push(&s1
->asm_labels
, label
, 0);
291 sym
->type
.t
= VT_STATIC
| VT_VOID
;
297 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
299 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
302 static void asm_free_labels(TCCState
*st
)
307 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
309 /* define symbol value in object file */
314 sec
= st
->sections
[s
->r
];
315 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
318 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
321 st
->asm_labels
= NULL
;
324 static void use_section1(TCCState
*s1
, Section
*sec
)
326 cur_text_section
->data_offset
= ind
;
327 cur_text_section
= sec
;
328 ind
= cur_text_section
->data_offset
;
331 static void use_section(TCCState
*s1
, const char *name
)
334 sec
= find_section(s1
, name
);
335 use_section1(s1
, sec
);
338 static void asm_parse_directive(TCCState
*s1
)
340 int n
, offset
, v
, size
, tok1
;
344 /* assembler directive */
345 sec
= cur_text_section
;
347 case TOK_ASMDIR_align
:
348 case TOK_ASMDIR_p2align
:
349 case TOK_ASMDIR_skip
:
350 case TOK_ASMDIR_space
:
353 n
= asm_int_expr(s1
);
354 if (tok1
== TOK_ASMDIR_p2align
)
357 tcc_error("invalid p2align, must be between 0 and 30");
359 tok1
= TOK_ASMDIR_align
;
361 if (tok1
== TOK_ASMDIR_align
) {
362 if (n
< 0 || (n
& (n
-1)) != 0)
363 tcc_error("alignment must be a positive power of two");
364 offset
= (ind
+ n
- 1) & -n
;
366 /* the section must have a compatible alignment */
367 if (sec
->sh_addralign
< n
)
368 sec
->sh_addralign
= n
;
375 v
= asm_int_expr(s1
);
378 if (sec
->sh_type
!= SHT_NOBITS
) {
379 sec
->data_offset
= ind
;
380 ptr
= section_ptr_add(sec
, size
);
381 memset(ptr
, v
, size
);
385 case TOK_ASMDIR_quad
:
392 if (tok
!= TOK_PPNUM
) {
394 tcc_error("64 bit constant");
396 vl
= strtoll(p
, (char **)&p
, 0);
400 if (sec
->sh_type
!= SHT_NOBITS
) {
401 /* XXX: endianness */
412 case TOK_ASMDIR_byte
:
415 case TOK_ASMDIR_word
:
416 case TOK_ASMDIR_short
:
419 case TOK_ASMDIR_long
:
427 if (sec
->sh_type
!= SHT_NOBITS
) {
446 case TOK_ASMDIR_fill
:
448 int repeat
, size
, val
, i
, j
;
449 uint8_t repeat_buf
[8];
451 repeat
= asm_int_expr(s1
);
453 tcc_error("repeat < 0; .fill ignored");
460 size
= asm_int_expr(s1
);
462 tcc_error("size < 0; .fill ignored");
469 val
= asm_int_expr(s1
);
472 /* XXX: endianness */
474 repeat_buf
[1] = val
>> 8;
475 repeat_buf
[2] = val
>> 16;
476 repeat_buf
[3] = val
>> 24;
481 for(i
= 0; i
< repeat
; i
++) {
482 for(j
= 0; j
< size
; j
++) {
488 case TOK_ASMDIR_rept
:
491 TokenString
*init_str
;
492 ParseState saved_parse_state
= {0};
494 repeat
= asm_int_expr(s1
);
495 init_str
= tok_str_alloc();
497 while ((tok
!= TOK_ASMDIR_endr
) && (tok
!= CH_EOF
)) {
498 tok_str_add_tok(init_str
);
501 if (tok
== CH_EOF
) tcc_error("we at end of file, .endr not found");
503 tok_str_add(init_str
, -1);
504 tok_str_add(init_str
, 0);
505 save_parse_state(&saved_parse_state
);
506 begin_macro(init_str
, 1);
507 while (repeat
-- > 0) {
508 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
));
509 macro_ptr
= init_str
->str
;
512 restore_parse_state(&saved_parse_state
);
519 /* XXX: handle section symbols too */
520 n
= asm_int_expr(s1
);
522 tcc_error("attempt to .org backwards");
528 case TOK_ASMDIR_globl
:
529 case TOK_ASMDIR_global
:
530 case TOK_ASMDIR_weak
:
531 case TOK_ASMDIR_hidden
:
537 sym
= label_find(tok
);
539 sym
= label_push(&s1
->asm_labels
, tok
, 0);
540 sym
->type
.t
= VT_VOID
;
542 if (tok1
!= TOK_ASMDIR_hidden
)
543 sym
->type
.t
&= ~VT_STATIC
;
544 if (tok1
== TOK_ASMDIR_weak
)
545 sym
->type
.t
|= VT_WEAK
;
546 else if (tok1
== TOK_ASMDIR_hidden
)
547 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
549 } while (tok
== ',');
551 case TOK_ASMDIR_string
:
552 case TOK_ASMDIR_ascii
:
553 case TOK_ASMDIR_asciz
:
562 expect("string constant");
564 size
= tokc
.str
.size
;
565 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
567 for(i
= 0; i
< size
; i
++)
572 } else if (tok
!= TOK_STR
) {
578 case TOK_ASMDIR_text
:
579 case TOK_ASMDIR_data
:
586 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
587 n
= asm_int_expr(s1
);
591 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
593 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
594 use_section(s1
, sname
);
597 case TOK_ASMDIR_file
:
605 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
607 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
609 if (s1
->warn_unsupported
)
610 tcc_warning("ignoring .file %s", filename
);
615 case TOK_ASMDIR_ident
:
623 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
625 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
627 if (s1
->warn_unsupported
)
628 tcc_warning("ignoring .ident %s", ident
);
633 case TOK_ASMDIR_size
:
638 sym
= label_find(tok
);
640 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
643 /* XXX .size name,label2-label1 */
644 if (s1
->warn_unsupported
)
645 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
649 while (tok
!= '\n' && tok
!= CH_EOF
) {
654 case TOK_ASMDIR_type
:
660 sym
= label_find(tok
);
662 sym
= label_push(&s1
->asm_labels
, tok
, 0);
663 sym
->type
.t
= VT_VOID
;
668 if (tok
== TOK_STR
) {
669 newtype
= tokc
.str
.data
;
671 if (tok
== '@' || tok
== '%')
673 newtype
= get_tok_str(tok
, NULL
);
676 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
677 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
679 else if (s1
->warn_unsupported
)
680 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
681 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
686 case TOK_ASMDIR_section
:
690 /* XXX: support more options */
693 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
695 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
697 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
701 /* skip section options */
704 expect("string constant");
708 if (tok
== '@' || tok
== '%')
713 last_text_section
= cur_text_section
;
714 use_section(s1
, sname
);
717 case TOK_ASMDIR_previous
:
721 if (!last_text_section
)
722 tcc_error("no previous section referenced");
723 sec
= cur_text_section
;
724 use_section1(s1
, last_text_section
);
725 last_text_section
= sec
;
728 #ifdef TCC_TARGET_I386
729 case TOK_ASMDIR_code16
:
735 case TOK_ASMDIR_code32
:
742 #ifdef TCC_TARGET_X86_64
743 /* added for compatibility with GAS */
744 case TOK_ASMDIR_code64
:
749 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
755 /* assemble a file */
756 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
761 /* print stats about opcodes */
766 int nb_op_vals
, i
, j
;
769 memset(freq
, 0, sizeof(freq
));
770 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
772 for(i
=0;i
<pa
->nb_ops
;i
++) {
773 for(j
=0;j
<nb_op_vals
;j
++) {
774 if (pa
->op_type
[i
] == op_vals
[j
])
777 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
781 for(i
=0;i
<nb_op_vals
;i
++) {
783 if ((v
& (v
- 1)) != 0)
784 printf("%3d: %08x\n", i
, v
);
786 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
787 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
788 freq
[0], freq
[1], freq
[2], freq
[3]);
792 /* XXX: undefine C labels */
794 ch
= file
->buf_ptr
[0];
795 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
796 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
798 parse_flags
|= PARSE_FLAG_PREPROCESS
;
803 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
806 /* horrible gas comment */
807 while (tok
!= TOK_LINEFEED
)
809 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
810 asm_parse_directive(s1
);
811 } else if (tok
== TOK_PPNUM
) {
815 n
= strtoul(p
, (char **)&p
, 10);
818 /* new local label */
819 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
823 } else if (tok
>= TOK_IDENT
) {
824 /* instruction or label */
828 /* handle "extern void vide(void); __asm__("vide: ret");" as
829 "__asm__("globl vide\nvide: ret");" */
830 Sym
*sym
= sym_find(opcode
);
831 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && nocode_wanted
) {
832 sym
= label_find(opcode
);
834 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
835 sym
->type
.t
= VT_VOID
;
839 asm_new_label(s1
, opcode
, 0);
842 } else if (tok
== '=') {
845 n
= asm_int_expr(s1
);
846 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
849 asm_opcode(s1
, opcode
);
853 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
854 expect("end of line");
856 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
865 /* Assemble the current file */
866 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
871 preprocess_start(s1
);
873 /* default section is text */
874 cur_text_section
= text_section
;
875 ind
= cur_text_section
->data_offset
;
877 define_start
= define_stack
;
879 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
880 symbols can be safely used */
881 put_elf_sym(symtab_section
, 0, 0,
882 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
883 SHN_ABS
, file
->filename
);
885 ret
= tcc_assemble_internal(s1
, do_preprocess
);
887 cur_text_section
->data_offset
= ind
;
889 free_defines(define_start
);
894 /********************************************************************/
895 /* GCC inline asm support */
897 /* assemble the string 'str' in the current C compilation unit without
898 C preprocessing. NOTE: str is modified by modifying the '\0' at the
900 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
902 int saved_parse_flags
;
903 const int *saved_macro_ptr
;
905 saved_parse_flags
= parse_flags
;
906 saved_macro_ptr
= macro_ptr
;
908 tcc_open_bf(s1
, ":asm:", len
);
909 memcpy(file
->buffer
, str
, len
);
912 tcc_assemble_internal(s1
, 0);
915 parse_flags
= saved_parse_flags
;
916 macro_ptr
= saved_macro_ptr
;
919 /* find a constraint by its number or id (gcc 3 extended
920 syntax). return -1 if not found. Return in *pp in char after the
922 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
923 const char *name
, const char **pp
)
931 while (isnum(*name
)) {
932 index
= (index
* 10) + (*name
) - '0';
935 if ((unsigned)index
>= nb_operands
)
937 } else if (*name
== '[') {
939 p
= strchr(name
, ']');
941 ts
= tok_alloc(name
, p
- name
);
942 for(index
= 0; index
< nb_operands
; index
++) {
943 if (operands
[index
].id
== ts
->tok
)
960 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
962 CString
*out_str
, CString
*in_str
)
964 int c
, index
, modifier
;
979 if (*str
== 'c' || *str
== 'n' ||
980 *str
== 'b' || *str
== 'w' ||
981 *str
== 'h' || *str
== 'k')
983 index
= find_constraint(operands
, nb_operands
, str
, &str
);
985 tcc_error("invalid operand reference after %%");
986 op
= &operands
[index
];
990 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
993 subst_asm_operand(out_str
, &sv
, modifier
);
996 cstr_ccat(out_str
, c
);
1004 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1011 nb_operands
= *nb_operands_ptr
;
1013 if (nb_operands
>= MAX_ASM_OPERANDS
)
1014 tcc_error("too many asm operands");
1015 op
= &operands
[nb_operands
++];
1019 if (tok
< TOK_IDENT
)
1020 expect("identifier");
1026 expect("string constant");
1027 op
->constraint
= tcc_malloc(tokc
.str
.size
);
1028 strcpy(op
->constraint
, tokc
.str
.data
);
1033 if (!(vtop
->type
.t
& VT_ARRAY
))
1036 /* we want to avoid LLOCAL case, except when the 'm'
1037 constraint is used. Note that it may come from
1038 register storage, so we need to convert (reg)
1040 if ((vtop
->r
& VT_LVAL
) &&
1041 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1042 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1043 !strchr(op
->constraint
, 'm')) {
1055 *nb_operands_ptr
= nb_operands
;
1059 /* parse the GCC asm() instruction */
1060 ST_FUNC
void asm_instr(void)
1062 CString astr
, astr1
;
1063 ASMOperand operands
[MAX_ASM_OPERANDS
];
1064 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1065 uint8_t clobber_regs
[NB_ASM_REGS
];
1068 /* since we always generate the asm() instruction, we can ignore
1070 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1073 parse_asm_str(&astr
);
1077 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1082 parse_asm_operands(operands
, &nb_operands
, 1);
1083 nb_outputs
= nb_operands
;
1088 parse_asm_operands(operands
, &nb_operands
, 0);
1091 /* XXX: handle registers */
1095 expect("string constant");
1096 asm_clobber(clobber_regs
, tokc
.str
.data
);
1109 /* NOTE: we do not eat the ';' so that we can restore the current
1110 token after the assembler parsing */
1114 /* save all values in the memory */
1117 /* compute constraints */
1118 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1119 clobber_regs
, &out_reg
);
1121 /* substitute the operands in the asm string. No substitution is
1122 done if no operands (GCC behaviour) */
1124 printf("asm: \"%s\"\n", (char *)astr
.data
);
1127 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1133 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1136 /* generate loads */
1137 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1138 clobber_regs
, out_reg
);
1140 /* assemble the string with tcc internal assembler */
1141 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1143 /* restore the current C token */
1146 /* store the output values if needed */
1147 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1148 clobber_regs
, out_reg
);
1150 /* free everything */
1151 for(i
=0;i
<nb_operands
;i
++) {
1154 tcc_free(op
->constraint
);
1160 ST_FUNC
void asm_global_instr(void)
1165 parse_asm_str(&astr
);
1167 /* NOTE: we do not eat the ';' so that we can restore the current
1168 token after the assembler parsing */
1173 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1175 cur_text_section
= text_section
;
1176 ind
= cur_text_section
->data_offset
;
1178 /* assemble the string with tcc internal assembler */
1179 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1181 cur_text_section
->data_offset
= ind
;
1183 /* restore the current C token */
1188 #endif /* CONFIG_TCC_ASM */