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
24 ST_FUNC
int asm_get_local_label_name(TCCState
*s1
, unsigned int n
)
29 snprintf(buf
, sizeof(buf
), "L..%u", n
);
30 ts
= tok_alloc(buf
, strlen(buf
));
34 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
);
35 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
);
38 /* Return a symbol we can use inside the assembler, having name NAME.
39 The assembler symbol table is different from the C symbol table
40 (and the Sym members are used differently). But we must be able
41 to look up file-global C symbols from inside the assembler, e.g.
42 for global asm blocks to be able to refer to defined C symbols.
44 This routine gives back either an existing asm-internal
45 symbol, or a new one. In the latter case the new asm-internal
46 symbol is initialized with info from the C symbol table.
48 If CSYM is non-null we take symbol info from it, otherwise
49 we look up NAME in the C symbol table and use that. */
50 ST_FUNC Sym
* get_asm_sym(int name
, Sym
*csym
)
52 Sym
*sym
= label_find(name
);
54 sym
= label_push(&tcc_state
->asm_labels
, name
, 0);
55 sym
->type
.t
= VT_VOID
| VT_STATIC
| VT_EXTERN
;
57 csym
= sym_find(name
);
58 /* We might be called for an asm block from inside a C routine
59 and so might have local decls on the identifier stack. Search
60 for the first global one. */
61 while (csym
&& csym
->sym_scope
)
62 csym
= csym
->prev_tok
;
64 /* Now, if we have a defined global symbol copy over
65 section and offset. */
67 ((csym
->r
& (VT_SYM
|VT_CONST
)) == (VT_SYM
|VT_CONST
)) &&
70 esym
= &((ElfW(Sym
) *)symtab_section
->data
)[csym
->c
];
72 sym
->r
= esym
->st_shndx
;
73 sym
->jnext
= esym
->st_value
;
74 /* XXX can't yet store st_size anywhere. */
75 sym
->type
.t
= VT_VOID
| (csym
->type
.t
& VT_STATIC
);
76 /* Mark that this asm symbol doesn't need to be fed back. */
83 /* We do not use the C expression parser to handle symbols. Maybe the
84 C expression parser could be tweaked to do so. */
86 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
96 n
= strtoull(p
, (char **)&p
, 0);
97 if (*p
== 'b' || *p
== 'f') {
98 /* backward or forward label */
99 label
= asm_get_local_label_name(s1
, n
);
100 sym
= label_find(label
);
102 /* backward : find the last corresponding defined label */
103 if (sym
&& sym
->r
== 0)
106 tcc_error("local label '%d' not found backward", n
);
109 if (!sym
|| sym
->r
) {
110 /* if the last label is defined, then define a new one */
111 sym
= label_push(&s1
->asm_labels
, label
, 0);
112 sym
->type
.t
= VT_STATIC
| VT_VOID
| VT_EXTERN
;
118 } else if (*p
== '\0') {
123 tcc_error("invalid number syntax");
129 asm_expr_unary(s1
, pe
);
135 asm_expr_unary(s1
, pe
);
137 tcc_error("invalid operation with label");
159 sym_dot
.type
.t
= VT_VOID
| VT_STATIC
;
160 sym_dot
.r
= cur_text_section
->sh_num
;
165 if (tok
>= TOK_IDENT
) {
166 /* label case : if the label was not found, add one */
167 sym
= get_asm_sym(tok
, NULL
);
168 if (sym
->r
== SHN_ABS
) {
169 /* if absolute symbol, no need to put a symbol value */
180 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
186 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
191 asm_expr_unary(s1
, pe
);
194 if (op
!= '*' && op
!= '/' && op
!= '%' &&
195 op
!= TOK_SHL
&& op
!= TOK_SAR
)
198 asm_expr_unary(s1
, &e2
);
199 if (pe
->sym
|| e2
.sym
)
200 tcc_error("invalid operation with label");
208 tcc_error("division by zero");
228 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
233 asm_expr_prod(s1
, pe
);
236 if (op
!= '&' && op
!= '|' && op
!= '^')
239 asm_expr_prod(s1
, &e2
);
240 if (pe
->sym
|| e2
.sym
)
241 tcc_error("invalid operation with label");
257 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
262 asm_expr_logic(s1
, pe
);
265 if (op
!= '+' && op
!= '-')
268 asm_expr_logic(s1
, &e2
);
270 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
271 goto cannot_relocate
;
273 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
277 /* NOTE: we are less powerful than gas in that case
278 because we store only one symbol in the expression */
281 } else if (pe
->sym
== e2
.sym
) {
283 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
284 } else if (pe
->sym
&& pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
285 /* we also accept defined symbols in the same section */
286 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
288 } else if (e2
.sym
->r
== cur_text_section
->sh_num
) {
289 /* When subtracting a defined symbol in current section
290 this actually makes the value PC-relative. */
291 pe
->v
-= e2
.sym
->jnext
- ind
- 4;
296 tcc_error("invalid operation with label");
302 static inline void asm_expr_cmp(TCCState
*s1
, ExprValue
*pe
)
307 asm_expr_sum(s1
, pe
);
310 if (op
!= TOK_EQ
&& op
!= TOK_NE
311 && (op
> TOK_GT
|| op
< TOK_ULE
))
314 asm_expr_sum(s1
, &e2
);
315 if (pe
->sym
|| e2
.sym
)
316 tcc_error("invalid operation with label");
319 pe
->v
= pe
->v
== e2
.v
;
322 pe
->v
= pe
->v
!= e2
.v
;
325 pe
->v
= (int64_t)pe
->v
< (int64_t)e2
.v
;
328 pe
->v
= (int64_t)pe
->v
>= (int64_t)e2
.v
;
331 pe
->v
= (int64_t)pe
->v
<= (int64_t)e2
.v
;
334 pe
->v
= (int64_t)pe
->v
> (int64_t)e2
.v
;
339 /* GAS compare results are -1/0 not 1/0. */
340 pe
->v
= -(int64_t)pe
->v
;
344 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
346 asm_expr_cmp(s1
, pe
);
349 ST_FUNC
int asm_int_expr(TCCState
*s1
)
358 /* NOTE: the same name space as C labels is used to avoid using too
359 much memory when storing labels in TokenStrings */
360 static Sym
* asm_new_label1(TCCState
*s1
, int label
, int is_local
,
361 int sh_num
, int value
)
365 sym
= label_find(label
);
367 /* A VT_EXTERN symbol, even if it has a section is considered
368 overridable. This is how we "define" .set targets. Real
369 definitions won't have VT_EXTERN set. */
370 if (sym
->r
&& !(sym
->type
.t
& VT_EXTERN
)) {
371 /* the label is already defined */
373 tcc_error("assembler label '%s' already defined",
374 get_tok_str(label
, NULL
));
376 /* redefinition of local labels is possible */
382 sym
= label_push(&s1
->asm_labels
, label
, 0);
383 /* If we need a symbol to hold a value, mark it as
384 tentative only (for .set). If this is for a real label
385 we'll remove VT_EXTERN. */
386 sym
->type
.t
= VT_STATIC
| VT_VOID
| VT_EXTERN
;
393 static Sym
* asm_new_label(TCCState
*s1
, int label
, int is_local
)
395 return asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
398 /* Set the value of LABEL to that of some expression (possibly
399 involving other symbols). LABEL can be overwritten later still. */
400 static Sym
* set_symbol(TCCState
*s1
, int label
)
409 return asm_new_label1(s1
, label
, 0, e
.sym
? e
.sym
->r
: SHN_ABS
, n
);
412 /* Patch ELF symbol associated with SYM based on the assemblers
414 static void patch_binding(Sym
*sym
)
419 esym
= &((ElfW(Sym
) *)symtab_section
->data
)[sym
->c
];
420 if (sym
->a
.visibility
)
421 esym
->st_other
= (esym
->st_other
& ~ELFW(ST_VISIBILITY
)(-1))
424 esym
->st_info
= ELFW(ST_INFO
)(sym
->a
.weak
? STB_WEAK
425 : (sym
->type
.t
& VT_STATIC
) ? STB_LOCAL
427 ELFW(ST_TYPE
)(esym
->st_info
));
430 static void asm_free_labels(TCCState
*st
)
435 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
437 /* define symbol value in object file */
438 s
->type
.t
&= ~VT_EXTERN
;
439 if (!s
->a
.dllimport
) {
444 sec
= st
->sections
[s
->r
];
445 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
446 } else /* undefined symbols are global */
447 s
->type
.t
&= ~VT_STATIC
;
451 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
454 st
->asm_labels
= NULL
;
457 static void use_section1(TCCState
*s1
, Section
*sec
)
459 cur_text_section
->data_offset
= ind
;
460 cur_text_section
= sec
;
461 ind
= cur_text_section
->data_offset
;
464 static void use_section(TCCState
*s1
, const char *name
)
467 sec
= find_section(s1
, name
);
468 use_section1(s1
, sec
);
471 static void push_section(TCCState
*s1
, const char *name
)
473 Section
*sec
= find_section(s1
, name
);
474 sec
->prev
= cur_text_section
;
475 use_section1(s1
, sec
);
478 static void pop_section(TCCState
*s1
)
480 Section
*prev
= cur_text_section
->prev
;
482 tcc_error(".popsection without .pushsection");
483 cur_text_section
->prev
= NULL
;
484 use_section1(s1
, prev
);
487 static void asm_parse_directive(TCCState
*s1
, int global
)
489 int n
, offset
, v
, size
, tok1
;
493 /* assembler directive */
494 sec
= cur_text_section
;
496 case TOK_ASMDIR_align
:
497 case TOK_ASMDIR_balign
:
498 case TOK_ASMDIR_p2align
:
499 case TOK_ASMDIR_skip
:
500 case TOK_ASMDIR_space
:
503 n
= asm_int_expr(s1
);
504 if (tok1
== TOK_ASMDIR_p2align
)
507 tcc_error("invalid p2align, must be between 0 and 30");
509 tok1
= TOK_ASMDIR_align
;
511 if (tok1
== TOK_ASMDIR_align
|| tok1
== TOK_ASMDIR_balign
) {
512 if (n
< 0 || (n
& (n
-1)) != 0)
513 tcc_error("alignment must be a positive power of two");
514 offset
= (ind
+ n
- 1) & -n
;
516 /* the section must have a compatible alignment */
517 if (sec
->sh_addralign
< n
)
518 sec
->sh_addralign
= n
;
527 v
= asm_int_expr(s1
);
530 if (sec
->sh_type
!= SHT_NOBITS
) {
531 sec
->data_offset
= ind
;
532 ptr
= section_ptr_add(sec
, size
);
533 memset(ptr
, v
, size
);
537 case TOK_ASMDIR_quad
:
538 #ifdef TCC_TARGET_X86_64
548 if (tok
!= TOK_PPNUM
) {
550 tcc_error("64 bit constant");
552 vl
= strtoll(p
, (char **)&p
, 0);
556 if (sec
->sh_type
!= SHT_NOBITS
) {
557 /* XXX: endianness */
569 case TOK_ASMDIR_byte
:
572 case TOK_ASMDIR_word
:
573 case TOK_ASMDIR_short
:
576 case TOK_ASMDIR_long
:
584 if (sec
->sh_type
!= SHT_NOBITS
) {
587 #ifdef TCC_TARGET_X86_64
588 } else if (size
== 8) {
607 case TOK_ASMDIR_fill
:
609 int repeat
, size
, val
, i
, j
;
610 uint8_t repeat_buf
[8];
612 repeat
= asm_int_expr(s1
);
614 tcc_error("repeat < 0; .fill ignored");
621 size
= asm_int_expr(s1
);
623 tcc_error("size < 0; .fill ignored");
630 val
= asm_int_expr(s1
);
633 /* XXX: endianness */
635 repeat_buf
[1] = val
>> 8;
636 repeat_buf
[2] = val
>> 16;
637 repeat_buf
[3] = val
>> 24;
642 for(i
= 0; i
< repeat
; i
++) {
643 for(j
= 0; j
< size
; j
++) {
649 case TOK_ASMDIR_rept
:
652 TokenString
*init_str
;
654 repeat
= asm_int_expr(s1
);
655 init_str
= tok_str_alloc();
656 while (next(), tok
!= TOK_ASMDIR_endr
) {
658 tcc_error("we at end of file, .endr not found");
659 tok_str_add_tok(init_str
);
661 tok_str_add(init_str
, -1);
662 tok_str_add(init_str
, 0);
663 begin_macro(init_str
, 1);
664 while (repeat
-- > 0) {
665 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
),
667 macro_ptr
= init_str
->str
;
681 if (e
.sym
->r
!= cur_text_section
->sh_num
)
682 expect("constant or same-section symbol");
686 tcc_error("attempt to .org backwards");
696 /* Also accept '.set stuff', but don't do anything with this.
697 It's used in GAS to set various features like '.set mips16'. */
699 set_symbol(s1
, tok1
);
701 case TOK_ASMDIR_globl
:
702 case TOK_ASMDIR_global
:
703 case TOK_ASMDIR_weak
:
704 case TOK_ASMDIR_hidden
:
710 sym
= get_asm_sym(tok
, NULL
);
711 if (tok1
!= TOK_ASMDIR_hidden
)
712 sym
->type
.t
&= ~VT_STATIC
;
713 if (tok1
== TOK_ASMDIR_weak
)
715 else if (tok1
== TOK_ASMDIR_hidden
)
716 sym
->a
.visibility
= STV_HIDDEN
;
718 } while (tok
== ',');
720 case TOK_ASMDIR_string
:
721 case TOK_ASMDIR_ascii
:
722 case TOK_ASMDIR_asciz
:
731 expect("string constant");
733 size
= tokc
.str
.size
;
734 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
736 for(i
= 0; i
< size
; i
++)
741 } else if (tok
!= TOK_STR
) {
747 case TOK_ASMDIR_text
:
748 case TOK_ASMDIR_data
:
755 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
756 n
= asm_int_expr(s1
);
760 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
762 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
763 use_section(s1
, sname
);
766 case TOK_ASMDIR_file
:
774 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
776 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
778 if (s1
->warn_unsupported
)
779 tcc_warning("ignoring .file %s", filename
);
784 case TOK_ASMDIR_ident
:
792 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
794 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
796 if (s1
->warn_unsupported
)
797 tcc_warning("ignoring .ident %s", ident
);
802 case TOK_ASMDIR_size
:
807 sym
= label_find(tok
);
809 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
812 /* XXX .size name,label2-label1 */
813 if (s1
->warn_unsupported
)
814 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
818 while (tok
!= TOK_LINEFEED
&& tok
!= ';' && tok
!= CH_EOF
) {
823 case TOK_ASMDIR_type
:
829 sym
= get_asm_sym(tok
, NULL
);
832 if (tok
== TOK_STR
) {
833 newtype
= tokc
.str
.data
;
835 if (tok
== '@' || tok
== '%')
837 newtype
= get_tok_str(tok
, NULL
);
840 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
841 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
843 else if (s1
->warn_unsupported
)
844 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
845 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
850 case TOK_ASMDIR_pushsection
:
851 case TOK_ASMDIR_section
:
854 int old_nb_section
= s1
->nb_sections
;
857 /* XXX: support more options */
860 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
862 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
864 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
868 /* skip section options */
871 expect("string constant");
875 if (tok
== '@' || tok
== '%')
880 last_text_section
= cur_text_section
;
881 if (tok1
== TOK_ASMDIR_section
)
882 use_section(s1
, sname
);
884 push_section(s1
, sname
);
885 /* If we just allocated a new section reset its alignment to
886 1. new_section normally acts for GCC compatibility and
887 sets alignment to PTR_SIZE. The assembler behaves different. */
888 if (old_nb_section
!= s1
->nb_sections
)
889 cur_text_section
->sh_addralign
= 1;
892 case TOK_ASMDIR_previous
:
896 if (!last_text_section
)
897 tcc_error("no previous section referenced");
898 sec
= cur_text_section
;
899 use_section1(s1
, last_text_section
);
900 last_text_section
= sec
;
903 case TOK_ASMDIR_popsection
:
907 #ifdef TCC_TARGET_I386
908 case TOK_ASMDIR_code16
:
914 case TOK_ASMDIR_code32
:
921 #ifdef TCC_TARGET_X86_64
922 /* added for compatibility with GAS */
923 case TOK_ASMDIR_code64
:
928 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
934 /* assemble a file */
935 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
)
938 int saved_parse_flags
= parse_flags
;
940 /* XXX: undefine C labels */
941 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
943 parse_flags
|= PARSE_FLAG_PREPROCESS
;
948 /* generate line number info */
949 if (global
&& s1
->do_debug
)
951 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
954 /* horrible gas comment */
955 while (tok
!= TOK_LINEFEED
)
957 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
958 asm_parse_directive(s1
, global
);
959 } else if (tok
== TOK_PPNUM
) {
964 n
= strtoul(p
, (char **)&p
, 10);
967 /* new local label */
968 sym
= asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
969 /* Remove the marker for tentative definitions. */
970 sym
->type
.t
&= ~VT_EXTERN
;
974 } else if (tok
>= TOK_IDENT
) {
975 /* instruction or label */
979 /* handle "extern void vide(void); __asm__("vide: ret");" as
980 "__asm__("globl vide\nvide: ret");" */
981 Sym
*sym
= sym_find(opcode
);
982 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && global
) {
983 sym
= label_find(opcode
);
985 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
986 sym
->type
.t
= VT_VOID
| VT_EXTERN
;
990 sym
= asm_new_label(s1
, opcode
, 0);
991 sym
->type
.t
&= ~VT_EXTERN
;
994 } else if (tok
== '=') {
995 set_symbol(s1
, opcode
);
998 asm_opcode(s1
, opcode
);
1002 if (tok
!= ';' && tok
!= TOK_LINEFEED
)
1003 expect("end of line");
1004 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
1007 asm_free_labels(s1
);
1008 parse_flags
= saved_parse_flags
;
1012 /* Assemble the current file */
1013 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
1016 tcc_debug_start(s1
);
1017 /* default section is text */
1018 cur_text_section
= text_section
;
1019 ind
= cur_text_section
->data_offset
;
1021 ret
= tcc_assemble_internal(s1
, do_preprocess
, 1);
1022 cur_text_section
->data_offset
= ind
;
1027 /********************************************************************/
1028 /* GCC inline asm support */
1030 /* assemble the string 'str' in the current C compilation unit without
1031 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1033 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
, int global
)
1035 const int *saved_macro_ptr
= macro_ptr
;
1036 int dotid
= set_idnum('.', IS_ID
);
1038 tcc_open_bf(s1
, ":asm:", len
);
1039 memcpy(file
->buffer
, str
, len
);
1041 tcc_assemble_internal(s1
, 0, global
);
1044 set_idnum('.', dotid
);
1045 macro_ptr
= saved_macro_ptr
;
1048 /* find a constraint by its number or id (gcc 3 extended
1049 syntax). return -1 if not found. Return in *pp in char after the
1051 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1052 const char *name
, const char **pp
)
1060 while (isnum(*name
)) {
1061 index
= (index
* 10) + (*name
) - '0';
1064 if ((unsigned)index
>= nb_operands
)
1066 } else if (*name
== '[') {
1068 p
= strchr(name
, ']');
1070 ts
= tok_alloc(name
, p
- name
);
1071 for(index
= 0; index
< nb_operands
; index
++) {
1072 if (operands
[index
].id
== ts
->tok
)
1089 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1090 CString
*out_str
, CString
*in_str
)
1092 int c
, index
, modifier
;
1107 if (*str
== 'c' || *str
== 'n' ||
1108 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1110 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1111 and make literal operands not be decorated with '$'. */
1114 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1116 tcc_error("invalid operand reference after %%");
1117 op
= &operands
[index
];
1121 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1124 subst_asm_operand(out_str
, &sv
, modifier
);
1127 cstr_ccat(out_str
, c
);
1135 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1142 nb_operands
= *nb_operands_ptr
;
1145 if (nb_operands
>= MAX_ASM_OPERANDS
)
1146 tcc_error("too many asm operands");
1147 op
= &operands
[nb_operands
++];
1151 if (tok
< TOK_IDENT
)
1152 expect("identifier");
1157 parse_mult_str(&astr
, "string constant");
1158 op
->constraint
= tcc_malloc(astr
.size
);
1159 strcpy(op
->constraint
, astr
.data
);
1164 if (!(vtop
->type
.t
& VT_ARRAY
))
1167 /* we want to avoid LLOCAL case, except when the 'm'
1168 constraint is used. Note that it may come from
1169 register storage, so we need to convert (reg)
1171 if ((vtop
->r
& VT_LVAL
) &&
1172 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1173 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1174 !strchr(op
->constraint
, 'm')) {
1186 *nb_operands_ptr
= nb_operands
;
1190 /* parse the GCC asm() instruction */
1191 ST_FUNC
void asm_instr(void)
1193 CString astr
, astr1
;
1194 ASMOperand operands
[MAX_ASM_OPERANDS
];
1195 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1196 uint8_t clobber_regs
[NB_ASM_REGS
];
1199 /* since we always generate the asm() instruction, we can ignore
1201 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1204 parse_asm_str(&astr
);
1208 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1213 parse_asm_operands(operands
, &nb_operands
, 1);
1214 nb_outputs
= nb_operands
;
1219 parse_asm_operands(operands
, &nb_operands
, 0);
1222 /* XXX: handle registers */
1226 expect("string constant");
1227 asm_clobber(clobber_regs
, tokc
.str
.data
);
1240 /* NOTE: we do not eat the ';' so that we can restore the current
1241 token after the assembler parsing */
1245 /* save all values in the memory */
1248 /* compute constraints */
1249 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1250 clobber_regs
, &out_reg
);
1252 /* substitute the operands in the asm string. No substitution is
1253 done if no operands (GCC behaviour) */
1255 printf("asm: \"%s\"\n", (char *)astr
.data
);
1258 subst_asm_operands(operands
, nb_operands
, &astr1
, &astr
);
1264 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1267 /* generate loads */
1268 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1269 clobber_regs
, out_reg
);
1271 /* assemble the string with tcc internal assembler */
1272 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1, 0);
1274 /* restore the current C token */
1277 /* store the output values if needed */
1278 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1279 clobber_regs
, out_reg
);
1281 /* free everything */
1282 for(i
=0;i
<nb_operands
;i
++) {
1285 tcc_free(op
->constraint
);
1291 ST_FUNC
void asm_global_instr(void)
1294 int saved_nocode_wanted
= nocode_wanted
;
1296 /* Global asm blocks are always emitted. */
1299 parse_asm_str(&astr
);
1301 /* NOTE: we do not eat the ';' so that we can restore the current
1302 token after the assembler parsing */
1307 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1309 cur_text_section
= text_section
;
1310 ind
= cur_text_section
->data_offset
;
1312 /* assemble the string with tcc internal assembler */
1313 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1, 1);
1315 cur_text_section
->data_offset
= ind
;
1317 /* restore the current C token */
1321 nocode_wanted
= saved_nocode_wanted
;
1323 #endif /* CONFIG_TCC_ASM */