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 */
334 sec
= cur_text_section
;
336 case TOK_ASMDIR_align
:
337 case TOK_ASMDIR_p2align
:
338 case TOK_ASMDIR_skip
:
339 case TOK_ASMDIR_space
:
342 n
= asm_int_expr(s1
);
343 if (tok1
== TOK_ASMDIR_p2align
)
346 tcc_error("invalid p2align, must be between 0 and 30");
348 tok1
= TOK_ASMDIR_align
;
350 if (tok1
== TOK_ASMDIR_align
) {
351 if (n
< 0 || (n
& (n
-1)) != 0)
352 tcc_error("alignment must be a positive power of two");
353 offset
= (ind
+ n
- 1) & -n
;
355 /* the section must have a compatible alignment */
356 if (sec
->sh_addralign
< n
)
357 sec
->sh_addralign
= n
;
364 v
= asm_int_expr(s1
);
367 if (sec
->sh_type
!= SHT_NOBITS
) {
368 sec
->data_offset
= ind
;
369 ptr
= section_ptr_add(sec
, size
);
370 memset(ptr
, v
, size
);
374 case TOK_ASMDIR_quad
:
381 if (tok
!= TOK_PPNUM
) {
383 tcc_error("64 bit constant");
385 vl
= strtoll(p
, (char **)&p
, 0);
389 if (sec
->sh_type
!= SHT_NOBITS
) {
390 /* XXX: endianness */
401 case TOK_ASMDIR_byte
:
404 case TOK_ASMDIR_word
:
405 case TOK_ASMDIR_short
:
408 case TOK_ASMDIR_long
:
416 if (sec
->sh_type
!= SHT_NOBITS
) {
435 case TOK_ASMDIR_fill
:
437 int repeat
, size
, val
, i
, j
;
438 uint8_t repeat_buf
[8];
440 repeat
= asm_int_expr(s1
);
442 tcc_error("repeat < 0; .fill ignored");
449 size
= asm_int_expr(s1
);
451 tcc_error("size < 0; .fill ignored");
458 val
= asm_int_expr(s1
);
461 /* XXX: endianness */
463 repeat_buf
[1] = val
>> 8;
464 repeat_buf
[2] = val
>> 16;
465 repeat_buf
[3] = val
>> 24;
470 for(i
= 0; i
< repeat
; i
++) {
471 for(j
= 0; j
< size
; j
++) {
481 /* XXX: handle section symbols too */
482 n
= asm_int_expr(s1
);
484 tcc_error("attempt to .org backwards");
490 case TOK_ASMDIR_globl
:
491 case TOK_ASMDIR_global
:
492 case TOK_ASMDIR_weak
:
493 case TOK_ASMDIR_hidden
:
499 sym
= label_find(tok
);
501 sym
= label_push(&s1
->asm_labels
, tok
, 0);
502 sym
->type
.t
= VT_VOID
;
504 if (tok1
!= TOK_ASMDIR_hidden
)
505 sym
->type
.t
&= ~VT_STATIC
;
506 if (tok1
== TOK_ASMDIR_weak
)
507 sym
->type
.t
|= VT_WEAK
;
508 else if (tok1
== TOK_ASMDIR_hidden
)
509 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
511 } while (tok
== ',');
513 case TOK_ASMDIR_string
:
514 case TOK_ASMDIR_ascii
:
515 case TOK_ASMDIR_asciz
:
524 expect("string constant");
526 size
= tokc
.str
.size
;
527 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
529 for(i
= 0; i
< size
; i
++)
534 } else if (tok
!= TOK_STR
) {
540 case TOK_ASMDIR_text
:
541 case TOK_ASMDIR_data
:
548 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
549 n
= asm_int_expr(s1
);
553 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
555 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
556 use_section(s1
, sname
);
559 case TOK_ASMDIR_file
:
567 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
569 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
571 if (s1
->warn_unsupported
)
572 tcc_warning("ignoring .file %s", filename
);
577 case TOK_ASMDIR_ident
:
585 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
587 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
589 if (s1
->warn_unsupported
)
590 tcc_warning("ignoring .ident %s", ident
);
595 case TOK_ASMDIR_size
:
600 sym
= label_find(tok
);
602 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
605 /* XXX .size name,label2-label1 */
606 if (s1
->warn_unsupported
)
607 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
611 while (tok
!= '\n' && tok
!= CH_EOF
) {
616 case TOK_ASMDIR_type
:
622 sym
= label_find(tok
);
624 sym
= label_push(&s1
->asm_labels
, tok
, 0);
625 sym
->type
.t
= VT_VOID
;
630 if (tok
== TOK_STR
) {
631 newtype
= tokc
.str
.data
;
633 if (tok
== '@' || tok
== '%')
635 newtype
= get_tok_str(tok
, NULL
);
638 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
639 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
641 else if (s1
->warn_unsupported
)
642 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
643 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
648 case TOK_ASMDIR_section
:
652 /* XXX: support more options */
655 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
657 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
659 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
663 /* skip section options */
666 expect("string constant");
670 if (tok
== '@' || tok
== '%')
675 last_text_section
= cur_text_section
;
676 use_section(s1
, sname
);
679 case TOK_ASMDIR_previous
:
683 if (!last_text_section
)
684 tcc_error("no previous section referenced");
685 sec
= cur_text_section
;
686 use_section1(s1
, last_text_section
);
687 last_text_section
= sec
;
690 #ifdef TCC_TARGET_I386
691 case TOK_ASMDIR_code16
:
697 case TOK_ASMDIR_code32
:
704 #ifdef TCC_TARGET_X86_64
705 /* added for compatibility with GAS */
706 case TOK_ASMDIR_code64
:
711 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
717 /* assemble a file */
718 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
723 /* print stats about opcodes */
728 int nb_op_vals
, i
, j
;
731 memset(freq
, 0, sizeof(freq
));
732 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
734 for(i
=0;i
<pa
->nb_ops
;i
++) {
735 for(j
=0;j
<nb_op_vals
;j
++) {
736 if (pa
->op_type
[i
] == op_vals
[j
])
739 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
743 for(i
=0;i
<nb_op_vals
;i
++) {
745 if ((v
& (v
- 1)) != 0)
746 printf("%3d: %08x\n", i
, v
);
748 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
749 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
750 freq
[0], freq
[1], freq
[2], freq
[3]);
754 /* XXX: undefine C labels */
756 ch
= file
->buf_ptr
[0];
757 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
758 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
760 parse_flags
|= PARSE_FLAG_PREPROCESS
;
765 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
768 /* horrible gas comment */
769 while (tok
!= TOK_LINEFEED
)
771 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
772 asm_parse_directive(s1
);
773 } else if (tok
== TOK_PPNUM
) {
777 n
= strtoul(p
, (char **)&p
, 10);
780 /* new local label */
781 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
785 } else if (tok
>= TOK_IDENT
) {
786 /* instruction or label */
791 asm_new_label(s1
, opcode
, 0);
794 } else if (tok
== '=') {
797 n
= asm_int_expr(s1
);
798 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
801 asm_opcode(s1
, opcode
);
805 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
806 expect("end of line");
808 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
817 /* Assemble the current file */
818 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
825 /* default section is text */
826 cur_text_section
= text_section
;
827 ind
= cur_text_section
->data_offset
;
829 define_start
= define_stack
;
831 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
832 symbols can be safely used */
833 put_elf_sym(symtab_section
, 0, 0,
834 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
835 SHN_ABS
, file
->filename
);
837 ret
= tcc_assemble_internal(s1
, do_preprocess
);
839 cur_text_section
->data_offset
= ind
;
841 free_defines(define_start
);
846 /********************************************************************/
847 /* GCC inline asm support */
849 /* assemble the string 'str' in the current C compilation unit without
850 C preprocessing. NOTE: str is modified by modifying the '\0' at the
852 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
854 int saved_parse_flags
;
855 const int *saved_macro_ptr
;
857 saved_parse_flags
= parse_flags
;
858 saved_macro_ptr
= macro_ptr
;
860 tcc_open_bf(s1
, ":asm:", len
);
861 memcpy(file
->buffer
, str
, len
);
864 tcc_assemble_internal(s1
, 0);
867 parse_flags
= saved_parse_flags
;
868 macro_ptr
= saved_macro_ptr
;
871 /* find a constraint by its number or id (gcc 3 extended
872 syntax). return -1 if not found. Return in *pp in char after the
874 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
875 const char *name
, const char **pp
)
883 while (isnum(*name
)) {
884 index
= (index
* 10) + (*name
) - '0';
887 if ((unsigned)index
>= nb_operands
)
889 } else if (*name
== '[') {
891 p
= strchr(name
, ']');
893 ts
= tok_alloc(name
, p
- name
);
894 for(index
= 0; index
< nb_operands
; index
++) {
895 if (operands
[index
].id
== ts
->tok
)
912 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
914 CString
*out_str
, CString
*in_str
)
916 int c
, index
, modifier
;
931 if (*str
== 'c' || *str
== 'n' ||
932 *str
== 'b' || *str
== 'w' || *str
== 'h')
934 index
= find_constraint(operands
, nb_operands
, str
, &str
);
936 tcc_error("invalid operand reference after %%");
937 op
= &operands
[index
];
941 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
944 subst_asm_operand(out_str
, &sv
, modifier
);
947 cstr_ccat(out_str
, c
);
955 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
962 nb_operands
= *nb_operands_ptr
;
964 if (nb_operands
>= MAX_ASM_OPERANDS
)
965 tcc_error("too many asm operands");
966 op
= &operands
[nb_operands
++];
971 expect("identifier");
977 expect("string constant");
978 op
->constraint
= tcc_malloc(tokc
.str
.size
);
979 strcpy(op
->constraint
, tokc
.str
.data
);
986 /* we want to avoid LLOCAL case, except when the 'm'
987 constraint is used. Note that it may come from
988 register storage, so we need to convert (reg)
990 if ((vtop
->r
& VT_LVAL
) &&
991 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
992 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
993 !strchr(op
->constraint
, 'm')) {
1005 *nb_operands_ptr
= nb_operands
;
1009 /* parse the GCC asm() instruction */
1010 ST_FUNC
void asm_instr(void)
1012 CString astr
, astr1
;
1013 ASMOperand operands
[MAX_ASM_OPERANDS
];
1014 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1015 uint8_t clobber_regs
[NB_ASM_REGS
];
1018 /* since we always generate the asm() instruction, we can ignore
1020 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1023 parse_asm_str(&astr
);
1027 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1032 parse_asm_operands(operands
, &nb_operands
, 1);
1033 nb_outputs
= nb_operands
;
1038 parse_asm_operands(operands
, &nb_operands
, 0);
1041 /* XXX: handle registers */
1045 expect("string constant");
1046 asm_clobber(clobber_regs
, tokc
.str
.data
);
1059 /* NOTE: we do not eat the ';' so that we can restore the current
1060 token after the assembler parsing */
1064 /* save all values in the memory */
1067 /* compute constraints */
1068 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1069 clobber_regs
, &out_reg
);
1071 /* substitute the operands in the asm string. No substitution is
1072 done if no operands (GCC behaviour) */
1074 printf("asm: \"%s\"\n", (char *)astr
.data
);
1077 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1083 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1086 /* generate loads */
1087 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1088 clobber_regs
, out_reg
);
1090 /* assemble the string with tcc internal assembler */
1091 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1093 /* restore the current C token */
1096 /* store the output values if needed */
1097 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1098 clobber_regs
, out_reg
);
1100 /* free everything */
1101 for(i
=0;i
<nb_operands
;i
++) {
1104 tcc_free(op
->constraint
);
1110 ST_FUNC
void asm_global_instr(void)
1115 parse_asm_str(&astr
);
1117 /* NOTE: we do not eat the ';' so that we can restore the current
1118 token after the assembler parsing */
1123 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1125 cur_text_section
= text_section
;
1126 ind
= cur_text_section
->data_offset
;
1128 /* assemble the string with tcc internal assembler */
1129 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1131 cur_text_section
->data_offset
= ind
;
1133 /* restore the current C token */
1138 #endif /* CONFIG_TCC_ASM */