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
;
479 expect("string constant");
481 size
= tokc
.cstr
->size
;
482 if (t
== TOK_ASM_ascii
&& size
> 0)
484 for(i
= 0; i
< size
; i
++)
489 } else if (tok
!= TOK_STR
) {
503 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
504 n
= asm_int_expr(s1
);
507 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
508 use_section(s1
, sname
);
515 /* XXX: support more options */
518 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
520 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
522 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
526 /* skip section options */
529 expect("string constant");
532 last_text_section
= cur_text_section
;
533 use_section(s1
, sname
);
536 case TOK_ASM_previous
:
540 if (!last_text_section
)
541 error("no previous section referenced");
542 sec
= cur_text_section
;
543 use_section1(s1
, last_text_section
);
544 last_text_section
= sec
;
548 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
554 /* assemble a file */
555 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
560 /* print stats about opcodes */
565 int nb_op_vals
, i
, j
;
568 memset(freq
, 0, sizeof(freq
));
569 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
571 for(i
=0;i
<pa
->nb_ops
;i
++) {
572 for(j
=0;j
<nb_op_vals
;j
++) {
573 if (pa
->op_type
[i
] == op_vals
[j
])
576 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
580 for(i
=0;i
<nb_op_vals
;i
++) {
582 if ((v
& (v
- 1)) != 0)
583 printf("%3d: %08x\n", i
, v
);
585 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
586 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
587 freq
[0], freq
[1], freq
[2], freq
[3]);
591 /* XXX: undefine C labels */
593 ch
= file
->buf_ptr
[0];
594 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
595 parse_flags
= PARSE_FLAG_ASM_COMMENTS
;
597 parse_flags
|= PARSE_FLAG_PREPROCESS
;
602 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
605 /* horrible gas comment */
606 while (tok
!= TOK_LINEFEED
)
608 } else if (tok
== '.') {
609 asm_parse_directive(s1
);
610 } else if (tok
== TOK_PPNUM
) {
614 n
= strtoul(p
, (char **)&p
, 10);
617 /* new local label */
618 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
622 } else if (tok
>= TOK_IDENT
) {
623 /* instruction or label */
628 asm_new_label(s1
, opcode
, 0);
631 } else if (tok
== '=') {
634 n
= asm_int_expr(s1
);
635 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
638 asm_opcode(s1
, opcode
);
642 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
643 expect("end of line");
645 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
654 /* Assemble the current file */
655 static int tcc_assemble(TCCState
*s1
, int do_preprocess
)
662 /* default section is text */
663 cur_text_section
= text_section
;
664 ind
= cur_text_section
->data_offset
;
666 define_start
= define_stack
;
668 ret
= tcc_assemble_internal(s1
, do_preprocess
);
670 cur_text_section
->data_offset
= ind
;
672 free_defines(define_start
);
677 /********************************************************************/
678 /* GCC inline asm support */
680 /* assemble the string 'str' in the current C compilation unit without
681 C preprocessing. NOTE: str is modified by modifying the '\0' at the
683 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
685 BufferedFile
*bf
, *saved_file
;
686 int saved_parse_flags
, *saved_macro_ptr
;
688 bf
= tcc_malloc(sizeof(BufferedFile
));
689 memset(bf
, 0, sizeof(BufferedFile
));
692 bf
->buf_end
= str
+ len
;
694 /* same name as current file so that errors are correctly
696 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
697 bf
->line_num
= file
->line_num
;
700 saved_parse_flags
= parse_flags
;
701 saved_macro_ptr
= macro_ptr
;
704 tcc_assemble_internal(s1
, 0);
706 parse_flags
= saved_parse_flags
;
707 macro_ptr
= saved_macro_ptr
;
712 /* find a constraint by its number or id (gcc 3 extended
713 syntax). return -1 if not found. Return in *pp in char after the
715 static int find_constraint(ASMOperand
*operands
, int nb_operands
,
716 const char *name
, const char **pp
)
724 while (isnum(*name
)) {
725 index
= (index
* 10) + (*name
) - '0';
728 if ((unsigned)index
>= nb_operands
)
730 } else if (*name
== '[') {
732 p
= strchr(name
, ']');
734 ts
= tok_alloc(name
, p
- name
);
735 for(index
= 0; index
< nb_operands
; index
++) {
736 if (operands
[index
].id
== ts
->tok
)
753 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
755 CString
*out_str
, CString
*in_str
)
757 int c
, index
, modifier
;
772 if (*str
== 'c' || *str
== 'n' ||
773 *str
== 'b' || *str
== 'w' || *str
== 'h')
775 index
= find_constraint(operands
, nb_operands
, str
, &str
);
777 error("invalid operand reference after %%");
778 op
= &operands
[index
];
782 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
)
785 subst_asm_operand(out_str
, &sv
, modifier
);
788 cstr_ccat(out_str
, c
);
796 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
803 nb_operands
= *nb_operands_ptr
;
805 if (nb_operands
>= MAX_ASM_OPERANDS
)
806 error("too many asm operands");
807 op
= &operands
[nb_operands
++];
812 expect("identifier");
818 expect("string constant");
819 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
820 strcpy(op
->constraint
, tokc
.cstr
->data
);
827 /* we want to avoid LLOCAL case, except when the 'm'
828 constraint is used. Note that it may come from
829 register storage, so we need to convert (reg)
831 if ((vtop
->r
& VT_LVAL
) &&
832 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
833 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
834 !strchr(op
->constraint
, 'm')) {
846 *nb_operands_ptr
= nb_operands
;
850 static void parse_asm_str(CString
*astr
)
853 /* read the string */
855 expect("string constant");
857 while (tok
== TOK_STR
) {
858 /* XXX: add \0 handling too ? */
859 cstr_cat(astr
, tokc
.cstr
->data
);
862 cstr_ccat(astr
, '\0');
865 /* parse the GCC asm() instruction */
866 static void asm_instr(void)
869 ASMOperand operands
[MAX_ASM_OPERANDS
];
870 int nb_inputs
, nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
871 uint8_t clobber_regs
[NB_ASM_REGS
];
874 /* since we always generate the asm() instruction, we can ignore
876 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
879 parse_asm_str(&astr
);
883 memset(clobber_regs
, 0, sizeof(clobber_regs
));
888 parse_asm_operands(operands
, &nb_operands
, 1);
889 nb_outputs
= nb_operands
;
893 parse_asm_operands(operands
, &nb_operands
, 0);
896 /* XXX: handle registers */
900 expect("string constant");
901 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
913 /* NOTE: we do not eat the ';' so that we can restore the current
914 token after the assembler parsing */
917 nb_inputs
= nb_operands
- nb_outputs
;
919 /* save all values in the memory */
922 /* compute constraints */
923 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
924 clobber_regs
, &out_reg
);
926 /* substitute the operands in the asm string. No substitution is
927 done if no operands (GCC behaviour) */
929 printf("asm: \"%s\"\n", (char *)astr
.data
);
932 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
938 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
942 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
943 clobber_regs
, out_reg
);
945 /* assemble the string with tcc internal assembler */
946 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
948 /* restore the current C token */
951 /* store the output values if needed */
952 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
953 clobber_regs
, out_reg
);
955 /* free everything */
956 for(i
=0;i
<nb_operands
;i
++) {
959 tcc_free(op
->constraint
);
965 static void asm_global_instr(void)
970 parse_asm_str(&astr
);
972 /* NOTE: we do not eat the ';' so that we can restore the current
973 token after the assembler parsing */
978 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
980 cur_text_section
= text_section
;
981 ind
= cur_text_section
->data_offset
;
983 /* assemble the string with tcc internal assembler */
984 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
986 cur_text_section
->data_offset
= ind
;
988 /* restore the current C token */