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
;
575 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
581 /* assemble a file */
582 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
587 /* print stats about opcodes */
592 int nb_op_vals
, i
, j
;
595 memset(freq
, 0, sizeof(freq
));
596 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
598 for(i
=0;i
<pa
->nb_ops
;i
++) {
599 for(j
=0;j
<nb_op_vals
;j
++) {
600 if (pa
->op_type
[i
] == op_vals
[j
])
603 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
607 for(i
=0;i
<nb_op_vals
;i
++) {
609 if ((v
& (v
- 1)) != 0)
610 printf("%3d: %08x\n", i
, v
);
612 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
613 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
614 freq
[0], freq
[1], freq
[2], freq
[3]);
618 /* XXX: undefine C labels */
620 ch
= file
->buf_ptr
[0];
621 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
622 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
624 parse_flags
|= PARSE_FLAG_PREPROCESS
;
629 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
632 /* horrible gas comment */
633 while (tok
!= TOK_LINEFEED
)
635 } else if (tok
== '.') {
636 asm_parse_directive(s1
);
637 } else if (tok
== TOK_PPNUM
) {
641 n
= strtoul(p
, (char **)&p
, 10);
644 /* new local label */
645 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
649 } else if (tok
>= TOK_IDENT
) {
650 /* instruction or label */
655 asm_new_label(s1
, opcode
, 0);
658 } else if (tok
== '=') {
661 n
= asm_int_expr(s1
);
662 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
665 asm_opcode(s1
, opcode
);
669 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
670 expect("end of line");
672 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
681 /* Assemble the current file */
682 static int tcc_assemble(TCCState
*s1
, int do_preprocess
)
689 /* default section is text */
690 cur_text_section
= text_section
;
691 ind
= cur_text_section
->data_offset
;
693 define_start
= define_stack
;
695 ret
= tcc_assemble_internal(s1
, do_preprocess
);
697 cur_text_section
->data_offset
= ind
;
699 free_defines(define_start
);
704 /********************************************************************/
705 /* GCC inline asm support */
707 /* assemble the string 'str' in the current C compilation unit without
708 C preprocessing. NOTE: str is modified by modifying the '\0' at the
710 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
712 BufferedFile
*bf
, *saved_file
;
713 int saved_parse_flags
, *saved_macro_ptr
;
715 bf
= tcc_malloc(sizeof(BufferedFile
));
716 memset(bf
, 0, sizeof(BufferedFile
));
719 bf
->buf_end
= str
+ len
;
721 /* same name as current file so that errors are correctly
723 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
724 bf
->line_num
= file
->line_num
;
727 saved_parse_flags
= parse_flags
;
728 saved_macro_ptr
= macro_ptr
;
731 tcc_assemble_internal(s1
, 0);
733 parse_flags
= saved_parse_flags
;
734 macro_ptr
= saved_macro_ptr
;
739 /* find a constraint by its number or id (gcc 3 extended
740 syntax). return -1 if not found. Return in *pp in char after the
742 static int find_constraint(ASMOperand
*operands
, int nb_operands
,
743 const char *name
, const char **pp
)
751 while (isnum(*name
)) {
752 index
= (index
* 10) + (*name
) - '0';
755 if ((unsigned)index
>= nb_operands
)
757 } else if (*name
== '[') {
759 p
= strchr(name
, ']');
761 ts
= tok_alloc(name
, p
- name
);
762 for(index
= 0; index
< nb_operands
; index
++) {
763 if (operands
[index
].id
== ts
->tok
)
780 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
782 CString
*out_str
, CString
*in_str
)
784 int c
, index
, modifier
;
799 if (*str
== 'c' || *str
== 'n' ||
800 *str
== 'b' || *str
== 'w' || *str
== 'h')
802 index
= find_constraint(operands
, nb_operands
, str
, &str
);
804 error("invalid operand reference after %%");
805 op
= &operands
[index
];
809 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
812 subst_asm_operand(out_str
, &sv
, modifier
);
815 cstr_ccat(out_str
, c
);
823 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
830 nb_operands
= *nb_operands_ptr
;
832 if (nb_operands
>= MAX_ASM_OPERANDS
)
833 error("too many asm operands");
834 op
= &operands
[nb_operands
++];
839 expect("identifier");
845 expect("string constant");
846 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
847 strcpy(op
->constraint
, tokc
.cstr
->data
);
854 /* we want to avoid LLOCAL case, except when the 'm'
855 constraint is used. Note that it may come from
856 register storage, so we need to convert (reg)
858 if ((vtop
->r
& VT_LVAL
) &&
859 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
860 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
861 !strchr(op
->constraint
, 'm')) {
873 *nb_operands_ptr
= nb_operands
;
877 static void parse_asm_str(CString
*astr
)
880 /* read the string */
882 expect("string constant");
884 while (tok
== TOK_STR
) {
885 /* XXX: add \0 handling too ? */
886 cstr_cat(astr
, tokc
.cstr
->data
);
889 cstr_ccat(astr
, '\0');
892 /* parse the GCC asm() instruction */
893 static void asm_instr(void)
896 ASMOperand operands
[MAX_ASM_OPERANDS
];
897 int nb_inputs
, nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
898 uint8_t clobber_regs
[NB_ASM_REGS
];
901 /* since we always generate the asm() instruction, we can ignore
903 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
906 parse_asm_str(&astr
);
910 memset(clobber_regs
, 0, sizeof(clobber_regs
));
915 parse_asm_operands(operands
, &nb_operands
, 1);
916 nb_outputs
= nb_operands
;
921 parse_asm_operands(operands
, &nb_operands
, 0);
924 /* XXX: handle registers */
928 expect("string constant");
929 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
942 /* NOTE: we do not eat the ';' so that we can restore the current
943 token after the assembler parsing */
946 nb_inputs
= nb_operands
- nb_outputs
;
948 /* save all values in the memory */
951 /* compute constraints */
952 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
953 clobber_regs
, &out_reg
);
955 /* substitute the operands in the asm string. No substitution is
956 done if no operands (GCC behaviour) */
958 printf("asm: \"%s\"\n", (char *)astr
.data
);
961 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
967 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
971 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
972 clobber_regs
, out_reg
);
974 /* assemble the string with tcc internal assembler */
975 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
977 /* restore the current C token */
980 /* store the output values if needed */
981 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
982 clobber_regs
, out_reg
);
984 /* free everything */
985 for(i
=0;i
<nb_operands
;i
++) {
988 tcc_free(op
->constraint
);
994 static void asm_global_instr(void)
999 parse_asm_str(&astr
);
1001 /* NOTE: we do not eat the ';' so that we can restore the current
1002 token after the assembler parsing */
1007 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1009 cur_text_section
= text_section
;
1010 ind
= cur_text_section
->data_offset
;
1012 /* assemble the string with tcc internal assembler */
1013 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1015 cur_text_section
->data_offset
= ind
;
1017 /* restore the current C token */