2 * GAS like assembler for TCC
4 * Copyright (c) 2001, 2002 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 */
112 pe
->v
= (long)sym
->next
;
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
+= (long)pe
->sym
->next
- (long)e2
.sym
->next
;
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
;
280 sym
->next
= (void *)value
;
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_sym(s
, sec
, (long)s
->next
, 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
);
379 if (sec
->sh_type
!= SHT_NOBITS
) {
400 int repeat
, size
, val
, i
, j
;
401 uint8_t repeat_buf
[8];
403 repeat
= asm_int_expr(s1
);
405 error("repeat < 0; .fill ignored");
412 size
= asm_int_expr(s1
);
414 error("size < 0; .fill ignored");
421 val
= asm_int_expr(s1
);
424 /* XXX: endianness */
426 repeat_buf
[1] = val
>> 8;
427 repeat_buf
[2] = val
>> 16;
428 repeat_buf
[3] = val
>> 24;
433 for(i
= 0; i
< repeat
; i
++) {
434 for(j
= 0; j
< size
; j
++) {
444 /* XXX: handle section symbols too */
445 n
= asm_int_expr(s1
);
447 error("attempt to .org backwards");
459 sym
= label_find(tok
);
461 sym
= label_push(&s1
->asm_labels
, tok
, 0);
462 sym
->type
.t
= VT_VOID
;
464 sym
->type
.t
&= ~VT_STATIC
;
475 expect("string constant");
477 for(i
= 0; i
< tokc
.cstr
->size
; i
++)
490 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
491 n
= asm_int_expr(s1
);
494 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
495 use_section(s1
, sname
);
502 /* XXX: support more options */
505 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
507 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
509 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
513 /* skip section options */
516 expect("string constant");
519 last_text_section
= cur_text_section
;
520 use_section(s1
, sname
);
523 case TOK_ASM_previous
:
527 if (!last_text_section
)
528 error("no previous section referenced");
529 sec
= cur_text_section
;
530 use_section1(s1
, last_text_section
);
531 last_text_section
= sec
;
535 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
541 /* assemble a file */
542 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
547 /* print stats about opcodes */
552 int nb_op_vals
, i
, j
;
555 memset(freq
, 0, sizeof(freq
));
556 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
558 for(i
=0;i
<pa
->nb_ops
;i
++) {
559 for(j
=0;j
<nb_op_vals
;j
++) {
560 if (pa
->op_type
[i
] == op_vals
[j
])
563 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
567 for(i
=0;i
<nb_op_vals
;i
++) {
569 if ((v
& (v
- 1)) != 0)
570 printf("%3d: %08x\n", i
, v
);
572 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
573 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
574 freq
[0], freq
[1], freq
[2], freq
[3]);
578 /* XXX: undefine C labels */
580 ch
= file
->buf_ptr
[0];
581 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
582 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
584 parse_flags
|= PARSE_FLAG_PREPROCESS
;
589 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
592 /* horrible gas comment */
593 while (tok
!= TOK_LINEFEED
)
595 } else if (tok
== '.') {
596 asm_parse_directive(s1
);
597 } else if (tok
== TOK_PPNUM
) {
601 n
= strtoul(p
, (char **)&p
, 10);
604 /* new local label */
605 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
609 } else if (tok
>= TOK_IDENT
) {
610 /* instruction or label */
615 asm_new_label(s1
, opcode
, 0);
618 } else if (tok
== '=') {
621 n
= asm_int_expr(s1
);
622 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
625 asm_opcode(s1
, opcode
);
629 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
630 expect("end of line");
632 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
641 /* Assemble the current file */
642 static int tcc_assemble(TCCState
*s1
, int do_preprocess
)
649 /* default section is text */
650 cur_text_section
= text_section
;
651 ind
= cur_text_section
->data_offset
;
653 define_start
= define_stack
;
655 ret
= tcc_assemble_internal(s1
, do_preprocess
);
657 cur_text_section
->data_offset
= ind
;
659 free_defines(define_start
);
664 /********************************************************************/
665 /* GCC inline asm support */
667 /* assemble the string 'str' in the current C compilation unit without
668 C preprocessing. NOTE: str is modified by modifying the '\0' at the
670 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
672 BufferedFile
*bf
, *saved_file
;
673 int saved_parse_flags
, *saved_macro_ptr
;
675 bf
= tcc_malloc(sizeof(BufferedFile
));
676 memset(bf
, 0, sizeof(BufferedFile
));
679 bf
->buf_end
= str
+ len
;
681 /* same name as current file so that errors are correctly
683 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
684 bf
->line_num
= file
->line_num
;
687 saved_parse_flags
= parse_flags
;
688 saved_macro_ptr
= macro_ptr
;
691 tcc_assemble_internal(s1
, 0);
693 parse_flags
= saved_parse_flags
;
694 macro_ptr
= saved_macro_ptr
;
699 /* find a constraint by its number or id (gcc 3 extended
700 syntax). return -1 if not found. Return in *pp in char after the
702 static int find_constraint(ASMOperand
*operands
, int nb_operands
,
703 const char *name
, const char **pp
)
711 while (isnum(*name
)) {
712 index
= (index
* 10) + (*name
) - '0';
715 if ((unsigned)index
>= nb_operands
)
717 } else if (*name
== '[') {
719 p
= strchr(name
, ']');
721 ts
= tok_alloc(name
, p
- name
);
722 for(index
= 0; index
< nb_operands
; index
++) {
723 if (operands
[index
].id
== ts
->tok
)
740 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
742 CString
*out_str
, CString
*in_str
)
744 int c
, index
, modifier
;
759 if (*str
== 'c' || *str
== 'n' ||
760 *str
== 'b' || *str
== 'w' || *str
== 'h')
762 index
= find_constraint(operands
, nb_operands
, str
, &str
);
764 error("invalid operand reference after %%");
765 op
= &operands
[index
];
769 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
)
772 subst_asm_operand(out_str
, &sv
, modifier
);
775 cstr_ccat(out_str
, c
);
783 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
790 nb_operands
= *nb_operands_ptr
;
792 if (nb_operands
>= MAX_ASM_OPERANDS
)
793 error("too many asm operands");
794 op
= &operands
[nb_operands
++];
799 expect("identifier");
805 expect("string constant");
806 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
807 strcpy(op
->constraint
, tokc
.cstr
->data
);
814 /* we want to avoid LLOCAL case, except when the 'm'
815 constraint is used. Note that it may come from
816 register storage, so we need to convert (reg)
818 if ((vtop
->r
& VT_LVAL
) &&
819 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
820 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
821 !strchr(op
->constraint
, 'm')) {
833 *nb_operands_ptr
= nb_operands
;
837 static void parse_asm_str(CString
*astr
)
840 /* read the string */
842 expect("string constant");
844 while (tok
== TOK_STR
) {
845 /* XXX: add \0 handling too ? */
846 cstr_cat(astr
, tokc
.cstr
->data
);
849 cstr_ccat(astr
, '\0');
852 /* parse the GCC asm() instruction */
853 static void asm_instr(void)
856 ASMOperand operands
[MAX_ASM_OPERANDS
];
857 int nb_inputs
, nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
858 uint8_t clobber_regs
[NB_ASM_REGS
];
861 /* since we always generate the asm() instruction, we can ignore
863 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
866 parse_asm_str(&astr
);
870 memset(clobber_regs
, 0, sizeof(clobber_regs
));
875 parse_asm_operands(operands
, &nb_operands
, 1);
876 nb_outputs
= nb_operands
;
880 parse_asm_operands(operands
, &nb_operands
, 0);
883 /* XXX: handle registers */
887 expect("string constant");
888 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
900 /* NOTE: we do not eat the ';' so that we can restore the current
901 token after the assembler parsing */
904 nb_inputs
= nb_operands
- nb_outputs
;
906 /* save all values in the memory */
909 /* compute constraints */
910 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
911 clobber_regs
, &out_reg
);
913 /* substitute the operands in the asm string. No substitution is
914 done if no operands (GCC behaviour) */
916 printf("asm: \"%s\"\n", (char *)astr
.data
);
919 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
925 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
929 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
930 clobber_regs
, out_reg
);
932 /* assemble the string with tcc internal assembler */
933 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
935 /* restore the current C token */
938 /* store the output values if needed */
939 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
940 clobber_regs
, out_reg
);
942 /* free everything */
943 for(i
=0;i
<nb_operands
;i
++) {
946 tcc_free(op
->constraint
);
952 static void asm_global_instr(void)
957 parse_asm_str(&astr
);
959 /* NOTE: we do not eat the ';' so that we can restore the current
960 token after the assembler parsing */
965 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
967 cur_text_section
= text_section
;
968 ind
= cur_text_section
->data_offset
;
970 /* assemble the string with tcc internal assembler */
971 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
973 cur_text_section
->data_offset
= ind
;
975 /* restore the current C token */