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_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_EXTERN
;
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 static void asm_free_labels(TCCState
*st
)
417 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
419 /* define symbol value in object file */
420 s
->type
.t
&= ~VT_EXTERN
;
421 if (s
->r
&& !s
->a
.dllimport
) {
425 sec
= st
->sections
[s
->r
];
426 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
429 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
432 st
->asm_labels
= NULL
;
435 static void use_section1(TCCState
*s1
, Section
*sec
)
437 cur_text_section
->data_offset
= ind
;
438 cur_text_section
= sec
;
439 ind
= cur_text_section
->data_offset
;
442 static void use_section(TCCState
*s1
, const char *name
)
445 sec
= find_section(s1
, name
);
446 use_section1(s1
, sec
);
449 static void push_section(TCCState
*s1
, const char *name
)
451 Section
*sec
= find_section(s1
, name
);
452 sec
->prev
= cur_text_section
;
453 use_section1(s1
, sec
);
456 static void pop_section(TCCState
*s1
)
458 Section
*prev
= cur_text_section
->prev
;
460 tcc_error(".popsection without .pushsection");
461 cur_text_section
->prev
= NULL
;
462 use_section1(s1
, prev
);
465 static void asm_parse_directive(TCCState
*s1
, int global
)
467 int n
, offset
, v
, size
, tok1
;
471 /* assembler directive */
472 sec
= cur_text_section
;
474 case TOK_ASMDIR_align
:
475 case TOK_ASMDIR_balign
:
476 case TOK_ASMDIR_p2align
:
477 case TOK_ASMDIR_skip
:
478 case TOK_ASMDIR_space
:
481 n
= asm_int_expr(s1
);
482 if (tok1
== TOK_ASMDIR_p2align
)
485 tcc_error("invalid p2align, must be between 0 and 30");
487 tok1
= TOK_ASMDIR_align
;
489 if (tok1
== TOK_ASMDIR_align
|| tok1
== TOK_ASMDIR_balign
) {
490 if (n
< 0 || (n
& (n
-1)) != 0)
491 tcc_error("alignment must be a positive power of two");
492 offset
= (ind
+ n
- 1) & -n
;
494 /* the section must have a compatible alignment */
495 if (sec
->sh_addralign
< n
)
496 sec
->sh_addralign
= n
;
505 v
= asm_int_expr(s1
);
508 if (sec
->sh_type
!= SHT_NOBITS
) {
509 sec
->data_offset
= ind
;
510 ptr
= section_ptr_add(sec
, size
);
511 memset(ptr
, v
, size
);
515 case TOK_ASMDIR_quad
:
516 #ifdef TCC_TARGET_X86_64
526 if (tok
!= TOK_PPNUM
) {
528 tcc_error("64 bit constant");
530 vl
= strtoll(p
, (char **)&p
, 0);
534 if (sec
->sh_type
!= SHT_NOBITS
) {
535 /* XXX: endianness */
547 case TOK_ASMDIR_byte
:
550 case TOK_ASMDIR_word
:
551 case TOK_ASMDIR_short
:
554 case TOK_ASMDIR_long
:
562 if (sec
->sh_type
!= SHT_NOBITS
) {
565 #ifdef TCC_TARGET_X86_64
566 } else if (size
== 8) {
585 case TOK_ASMDIR_fill
:
587 int repeat
, size
, val
, i
, j
;
588 uint8_t repeat_buf
[8];
590 repeat
= asm_int_expr(s1
);
592 tcc_error("repeat < 0; .fill ignored");
599 size
= asm_int_expr(s1
);
601 tcc_error("size < 0; .fill ignored");
608 val
= asm_int_expr(s1
);
611 /* XXX: endianness */
613 repeat_buf
[1] = val
>> 8;
614 repeat_buf
[2] = val
>> 16;
615 repeat_buf
[3] = val
>> 24;
620 for(i
= 0; i
< repeat
; i
++) {
621 for(j
= 0; j
< size
; j
++) {
627 case TOK_ASMDIR_rept
:
630 TokenString
*init_str
;
632 repeat
= asm_int_expr(s1
);
633 init_str
= tok_str_alloc();
634 while (next(), tok
!= TOK_ASMDIR_endr
) {
636 tcc_error("we at end of file, .endr not found");
637 tok_str_add_tok(init_str
);
639 tok_str_add(init_str
, -1);
640 tok_str_add(init_str
, 0);
641 begin_macro(init_str
, 1);
642 while (repeat
-- > 0) {
643 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
),
645 macro_ptr
= init_str
->str
;
659 if (e
.sym
->r
!= cur_text_section
->sh_num
)
660 expect("constant or same-section symbol");
664 tcc_error("attempt to .org backwards");
674 /* Also accept '.set stuff', but don't do anything with this.
675 It's used in GAS to set various features like '.set mips16'. */
677 set_symbol(s1
, tok1
);
679 case TOK_ASMDIR_globl
:
680 case TOK_ASMDIR_global
:
681 case TOK_ASMDIR_weak
:
682 case TOK_ASMDIR_hidden
:
688 sym
= get_asm_sym(tok
, NULL
);
689 if (tok1
!= TOK_ASMDIR_hidden
)
690 sym
->type
.t
&= ~VT_STATIC
;
691 if (tok1
== TOK_ASMDIR_weak
)
693 else if (tok1
== TOK_ASMDIR_hidden
)
694 sym
->a
.visibility
= STV_HIDDEN
;
696 } while (tok
== ',');
698 case TOK_ASMDIR_string
:
699 case TOK_ASMDIR_ascii
:
700 case TOK_ASMDIR_asciz
:
709 expect("string constant");
711 size
= tokc
.str
.size
;
712 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
714 for(i
= 0; i
< size
; i
++)
719 } else if (tok
!= TOK_STR
) {
725 case TOK_ASMDIR_text
:
726 case TOK_ASMDIR_data
:
733 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
734 n
= asm_int_expr(s1
);
738 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
740 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
741 use_section(s1
, sname
);
744 case TOK_ASMDIR_file
:
752 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
754 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
756 if (s1
->warn_unsupported
)
757 tcc_warning("ignoring .file %s", filename
);
762 case TOK_ASMDIR_ident
:
770 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
772 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
774 if (s1
->warn_unsupported
)
775 tcc_warning("ignoring .ident %s", ident
);
780 case TOK_ASMDIR_size
:
785 sym
= label_find(tok
);
787 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
790 /* XXX .size name,label2-label1 */
791 if (s1
->warn_unsupported
)
792 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
796 while (tok
!= TOK_LINEFEED
&& tok
!= ';' && tok
!= CH_EOF
) {
801 case TOK_ASMDIR_type
:
807 sym
= get_asm_sym(tok
, NULL
);
810 if (tok
== TOK_STR
) {
811 newtype
= tokc
.str
.data
;
813 if (tok
== '@' || tok
== '%')
815 newtype
= get_tok_str(tok
, NULL
);
818 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
819 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
821 else if (s1
->warn_unsupported
)
822 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
823 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
828 case TOK_ASMDIR_pushsection
:
829 case TOK_ASMDIR_section
:
832 int old_nb_section
= s1
->nb_sections
;
835 /* XXX: support more options */
838 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
840 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
842 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
846 /* skip section options */
849 expect("string constant");
853 if (tok
== '@' || tok
== '%')
858 last_text_section
= cur_text_section
;
859 if (tok1
== TOK_ASMDIR_section
)
860 use_section(s1
, sname
);
862 push_section(s1
, sname
);
863 /* If we just allocated a new section reset its alignment to
864 1. new_section normally acts for GCC compatibility and
865 sets alignment to PTR_SIZE. The assembler behaves different. */
866 if (old_nb_section
!= s1
->nb_sections
)
867 cur_text_section
->sh_addralign
= 1;
870 case TOK_ASMDIR_previous
:
874 if (!last_text_section
)
875 tcc_error("no previous section referenced");
876 sec
= cur_text_section
;
877 use_section1(s1
, last_text_section
);
878 last_text_section
= sec
;
881 case TOK_ASMDIR_popsection
:
885 #ifdef TCC_TARGET_I386
886 case TOK_ASMDIR_code16
:
892 case TOK_ASMDIR_code32
:
899 #ifdef TCC_TARGET_X86_64
900 /* added for compatibility with GAS */
901 case TOK_ASMDIR_code64
:
906 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
912 /* assemble a file */
913 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
)
916 int saved_parse_flags
= parse_flags
;
918 /* XXX: undefine C labels */
919 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
921 parse_flags
|= PARSE_FLAG_PREPROCESS
;
926 /* generate line number info */
927 if (global
&& s1
->do_debug
)
929 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
932 /* horrible gas comment */
933 while (tok
!= TOK_LINEFEED
)
935 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
936 asm_parse_directive(s1
, global
);
937 } else if (tok
== TOK_PPNUM
) {
942 n
= strtoul(p
, (char **)&p
, 10);
945 /* new local label */
946 sym
= asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
947 /* Remove the marker for tentative definitions. */
948 sym
->type
.t
&= ~VT_EXTERN
;
952 } else if (tok
>= TOK_IDENT
) {
953 /* instruction or label */
957 /* handle "extern void vide(void); __asm__("vide: ret");" as
958 "__asm__("globl vide\nvide: ret");" */
959 Sym
*sym
= sym_find(opcode
);
960 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && global
) {
961 sym
= label_find(opcode
);
963 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
964 sym
->type
.t
= VT_VOID
| VT_EXTERN
;
968 sym
= asm_new_label(s1
, opcode
, 0);
969 sym
->type
.t
&= ~VT_EXTERN
;
972 } else if (tok
== '=') {
973 set_symbol(s1
, opcode
);
976 asm_opcode(s1
, opcode
);
980 if (tok
!= ';' && tok
!= TOK_LINEFEED
)
981 expect("end of line");
982 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
986 parse_flags
= saved_parse_flags
;
990 /* Assemble the current file */
991 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
995 /* default section is text */
996 cur_text_section
= text_section
;
997 ind
= cur_text_section
->data_offset
;
999 ret
= tcc_assemble_internal(s1
, do_preprocess
, 1);
1000 cur_text_section
->data_offset
= ind
;
1005 /********************************************************************/
1006 /* GCC inline asm support */
1008 /* assemble the string 'str' in the current C compilation unit without
1009 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1011 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
, int global
)
1013 const int *saved_macro_ptr
= macro_ptr
;
1014 int dotid
= set_idnum('.', IS_ID
);
1016 tcc_open_bf(s1
, ":asm:", len
);
1017 memcpy(file
->buffer
, str
, len
);
1019 tcc_assemble_internal(s1
, 0, global
);
1022 set_idnum('.', dotid
);
1023 macro_ptr
= saved_macro_ptr
;
1026 /* find a constraint by its number or id (gcc 3 extended
1027 syntax). return -1 if not found. Return in *pp in char after the
1029 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1030 const char *name
, const char **pp
)
1038 while (isnum(*name
)) {
1039 index
= (index
* 10) + (*name
) - '0';
1042 if ((unsigned)index
>= nb_operands
)
1044 } else if (*name
== '[') {
1046 p
= strchr(name
, ']');
1048 ts
= tok_alloc(name
, p
- name
);
1049 for(index
= 0; index
< nb_operands
; index
++) {
1050 if (operands
[index
].id
== ts
->tok
)
1067 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1068 CString
*out_str
, CString
*in_str
)
1070 int c
, index
, modifier
;
1085 if (*str
== 'c' || *str
== 'n' ||
1086 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1088 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1089 and make literal operands not be decorated with '$'. */
1092 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1094 tcc_error("invalid operand reference after %%");
1095 op
= &operands
[index
];
1099 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1102 subst_asm_operand(out_str
, &sv
, modifier
);
1105 cstr_ccat(out_str
, c
);
1113 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1120 nb_operands
= *nb_operands_ptr
;
1123 if (nb_operands
>= MAX_ASM_OPERANDS
)
1124 tcc_error("too many asm operands");
1125 op
= &operands
[nb_operands
++];
1129 if (tok
< TOK_IDENT
)
1130 expect("identifier");
1135 parse_mult_str(&astr
, "string constant");
1136 op
->constraint
= tcc_malloc(astr
.size
);
1137 strcpy(op
->constraint
, astr
.data
);
1142 if (!(vtop
->type
.t
& VT_ARRAY
))
1145 /* we want to avoid LLOCAL case, except when the 'm'
1146 constraint is used. Note that it may come from
1147 register storage, so we need to convert (reg)
1149 if ((vtop
->r
& VT_LVAL
) &&
1150 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1151 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1152 !strchr(op
->constraint
, 'm')) {
1164 *nb_operands_ptr
= nb_operands
;
1168 /* parse the GCC asm() instruction */
1169 ST_FUNC
void asm_instr(void)
1171 CString astr
, astr1
;
1172 ASMOperand operands
[MAX_ASM_OPERANDS
];
1173 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1174 uint8_t clobber_regs
[NB_ASM_REGS
];
1177 /* since we always generate the asm() instruction, we can ignore
1179 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1182 parse_asm_str(&astr
);
1186 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1191 parse_asm_operands(operands
, &nb_operands
, 1);
1192 nb_outputs
= nb_operands
;
1197 parse_asm_operands(operands
, &nb_operands
, 0);
1200 /* XXX: handle registers */
1204 expect("string constant");
1205 asm_clobber(clobber_regs
, tokc
.str
.data
);
1218 /* NOTE: we do not eat the ';' so that we can restore the current
1219 token after the assembler parsing */
1223 /* save all values in the memory */
1226 /* compute constraints */
1227 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1228 clobber_regs
, &out_reg
);
1230 /* substitute the operands in the asm string. No substitution is
1231 done if no operands (GCC behaviour) */
1233 printf("asm: \"%s\"\n", (char *)astr
.data
);
1236 subst_asm_operands(operands
, nb_operands
, &astr1
, &astr
);
1242 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1245 /* generate loads */
1246 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1247 clobber_regs
, out_reg
);
1249 /* assemble the string with tcc internal assembler */
1250 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1, 0);
1252 /* restore the current C token */
1255 /* store the output values if needed */
1256 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1257 clobber_regs
, out_reg
);
1259 /* free everything */
1260 for(i
=0;i
<nb_operands
;i
++) {
1263 tcc_free(op
->constraint
);
1269 ST_FUNC
void asm_global_instr(void)
1272 int saved_nocode_wanted
= nocode_wanted
;
1274 /* Global asm blocks are always emitted. */
1277 parse_asm_str(&astr
);
1279 /* NOTE: we do not eat the ';' so that we can restore the current
1280 token after the assembler parsing */
1285 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1287 cur_text_section
= text_section
;
1288 ind
= cur_text_section
->data_offset
;
1290 /* assemble the string with tcc internal assembler */
1291 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1, 1);
1293 cur_text_section
->data_offset
= ind
;
1295 /* restore the current C token */
1299 nocode_wanted
= saved_nocode_wanted
;
1301 #endif /* CONFIG_TCC_ASM */