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
21 static int asm_get_local_label_name(TCCState
*s1
, unsigned int n
)
26 snprintf(buf
, sizeof(buf
), "L..%u", n
);
27 ts
= tok_alloc(buf
, strlen(buf
));
31 static void asm_expr(TCCState
*s1
, ExprValue
*pe
);
33 /* We do not use the C expression parser to handle symbols. Maybe the
34 C expression parser could be tweaked to do so. */
36 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
45 n
= strtoul(p
, (char **)&p
, 0);
46 if (*p
== 'b' || *p
== 'f') {
47 /* backward or forward label */
48 label
= asm_get_local_label_name(s1
, n
);
49 sym
= label_find(label
);
51 /* backward : find the last corresponding defined label */
52 if (sym
&& sym
->r
== 0)
55 error("local label '%d' not found backward", n
);
59 /* if the last label is defined, then define a new one */
60 sym
= label_push(&s1
->asm_labels
, label
, 0);
61 sym
->type
.t
= VT_STATIC
| VT_VOID
;
66 } else if (*p
== '\0') {
70 error("invalid number syntax");
76 asm_expr_unary(s1
, pe
);
82 asm_expr_unary(s1
, pe
);
84 error("invalid operation with label");
102 if (tok
>= TOK_IDENT
) {
103 /* label case : if the label was not found, add one */
104 sym
= label_find(tok
);
106 sym
= label_push(&s1
->asm_labels
, tok
, 0);
107 /* NOTE: by default, the symbol is global */
108 sym
->type
.t
= VT_VOID
;
110 if (sym
->r
== SHN_ABS
) {
111 /* if absolute symbol, no need to put a symbol value */
120 error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
126 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
131 asm_expr_unary(s1
, pe
);
134 if (op
!= '*' && op
!= '/' && op
!= '%' &&
135 op
!= TOK_SHL
&& op
!= TOK_SAR
)
138 asm_expr_unary(s1
, &e2
);
139 if (pe
->sym
|| e2
.sym
)
140 error("invalid operation with label");
148 error("division by zero");
168 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
173 asm_expr_prod(s1
, pe
);
176 if (op
!= '&' && op
!= '|' && op
!= '^')
179 asm_expr_prod(s1
, &e2
);
180 if (pe
->sym
|| e2
.sym
)
181 error("invalid operation with label");
197 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
202 asm_expr_logic(s1
, pe
);
205 if (op
!= '+' && op
!= '-')
208 asm_expr_logic(s1
, &e2
);
210 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
211 goto cannot_relocate
;
213 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
217 /* NOTE: we are less powerful than gas in that case
218 because we store only one symbol in the expression */
219 if (!pe
->sym
&& !e2
.sym
) {
221 } else if (pe
->sym
&& !e2
.sym
) {
223 } else if (pe
->sym
&& e2
.sym
) {
224 if (pe
->sym
== e2
.sym
) {
226 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
227 /* we also accept defined symbols in the same section */
228 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
230 goto cannot_relocate
;
232 pe
->sym
= NULL
; /* same symbols can be substracted to NULL */
235 error("invalid operation with label");
241 static void asm_expr(TCCState
*s1
, ExprValue
*pe
)
243 asm_expr_sum(s1
, pe
);
246 static int asm_int_expr(TCCState
*s1
)
255 /* NOTE: the same name space as C labels is used to avoid using too
256 much memory when storing labels in TokenStrings */
257 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
258 int sh_num
, int value
)
262 sym
= label_find(label
);
265 /* the label is already defined */
267 error("assembler label '%s' already defined",
268 get_tok_str(label
, NULL
));
270 /* redefinition of local labels is possible */
276 sym
= label_push(&s1
->asm_labels
, label
, 0);
277 sym
->type
.t
= VT_STATIC
| VT_VOID
;
283 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
285 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
288 static void asm_free_labels(TCCState
*st
)
293 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
295 /* define symbol value in object file */
300 sec
= st
->sections
[s
->r
];
301 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
304 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
307 st
->asm_labels
= NULL
;
310 static void use_section1(TCCState
*s1
, Section
*sec
)
312 cur_text_section
->data_offset
= ind
;
313 cur_text_section
= sec
;
314 ind
= cur_text_section
->data_offset
;
317 static void use_section(TCCState
*s1
, const char *name
)
320 sec
= find_section(s1
, name
);
321 use_section1(s1
, sec
);
324 static void asm_parse_directive(TCCState
*s1
)
326 int n
, offset
, v
, size
, tok1
;
330 /* assembler directive */
332 sec
= cur_text_section
;
339 n
= asm_int_expr(s1
);
340 if (tok1
== TOK_ASM_align
) {
341 if (n
< 0 || (n
& (n
-1)) != 0)
342 error("alignment must be a positive power of two");
343 offset
= (ind
+ n
- 1) & -n
;
345 /* the section must have a compatible alignment */
346 if (sec
->sh_addralign
< n
)
347 sec
->sh_addralign
= n
;
354 v
= asm_int_expr(s1
);
357 if (sec
->sh_type
!= SHT_NOBITS
) {
358 sec
->data_offset
= ind
;
359 ptr
= section_ptr_add(sec
, size
);
360 memset(ptr
, v
, size
);
371 if (tok
!= TOK_PPNUM
) {
373 error("64 bit constant");
375 vl
= strtoll(p
, (char **)&p
, 0);
379 if (sec
->sh_type
!= SHT_NOBITS
) {
380 /* XXX: endianness */
406 if (sec
->sh_type
!= SHT_NOBITS
) {
427 int repeat
, size
, val
, i
, j
;
428 uint8_t repeat_buf
[8];
430 repeat
= asm_int_expr(s1
);
432 error("repeat < 0; .fill ignored");
439 size
= asm_int_expr(s1
);
441 error("size < 0; .fill ignored");
448 val
= asm_int_expr(s1
);
451 /* XXX: endianness */
453 repeat_buf
[1] = val
>> 8;
454 repeat_buf
[2] = val
>> 16;
455 repeat_buf
[3] = val
>> 24;
460 for(i
= 0; i
< repeat
; i
++) {
461 for(j
= 0; j
< size
; j
++) {
471 /* XXX: handle section symbols too */
472 n
= asm_int_expr(s1
);
474 error("attempt to .org backwards");
486 sym
= label_find(tok
);
488 sym
= label_push(&s1
->asm_labels
, tok
, 0);
489 sym
->type
.t
= VT_VOID
;
491 sym
->type
.t
&= ~VT_STATIC
;
506 expect("string constant");
508 size
= tokc
.cstr
->size
;
509 if (t
== TOK_ASM_ascii
&& size
> 0)
511 for(i
= 0; i
< size
; i
++)
516 } else if (tok
!= TOK_STR
) {
530 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
531 n
= asm_int_expr(s1
);
534 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
535 use_section(s1
, sname
);
542 /* XXX: support more options */
545 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
547 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
549 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
553 /* skip section options */
556 expect("string constant");
559 last_text_section
= cur_text_section
;
560 use_section(s1
, sname
);
563 case TOK_ASM_previous
:
567 if (!last_text_section
)
568 error("no previous section referenced");
569 sec
= cur_text_section
;
570 use_section1(s1
, last_text_section
);
571 last_text_section
= sec
;
574 #ifdef TCC_TARGET_I386
588 #ifdef TCC_TARGET_X86_64
589 /* added for compatibility with GAS */
595 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
601 /* assemble a file */
602 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
607 /* print stats about opcodes */
612 int nb_op_vals
, i
, j
;
615 memset(freq
, 0, sizeof(freq
));
616 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
618 for(i
=0;i
<pa
->nb_ops
;i
++) {
619 for(j
=0;j
<nb_op_vals
;j
++) {
620 if (pa
->op_type
[i
] == op_vals
[j
])
623 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
627 for(i
=0;i
<nb_op_vals
;i
++) {
629 if ((v
& (v
- 1)) != 0)
630 printf("%3d: %08x\n", i
, v
);
632 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
633 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
634 freq
[0], freq
[1], freq
[2], freq
[3]);
638 /* XXX: undefine C labels */
640 ch
= file
->buf_ptr
[0];
641 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
642 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
644 parse_flags
|= PARSE_FLAG_PREPROCESS
;
649 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
652 /* horrible gas comment */
653 while (tok
!= TOK_LINEFEED
)
655 } else if (tok
== '.') {
656 asm_parse_directive(s1
);
657 } else if (tok
== TOK_PPNUM
) {
661 n
= strtoul(p
, (char **)&p
, 10);
664 /* new local label */
665 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
669 } else if (tok
>= TOK_IDENT
) {
670 /* instruction or label */
675 asm_new_label(s1
, opcode
, 0);
678 } else if (tok
== '=') {
681 n
= asm_int_expr(s1
);
682 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
685 asm_opcode(s1
, opcode
);
689 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
690 expect("end of line");
692 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
701 /* Assemble the current file */
702 static int tcc_assemble(TCCState
*s1
, int do_preprocess
)
709 /* default section is text */
710 cur_text_section
= text_section
;
711 ind
= cur_text_section
->data_offset
;
713 define_start
= define_stack
;
715 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
716 symbols can be safely used */
717 put_elf_sym(symtab_section
, 0, 0,
718 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
719 SHN_ABS
, file
->filename
);
721 ret
= tcc_assemble_internal(s1
, do_preprocess
);
723 cur_text_section
->data_offset
= ind
;
725 free_defines(define_start
);
730 /********************************************************************/
731 /* GCC inline asm support */
733 /* assemble the string 'str' in the current C compilation unit without
734 C preprocessing. NOTE: str is modified by modifying the '\0' at the
736 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
738 BufferedFile
*bf
, *saved_file
;
739 int saved_parse_flags
, *saved_macro_ptr
;
741 bf
= tcc_malloc(sizeof(BufferedFile
));
742 memset(bf
, 0, sizeof(BufferedFile
));
745 bf
->buf_end
= str
+ len
;
747 /* same name as current file so that errors are correctly
749 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
750 bf
->line_num
= file
->line_num
;
753 saved_parse_flags
= parse_flags
;
754 saved_macro_ptr
= macro_ptr
;
757 tcc_assemble_internal(s1
, 0);
759 parse_flags
= saved_parse_flags
;
760 macro_ptr
= saved_macro_ptr
;
765 /* find a constraint by its number or id (gcc 3 extended
766 syntax). return -1 if not found. Return in *pp in char after the
768 static int find_constraint(ASMOperand
*operands
, int nb_operands
,
769 const char *name
, const char **pp
)
777 while (isnum(*name
)) {
778 index
= (index
* 10) + (*name
) - '0';
781 if ((unsigned)index
>= nb_operands
)
783 } else if (*name
== '[') {
785 p
= strchr(name
, ']');
787 ts
= tok_alloc(name
, p
- name
);
788 for(index
= 0; index
< nb_operands
; index
++) {
789 if (operands
[index
].id
== ts
->tok
)
806 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
808 CString
*out_str
, CString
*in_str
)
810 int c
, index
, modifier
;
825 if (*str
== 'c' || *str
== 'n' ||
826 *str
== 'b' || *str
== 'w' || *str
== 'h')
828 index
= find_constraint(operands
, nb_operands
, str
, &str
);
830 error("invalid operand reference after %%");
831 op
= &operands
[index
];
835 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
838 subst_asm_operand(out_str
, &sv
, modifier
);
841 cstr_ccat(out_str
, c
);
849 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
856 nb_operands
= *nb_operands_ptr
;
858 if (nb_operands
>= MAX_ASM_OPERANDS
)
859 error("too many asm operands");
860 op
= &operands
[nb_operands
++];
865 expect("identifier");
871 expect("string constant");
872 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
873 strcpy(op
->constraint
, tokc
.cstr
->data
);
880 /* we want to avoid LLOCAL case, except when the 'm'
881 constraint is used. Note that it may come from
882 register storage, so we need to convert (reg)
884 if ((vtop
->r
& VT_LVAL
) &&
885 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
886 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
887 !strchr(op
->constraint
, 'm')) {
899 *nb_operands_ptr
= nb_operands
;
903 static void parse_asm_str(CString
*astr
)
906 /* read the string */
908 expect("string constant");
910 while (tok
== TOK_STR
) {
911 /* XXX: add \0 handling too ? */
912 cstr_cat(astr
, tokc
.cstr
->data
);
915 cstr_ccat(astr
, '\0');
918 /* parse the GCC asm() instruction */
919 static void asm_instr(void)
922 ASMOperand operands
[MAX_ASM_OPERANDS
];
923 int nb_inputs
, nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
924 uint8_t clobber_regs
[NB_ASM_REGS
];
927 /* since we always generate the asm() instruction, we can ignore
929 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
932 parse_asm_str(&astr
);
936 memset(clobber_regs
, 0, sizeof(clobber_regs
));
941 parse_asm_operands(operands
, &nb_operands
, 1);
942 nb_outputs
= nb_operands
;
947 parse_asm_operands(operands
, &nb_operands
, 0);
950 /* XXX: handle registers */
954 expect("string constant");
955 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
968 /* NOTE: we do not eat the ';' so that we can restore the current
969 token after the assembler parsing */
972 nb_inputs
= nb_operands
- nb_outputs
;
974 /* save all values in the memory */
977 /* compute constraints */
978 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
979 clobber_regs
, &out_reg
);
981 /* substitute the operands in the asm string. No substitution is
982 done if no operands (GCC behaviour) */
984 printf("asm: \"%s\"\n", (char *)astr
.data
);
987 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
993 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
997 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
998 clobber_regs
, out_reg
);
1000 /* assemble the string with tcc internal assembler */
1001 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1003 /* restore the current C token */
1006 /* store the output values if needed */
1007 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1008 clobber_regs
, out_reg
);
1010 /* free everything */
1011 for(i
=0;i
<nb_operands
;i
++) {
1014 tcc_free(op
->constraint
);
1020 static void asm_global_instr(void)
1025 parse_asm_str(&astr
);
1027 /* NOTE: we do not eat the ';' so that we can restore the current
1028 token after the assembler parsing */
1033 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1035 cur_text_section
= text_section
;
1036 ind
= cur_text_section
->data_offset
;
1038 /* assemble the string with tcc internal assembler */
1039 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1041 cur_text_section
->data_offset
= ind
;
1043 /* restore the current C token */