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 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
);
37 static Sym
*asm_label_find(int v
)
39 Sym
*sym
= sym_find(v
);
40 while (sym
&& sym
->sym_scope
)
45 static Sym
*asm_label_push(int v
, int t
)
47 Sym
*sym
= global_identifier_push_1(&tcc_state
->asm_labels
, v
, t
, 0);
48 /* We always add VT_EXTERN, for sym definition that's tentative
49 (for .set, removed for real defs), for mere references it's correct
51 sym
->type
.t
|= VT_VOID
| VT_EXTERN
;
52 sym
->r
= VT_CONST
| VT_SYM
;
56 /* Return a symbol we can use inside the assembler, having name NAME.
57 Symbols from asm and C source share a namespace. If we generate
58 an asm symbol it's also a (file-global) C symbol, but it's
59 either not accessible by name (like "L.123"), or its type information
60 is such that it's not usable without a proper C declaration.
62 Sometimes we need symbols accessible by name from asm, which
63 are anonymous in C, in this case CSYM can be used to transfer
64 all information from that symbol to the (possibly newly created)
66 ST_FUNC Sym
* get_asm_sym(int name
, Sym
*csym
)
68 Sym
*sym
= asm_label_find(name
);
70 sym
= asm_label_push(name
, 0);
77 /* We do not use the C expression parser to handle symbols. Maybe the
78 C expression parser could be tweaked to do so. */
80 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
90 n
= strtoull(p
, (char **)&p
, 0);
91 if (*p
== 'b' || *p
== 'f') {
92 /* backward or forward label */
93 label
= asm_get_local_label_name(s1
, n
);
94 sym
= asm_label_find(label
);
96 /* backward : find the last corresponding defined label */
97 if (sym
&& (!sym
->c
|| elfsym(sym
)->st_shndx
== SHN_UNDEF
))
100 tcc_error("local label '%d' not found backward", n
);
103 if (!sym
|| (sym
->c
&& elfsym(sym
)->st_shndx
!= SHN_UNDEF
)) {
104 /* if the last label is defined, then define a new one */
105 sym
= asm_label_push(label
, VT_STATIC
);
111 } else if (*p
== '\0') {
116 tcc_error("invalid number syntax");
122 asm_expr_unary(s1
, pe
);
128 asm_expr_unary(s1
, pe
);
130 tcc_error("invalid operation with label");
152 sym_dot
.type
.t
= VT_VOID
| VT_STATIC
;
154 tcc_state
->esym_dot
.st_shndx
= cur_text_section
->sh_num
;
155 tcc_state
->esym_dot
.st_value
= ind
;
159 if (tok
>= TOK_IDENT
) {
161 /* label case : if the label was not found, add one */
162 sym
= get_asm_sym(tok
, NULL
);
164 if (esym
&& esym
->st_shndx
== SHN_ABS
) {
165 /* if absolute symbol, no need to put a symbol value */
166 pe
->v
= esym
->st_value
;
176 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
182 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
187 asm_expr_unary(s1
, pe
);
190 if (op
!= '*' && op
!= '/' && op
!= '%' &&
191 op
!= TOK_SHL
&& op
!= TOK_SAR
)
194 asm_expr_unary(s1
, &e2
);
195 if (pe
->sym
|| e2
.sym
)
196 tcc_error("invalid operation with label");
204 tcc_error("division by zero");
224 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
229 asm_expr_prod(s1
, pe
);
232 if (op
!= '&' && op
!= '|' && op
!= '^')
235 asm_expr_prod(s1
, &e2
);
236 if (pe
->sym
|| e2
.sym
)
237 tcc_error("invalid operation with label");
253 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
258 asm_expr_logic(s1
, pe
);
261 if (op
!= '+' && op
!= '-')
264 asm_expr_logic(s1
, &e2
);
266 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
267 goto cannot_relocate
;
269 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
273 /* NOTE: we are less powerful than gas in that case
274 because we store only one symbol in the expression */
277 } else if (pe
->sym
== e2
.sym
) {
279 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
281 ElfSym
*esym1
, *esym2
;
282 esym1
= elfsym(pe
->sym
);
283 esym2
= elfsym(e2
.sym
);
284 if (esym1
&& esym1
->st_shndx
== esym2
->st_shndx
285 && esym1
->st_shndx
!= SHN_UNDEF
) {
286 /* we also accept defined symbols in the same section */
287 pe
->v
+= esym1
->st_value
- esym2
->st_value
;
289 } else if (esym2
->st_shndx
== cur_text_section
->sh_num
) {
290 /* When subtracting a defined symbol in current section
291 this actually makes the value PC-relative. */
292 pe
->v
-= esym2
->st_value
- ind
- 4;
297 tcc_error("invalid operation with label");
304 static inline void asm_expr_cmp(TCCState
*s1
, ExprValue
*pe
)
309 asm_expr_sum(s1
, pe
);
312 if (op
!= TOK_EQ
&& op
!= TOK_NE
313 && (op
> TOK_GT
|| op
< TOK_ULE
))
316 asm_expr_sum(s1
, &e2
);
317 if (pe
->sym
|| e2
.sym
)
318 tcc_error("invalid operation with label");
321 pe
->v
= pe
->v
== e2
.v
;
324 pe
->v
= pe
->v
!= e2
.v
;
327 pe
->v
= (int64_t)pe
->v
< (int64_t)e2
.v
;
330 pe
->v
= (int64_t)pe
->v
>= (int64_t)e2
.v
;
333 pe
->v
= (int64_t)pe
->v
<= (int64_t)e2
.v
;
336 pe
->v
= (int64_t)pe
->v
> (int64_t)e2
.v
;
341 /* GAS compare results are -1/0 not 1/0. */
342 pe
->v
= -(int64_t)pe
->v
;
346 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
348 asm_expr_cmp(s1
, pe
);
351 ST_FUNC
int asm_int_expr(TCCState
*s1
)
360 static Sym
* asm_new_label1(TCCState
*s1
, int label
, int is_local
,
361 int sh_num
, int value
)
366 sym
= asm_label_find(label
);
369 /* A VT_EXTERN symbol, even if it has a section is considered
370 overridable. This is how we "define" .set targets. Real
371 definitions won't have VT_EXTERN set. */
372 if (esym
&& esym
->st_shndx
!= SHN_UNDEF
&& !(sym
->type
.t
& VT_EXTERN
)) {
373 /* the label is already defined */
375 tcc_error("assembler label '%s' already defined",
376 get_tok_str(label
, NULL
));
378 /* redefinition of local labels is possible */
384 sym
= asm_label_push(label
, is_local
? VT_STATIC
: 0);
387 put_extern_sym2(sym
, NULL
, 0, 0, 0);
389 esym
->st_shndx
= sh_num
;
390 esym
->st_value
= value
;
394 static Sym
* asm_new_label(TCCState
*s1
, int label
, int is_local
)
396 return asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
399 /* Set the value of LABEL to that of some expression (possibly
400 involving other symbols). LABEL can be overwritten later still. */
401 static Sym
* set_symbol(TCCState
*s1
, int label
)
410 esym
= elfsym(e
.sym
);
413 sym
= asm_new_label1(s1
, label
, 0, esym
? esym
->st_shndx
: SHN_ABS
, n
);
414 elfsym(sym
)->st_other
|= ST_ASM_SET
;
418 ST_FUNC
void asm_free_labels(TCCState
*st
)
422 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
423 ElfSym
*esym
= elfsym(s
);
425 /* Possibly update binding and visibility from asm directives
426 if the symbol has no C decl (type is VT_VOID).*/
427 s
->type
.t
&= ~VT_EXTERN
;
428 if (esym
&& s
->type
.t
== VT_VOID
) {
429 if (!s
->a
.asmexport
&& esym
->st_shndx
!= SHN_UNDEF
)
430 s
->type
.t
|= VT_STATIC
;
432 esym
->st_other
= (esym
->st_other
& ~ELFW(ST_VISIBILITY
)(-1))
434 esym
->st_info
= ELFW(ST_INFO
)(s
->a
.weak
? STB_WEAK
435 : (s
->type
.t
& VT_STATIC
) ? STB_LOCAL
437 ELFW(ST_TYPE
)(esym
->st_info
));
440 table_ident
[s
->v
- TOK_IDENT
]->sym_identifier
= s
->prev_tok
;
443 st
->asm_labels
= NULL
;
446 static void use_section1(TCCState
*s1
, Section
*sec
)
448 cur_text_section
->data_offset
= ind
;
449 cur_text_section
= sec
;
450 ind
= cur_text_section
->data_offset
;
453 static void use_section(TCCState
*s1
, const char *name
)
456 sec
= find_section(s1
, name
);
457 use_section1(s1
, sec
);
460 static void push_section(TCCState
*s1
, const char *name
)
462 Section
*sec
= find_section(s1
, name
);
463 sec
->prev
= cur_text_section
;
464 use_section1(s1
, sec
);
467 static void pop_section(TCCState
*s1
)
469 Section
*prev
= cur_text_section
->prev
;
471 tcc_error(".popsection without .pushsection");
472 cur_text_section
->prev
= NULL
;
473 use_section1(s1
, prev
);
476 static void asm_parse_directive(TCCState
*s1
, int global
)
478 int n
, offset
, v
, size
, tok1
;
482 /* assembler directive */
483 sec
= cur_text_section
;
485 case TOK_ASMDIR_align
:
486 case TOK_ASMDIR_balign
:
487 case TOK_ASMDIR_p2align
:
488 case TOK_ASMDIR_skip
:
489 case TOK_ASMDIR_space
:
492 n
= asm_int_expr(s1
);
493 if (tok1
== TOK_ASMDIR_p2align
)
496 tcc_error("invalid p2align, must be between 0 and 30");
498 tok1
= TOK_ASMDIR_align
;
500 if (tok1
== TOK_ASMDIR_align
|| tok1
== TOK_ASMDIR_balign
) {
501 if (n
< 0 || (n
& (n
-1)) != 0)
502 tcc_error("alignment must be a positive power of two");
503 offset
= (ind
+ n
- 1) & -n
;
505 /* the section must have a compatible alignment */
506 if (sec
->sh_addralign
< n
)
507 sec
->sh_addralign
= n
;
516 v
= asm_int_expr(s1
);
519 if (sec
->sh_type
!= SHT_NOBITS
) {
520 sec
->data_offset
= ind
;
521 ptr
= section_ptr_add(sec
, size
);
522 memset(ptr
, v
, size
);
526 case TOK_ASMDIR_quad
:
527 #ifdef TCC_TARGET_X86_64
537 if (tok
!= TOK_PPNUM
) {
539 tcc_error("64 bit constant");
541 vl
= strtoll(p
, (char **)&p
, 0);
545 if (sec
->sh_type
!= SHT_NOBITS
) {
546 /* XXX: endianness */
558 case TOK_ASMDIR_byte
:
561 case TOK_ASMDIR_word
:
562 case TOK_ASMDIR_short
:
565 case TOK_ASMDIR_long
:
573 if (sec
->sh_type
!= SHT_NOBITS
) {
576 #ifdef TCC_TARGET_X86_64
577 } else if (size
== 8) {
596 case TOK_ASMDIR_fill
:
598 int repeat
, size
, val
, i
, j
;
599 uint8_t repeat_buf
[8];
601 repeat
= asm_int_expr(s1
);
603 tcc_error("repeat < 0; .fill ignored");
610 size
= asm_int_expr(s1
);
612 tcc_error("size < 0; .fill ignored");
619 val
= asm_int_expr(s1
);
622 /* XXX: endianness */
624 repeat_buf
[1] = val
>> 8;
625 repeat_buf
[2] = val
>> 16;
626 repeat_buf
[3] = val
>> 24;
631 for(i
= 0; i
< repeat
; i
++) {
632 for(j
= 0; j
< size
; j
++) {
638 case TOK_ASMDIR_rept
:
641 TokenString
*init_str
;
643 repeat
= asm_int_expr(s1
);
644 init_str
= tok_str_alloc();
645 while (next(), tok
!= TOK_ASMDIR_endr
) {
647 tcc_error("we at end of file, .endr not found");
648 tok_str_add_tok(init_str
);
650 tok_str_add(init_str
, -1);
651 tok_str_add(init_str
, 0);
652 begin_macro(init_str
, 1);
653 while (repeat
-- > 0) {
654 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
),
656 macro_ptr
= init_str
->str
;
670 esym
= elfsym(e
.sym
);
672 if (esym
->st_shndx
!= cur_text_section
->sh_num
)
673 expect("constant or same-section symbol");
677 tcc_error("attempt to .org backwards");
687 /* Also accept '.set stuff', but don't do anything with this.
688 It's used in GAS to set various features like '.set mips16'. */
690 set_symbol(s1
, tok1
);
692 case TOK_ASMDIR_globl
:
693 case TOK_ASMDIR_global
:
694 case TOK_ASMDIR_weak
:
695 case TOK_ASMDIR_hidden
:
701 sym
= get_asm_sym(tok
, NULL
);
702 if (tok1
!= TOK_ASMDIR_hidden
)
703 sym
->type
.t
&= ~VT_STATIC
, sym
->a
.asmexport
= 1;
704 if (tok1
== TOK_ASMDIR_weak
)
706 else if (tok1
== TOK_ASMDIR_hidden
)
707 sym
->a
.visibility
= STV_HIDDEN
;
709 } while (tok
== ',');
711 case TOK_ASMDIR_string
:
712 case TOK_ASMDIR_ascii
:
713 case TOK_ASMDIR_asciz
:
722 expect("string constant");
724 size
= tokc
.str
.size
;
725 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
727 for(i
= 0; i
< size
; i
++)
732 } else if (tok
!= TOK_STR
) {
738 case TOK_ASMDIR_text
:
739 case TOK_ASMDIR_data
:
746 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
747 n
= asm_int_expr(s1
);
751 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
753 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
754 use_section(s1
, sname
);
757 case TOK_ASMDIR_file
:
765 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
767 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
769 if (s1
->warn_unsupported
)
770 tcc_warning("ignoring .file %s", filename
);
775 case TOK_ASMDIR_ident
:
783 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
785 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
787 if (s1
->warn_unsupported
)
788 tcc_warning("ignoring .ident %s", ident
);
793 case TOK_ASMDIR_size
:
798 sym
= asm_label_find(tok
);
800 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
803 /* XXX .size name,label2-label1 */
804 if (s1
->warn_unsupported
)
805 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
809 while (tok
!= TOK_LINEFEED
&& tok
!= ';' && tok
!= CH_EOF
) {
814 case TOK_ASMDIR_type
:
820 sym
= get_asm_sym(tok
, NULL
);
823 if (tok
== TOK_STR
) {
824 newtype
= tokc
.str
.data
;
826 if (tok
== '@' || tok
== '%')
828 newtype
= get_tok_str(tok
, NULL
);
831 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
832 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
834 else if (s1
->warn_unsupported
)
835 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
836 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
841 case TOK_ASMDIR_pushsection
:
842 case TOK_ASMDIR_section
:
845 int old_nb_section
= s1
->nb_sections
;
848 /* XXX: support more options */
851 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
853 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
855 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
859 /* skip section options */
862 expect("string constant");
866 if (tok
== '@' || tok
== '%')
871 last_text_section
= cur_text_section
;
872 if (tok1
== TOK_ASMDIR_section
)
873 use_section(s1
, sname
);
875 push_section(s1
, sname
);
876 /* If we just allocated a new section reset its alignment to
877 1. new_section normally acts for GCC compatibility and
878 sets alignment to PTR_SIZE. The assembler behaves different. */
879 if (old_nb_section
!= s1
->nb_sections
)
880 cur_text_section
->sh_addralign
= 1;
883 case TOK_ASMDIR_previous
:
887 if (!last_text_section
)
888 tcc_error("no previous section referenced");
889 sec
= cur_text_section
;
890 use_section1(s1
, last_text_section
);
891 last_text_section
= sec
;
894 case TOK_ASMDIR_popsection
:
898 #ifdef TCC_TARGET_I386
899 case TOK_ASMDIR_code16
:
905 case TOK_ASMDIR_code32
:
912 #ifdef TCC_TARGET_X86_64
913 /* added for compatibility with GAS */
914 case TOK_ASMDIR_code64
:
919 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
925 /* assemble a file */
926 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
)
929 int saved_parse_flags
= parse_flags
;
931 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
933 parse_flags
|= PARSE_FLAG_PREPROCESS
;
938 /* generate line number info */
939 if (global
&& s1
->do_debug
)
941 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
944 /* horrible gas comment */
945 while (tok
!= TOK_LINEFEED
)
947 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
948 asm_parse_directive(s1
, global
);
949 } else if (tok
== TOK_PPNUM
) {
954 n
= strtoul(p
, (char **)&p
, 10);
957 /* new local label */
958 sym
= asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
959 /* Remove the marker for tentative definitions. */
960 sym
->type
.t
&= ~VT_EXTERN
;
964 } else if (tok
>= TOK_IDENT
) {
965 /* instruction or label */
970 Sym
*sym
= asm_new_label(s1
, opcode
, 0);
971 sym
->type
.t
&= ~VT_EXTERN
;
974 } else if (tok
== '=') {
975 set_symbol(s1
, opcode
);
978 asm_opcode(s1
, opcode
);
982 if (tok
!= ';' && tok
!= TOK_LINEFEED
)
983 expect("end of line");
984 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
987 parse_flags
= saved_parse_flags
;
991 /* Assemble the current file */
992 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
996 /* default section is text */
997 cur_text_section
= text_section
;
998 ind
= cur_text_section
->data_offset
;
1000 ret
= tcc_assemble_internal(s1
, do_preprocess
, 1);
1001 asm_free_labels(s1
);
1002 cur_text_section
->data_offset
= ind
;
1007 /********************************************************************/
1008 /* GCC inline asm support */
1010 /* assemble the string 'str' in the current C compilation unit without
1011 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1013 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
, int global
)
1015 const int *saved_macro_ptr
= macro_ptr
;
1016 int dotid
= set_idnum('.', IS_ID
);
1018 tcc_open_bf(s1
, ":asm:", len
);
1019 memcpy(file
->buffer
, str
, len
);
1021 tcc_assemble_internal(s1
, 0, global
);
1024 set_idnum('.', dotid
);
1025 macro_ptr
= saved_macro_ptr
;
1028 /* find a constraint by its number or id (gcc 3 extended
1029 syntax). return -1 if not found. Return in *pp in char after the
1031 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1032 const char *name
, const char **pp
)
1040 while (isnum(*name
)) {
1041 index
= (index
* 10) + (*name
) - '0';
1044 if ((unsigned)index
>= nb_operands
)
1046 } else if (*name
== '[') {
1048 p
= strchr(name
, ']');
1050 ts
= tok_alloc(name
, p
- name
);
1051 for(index
= 0; index
< nb_operands
; index
++) {
1052 if (operands
[index
].id
== ts
->tok
)
1069 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1070 CString
*out_str
, CString
*in_str
)
1072 int c
, index
, modifier
;
1087 if (*str
== 'c' || *str
== 'n' ||
1088 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1090 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1091 and make literal operands not be decorated with '$'. */
1094 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1096 tcc_error("invalid operand reference after %%");
1097 op
= &operands
[index
];
1101 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1104 subst_asm_operand(out_str
, &sv
, modifier
);
1107 cstr_ccat(out_str
, c
);
1115 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1122 nb_operands
= *nb_operands_ptr
;
1125 if (nb_operands
>= MAX_ASM_OPERANDS
)
1126 tcc_error("too many asm operands");
1127 op
= &operands
[nb_operands
++];
1131 if (tok
< TOK_IDENT
)
1132 expect("identifier");
1137 parse_mult_str(&astr
, "string constant");
1138 op
->constraint
= tcc_malloc(astr
.size
);
1139 strcpy(op
->constraint
, astr
.data
);
1144 if (!(vtop
->type
.t
& VT_ARRAY
))
1147 /* we want to avoid LLOCAL case, except when the 'm'
1148 constraint is used. Note that it may come from
1149 register storage, so we need to convert (reg)
1151 if ((vtop
->r
& VT_LVAL
) &&
1152 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1153 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1154 !strchr(op
->constraint
, 'm')) {
1166 *nb_operands_ptr
= nb_operands
;
1170 /* parse the GCC asm() instruction */
1171 ST_FUNC
void asm_instr(void)
1173 CString astr
, astr1
;
1174 ASMOperand operands
[MAX_ASM_OPERANDS
];
1175 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1176 uint8_t clobber_regs
[NB_ASM_REGS
];
1179 /* since we always generate the asm() instruction, we can ignore
1181 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1184 parse_asm_str(&astr
);
1188 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1193 parse_asm_operands(operands
, &nb_operands
, 1);
1194 nb_outputs
= nb_operands
;
1199 parse_asm_operands(operands
, &nb_operands
, 0);
1202 /* XXX: handle registers */
1206 expect("string constant");
1207 asm_clobber(clobber_regs
, tokc
.str
.data
);
1220 /* NOTE: we do not eat the ';' so that we can restore the current
1221 token after the assembler parsing */
1225 /* save all values in the memory */
1228 /* compute constraints */
1229 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1230 clobber_regs
, &out_reg
);
1232 /* substitute the operands in the asm string. No substitution is
1233 done if no operands (GCC behaviour) */
1235 printf("asm: \"%s\"\n", (char *)astr
.data
);
1238 subst_asm_operands(operands
, nb_operands
, &astr1
, &astr
);
1244 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1247 /* generate loads */
1248 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1249 clobber_regs
, out_reg
);
1251 /* assemble the string with tcc internal assembler */
1252 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1, 0);
1254 /* restore the current C token */
1257 /* store the output values if needed */
1258 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1259 clobber_regs
, out_reg
);
1261 /* free everything */
1262 for(i
=0;i
<nb_operands
;i
++) {
1265 tcc_free(op
->constraint
);
1271 ST_FUNC
void asm_global_instr(void)
1274 int saved_nocode_wanted
= nocode_wanted
;
1276 /* Global asm blocks are always emitted. */
1279 parse_asm_str(&astr
);
1281 /* NOTE: we do not eat the ';' so that we can restore the current
1282 token after the assembler parsing */
1287 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1289 cur_text_section
= text_section
;
1290 ind
= cur_text_section
->data_offset
;
1292 /* assemble the string with tcc internal assembler */
1293 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1, 1);
1295 cur_text_section
->data_offset
= ind
;
1297 /* restore the current C token */
1301 nocode_wanted
= saved_nocode_wanted
;
1303 #endif /* CONFIG_TCC_ASM */