2 * GAS like assembler for TCC
4 * Copyright (c) 2001, 2002 Fabrice Bellard
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 /* We do not use the C expression parser to handle symbols. Maybe the
32 C expression parser could be tweaked to do so. */
34 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
43 n
= strtoul(p
, (char **)&p
, 0);
44 if (*p
== 'b' || *p
== 'f') {
45 /* backward or forward label */
46 label
= asm_get_local_label_name(s1
, n
);
47 sym
= label_find(label
);
49 /* backward : find the last corresponding defined label */
50 if (sym
&& sym
->r
== 0)
53 error("local label '%d' not found backward", n
);
57 /* if the last label is defined, then define a new one */
58 sym
= label_push(&s1
->asm_labels
, label
, 0);
59 sym
->type
.t
= VT_STATIC
| VT_VOID
;
64 } else if (*p
== '\0') {
68 error("invalid number syntax");
74 asm_expr_unary(s1
, pe
);
80 asm_expr_unary(s1
, pe
);
82 error("invalid operation with label");
95 if (tok
>= TOK_IDENT
) {
96 /* label case : if the label was not found, add one */
97 sym
= label_find(tok
);
99 sym
= label_push(&s1
->asm_labels
, tok
, 0);
100 /* NOTE: by default, the symbol is global */
101 sym
->type
.t
= VT_VOID
;
107 error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
113 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
118 asm_expr_unary(s1
, pe
);
121 if (op
!= '*' && op
!= '/' && op
!= '%' &&
122 op
!= TOK_SHL
&& op
!= TOK_SAR
)
125 asm_expr_unary(s1
, &e2
);
126 if (pe
->sym
|| e2
.sym
)
127 error("invalid operation with label");
135 error("division by zero");
155 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
160 asm_expr_prod(s1
, pe
);
163 if (op
!= '&' && op
!= '|' && op
!= '^')
166 asm_expr_prod(s1
, &e2
);
167 if (pe
->sym
|| e2
.sym
)
168 error("invalid operation with label");
184 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
189 asm_expr_logic(s1
, pe
);
192 if (op
!= '+' && op
!= '-')
195 asm_expr_logic(s1
, &e2
);
197 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
198 goto cannot_relocate
;
200 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
204 /* NOTE: we are less powerful than gas in that case
205 because we store only one symbol in the expression */
206 if (!pe
->sym
&& !e2
.sym
) {
208 } else if (pe
->sym
&& !e2
.sym
) {
210 } else if (pe
->sym
&& e2
.sym
) {
211 if (pe
->sym
== e2
.sym
) {
213 } else if (pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
214 /* we also accept defined symbols in the same section */
215 pe
->v
+= (long)pe
->sym
->next
- (long)e2
.sym
->next
;
217 goto cannot_relocate
;
219 pe
->sym
= NULL
; /* same symbols can be substracted to NULL */
222 error("invalid operation with label");
228 static void asm_expr(TCCState
*s1
, ExprValue
*pe
)
230 asm_expr_sum(s1
, pe
);
233 static int asm_int_expr(TCCState
*s1
)
242 /* NOTE: the same name space as C labels is used to avoid using too
243 much memory when storing labels in TokenStrings */
244 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
248 sym
= label_find(label
);
251 /* the label is already defined */
253 error("assembler label '%s' already defined",
254 get_tok_str(label
, NULL
));
256 /* redefinition of local labels is possible */
262 sym
= label_push(&s1
->asm_labels
, label
, 0);
263 sym
->type
.t
= VT_STATIC
| VT_VOID
;
265 sym
->r
= cur_text_section
->sh_num
;
266 sym
->next
= (void *)ind
;
269 static void asm_free_labels(TCCState
*st
)
272 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
274 /* define symbol value in object file */
276 put_extern_sym(s
, st
->sections
[s
->r
], (long)s
->next
, 0);
279 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
282 st
->asm_labels
= NULL
;
285 static void use_section(TCCState
*s1
, const char *name
)
288 sec
= find_section(s1
, name
);
289 cur_text_section
->data_offset
= ind
;
290 cur_text_section
= sec
;
291 ind
= cur_text_section
->data_offset
;
294 static void asm_parse_directive(TCCState
*s1
)
296 int n
, offset
, v
, size
, tok1
;
300 /* assembler directive */
302 sec
= cur_text_section
;
309 n
= asm_int_expr(s1
);
310 if (tok1
== TOK_ASM_align
) {
311 if (n
< 0 || (n
& (n
-1)) != 0)
312 error("alignment must be a positive power of two");
313 offset
= (ind
+ n
- 1) & -n
;
321 v
= asm_int_expr(s1
);
323 if (sec
->sh_type
!= SHT_NOBITS
) {
324 sec
->data_offset
= ind
;
325 ptr
= section_ptr_add(sec
, size
);
326 memset(ptr
, v
, size
);
345 if (sec
->sh_type
!= SHT_NOBITS
) {
369 sym
= label_find(tok
);
371 sym
= label_push(&s1
->asm_labels
, tok
, 0);
372 sym
->type
.t
= VT_VOID
;
374 sym
->type
.t
&= ~VT_STATIC
;
385 expect("string constant");
387 for(i
= 0; i
< tokc
.cstr
->size
; i
++)
400 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
401 n
= asm_int_expr(s1
);
404 sprintf(sname
, (n
?".%s%d":".%s"), get_tok_str(tok1
, NULL
), n
);
405 use_section(s1
, sname
);
412 /* XXX: support more options */
415 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
417 pstrcat(sname
, sizeof(sname
), tokc
.cstr
->data
);
419 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
422 use_section(s1
, sname
);
426 error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
432 /* assemble a file */
433 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
438 /* print stats about opcodes */
443 int nb_op_vals
, i
, j
;
446 memset(freq
, 0, sizeof(freq
));
447 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
449 for(i
=0;i
<pa
->nb_ops
;i
++) {
450 for(j
=0;j
<nb_op_vals
;j
++) {
451 if (pa
->op_type
[i
] == op_vals
[j
])
454 op_vals
[nb_op_vals
++] = pa
->op_type
[i
];
458 for(i
=0;i
<nb_op_vals
;i
++) {
460 if ((v
& (v
- 1)) != 0)
461 printf("%3d: %08x\n", i
, v
);
463 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
464 sizeof(asm_instrs
), sizeof(asm_instrs
) / sizeof(ASMInstr
),
465 freq
[0], freq
[1], freq
[2], freq
[3]);
469 /* XXX: undefine C labels */
471 ch
= file
->buf_ptr
[0];
472 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
475 parse_flags
|= PARSE_FLAG_PREPROCESS
;
480 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
483 /* horrible gas comment */
484 while (tok
!= TOK_LINEFEED
)
486 } else if (tok
== '.') {
487 asm_parse_directive(s1
);
488 } else if (tok
== TOK_PPNUM
) {
492 n
= strtoul(p
, (char **)&p
, 10);
495 /* new local label */
496 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
500 } else if (tok
>= TOK_IDENT
) {
501 /* instruction or label */
506 asm_new_label(s1
, opcode
, 0);
510 asm_opcode(s1
, opcode
);
514 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
515 expect("end of line");
517 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
526 /* Assemble the current file */
527 static int tcc_assemble(TCCState
*s1
, int do_preprocess
)
533 /* default section is text */
534 cur_text_section
= text_section
;
535 ind
= cur_text_section
->data_offset
;
537 ret
= tcc_assemble_internal(s1
, do_preprocess
);
539 cur_text_section
->data_offset
= ind
;
543 /********************************************************************/
544 /* GCC inline asm support */
546 /* assemble the string 'str' in the current C compilation unit without
547 C preprocessing. NOTE: str is modified by modifying the '\0' at the
549 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
551 BufferedFile
*bf
, *saved_file
;
552 int saved_parse_flags
, *saved_macro_ptr
;
554 bf
= tcc_malloc(sizeof(BufferedFile
));
555 memset(bf
, 0, sizeof(BufferedFile
));
558 bf
->buf_end
= str
+ len
;
560 /* same name as current file so that errors are correctly
562 pstrcpy(bf
->filename
, sizeof(bf
->filename
), file
->filename
);
563 bf
->line_num
= file
->line_num
;
566 saved_parse_flags
= parse_flags
;
567 saved_macro_ptr
= macro_ptr
;
570 tcc_assemble_internal(s1
, 0);
572 parse_flags
= saved_parse_flags
;
573 macro_ptr
= saved_macro_ptr
;
578 /* find a constraint by its number or id (gcc 3 extended
579 syntax). return -1 if not found. Return in *pp in char after the
581 static int find_constraint(ASMOperand
*operands
, int nb_operands
,
582 const char *name
, const char **pp
)
590 while (isnum(*name
)) {
591 index
= (index
* 10) + (*name
) - '0';
594 if ((unsigned)index
>= nb_operands
)
596 } else if (*name
== '[') {
598 p
= strchr(name
, ']');
600 ts
= tok_alloc(name
, p
- name
);
601 for(index
= 0; index
< nb_operands
; index
++) {
602 if (operands
[index
].id
== ts
->tok
)
619 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
621 CString
*out_str
, CString
*in_str
)
623 int c
, index
, modifier
;
638 if (*str
== 'c' || *str
== 'n' ||
639 *str
== 'b' || *str
== 'w' || *str
== 'h')
641 index
= find_constraint(operands
, nb_operands
, str
, &str
);
643 error("invalid operand reference after %%");
644 op
= &operands
[index
];
648 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
)
651 subst_asm_operand(out_str
, &sv
, modifier
);
654 cstr_ccat(out_str
, c
);
662 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
669 nb_operands
= *nb_operands_ptr
;
671 if (nb_operands
>= MAX_ASM_OPERANDS
)
672 error("too many asm operands");
673 op
= &operands
[nb_operands
++];
678 expect("identifier");
684 expect("string constant");
685 op
->constraint
= tcc_malloc(tokc
.cstr
->size
);
686 strcpy(op
->constraint
, tokc
.cstr
->data
);
693 /* we want to avoid LLOCAL case. note that it may come
694 from register storage, so we need to convert (reg)
696 if ((vtop
->r
& VT_LVAL
) &&
697 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
698 (vtop
->r
& VT_VALMASK
) < VT_CONST
)) {
710 *nb_operands_ptr
= nb_operands
;
714 /* parse the GCC asm() instruction */
715 static void asm_instr(void)
718 ASMOperand operands
[MAX_ASM_OPERANDS
];
719 int nb_inputs
, nb_outputs
, nb_operands
, i
;
720 uint8_t input_regs_allocated
[NB_ASM_REGS
];
721 uint8_t output_regs_allocated
[NB_ASM_REGS
];
722 uint8_t clobber_regs
[NB_ASM_REGS
];
725 /* since we always generate the asm() instruction, we can ignore
727 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
731 /* read the string */
733 expect("string constant");
735 while (tok
== TOK_STR
) {
736 /* XXX: add \0 handling too ? */
737 cstr_cat(&astr
, tokc
.cstr
->data
);
740 cstr_ccat(&astr
, '\0');
743 memset(clobber_regs
, 0, sizeof(clobber_regs
));
747 parse_asm_operands(operands
, &nb_operands
, 1);
748 nb_outputs
= nb_operands
;
752 parse_asm_operands(operands
, &nb_operands
, 0);
755 /* XXX: handle registers */
759 expect("string constant");
760 asm_clobber(clobber_regs
, tokc
.cstr
->data
);
772 /* NOTE: we do not eat the ';' so that we can restore the current
773 token after the assembler parsing */
776 nb_inputs
= nb_operands
- nb_outputs
;
778 /* save all values in the memory */
781 /* compute constraints */
782 asm_compute_constraints(input_regs_allocated
,
783 operands
, nb_operands
, nb_outputs
, 0,
785 asm_compute_constraints(output_regs_allocated
,
786 operands
, nb_operands
, nb_outputs
, 1,
787 input_regs_allocated
);
789 /* substitute the operands in the asm string. No substitution is
790 done if no operands (GCC behaviour) */
792 printf("asm: \"%s\"\n", (char *)astr
.data
);
794 if (nb_operands
> 0) {
795 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
801 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
805 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0, clobber_regs
);
807 /* assemble the string with tcc internal assembler */
808 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
810 /* restore the current C token */
813 /* store the output values if needed */
814 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1, clobber_regs
);
816 /* free everything */
817 for(i
=0;i
<nb_operands
;i
++) {
820 tcc_free(op
->constraint
);