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
25 static Section
*last_text_section
; /* to handle .previous asm directive */
27 ST_FUNC
int asm_get_local_label_name(TCCState
*s1
, unsigned int n
)
32 snprintf(buf
, sizeof(buf
), "L..%u", n
);
33 ts
= tok_alloc(buf
, strlen(buf
));
37 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
);
38 static Sym
* asm_new_label(TCCState
*s1
, int label
, int is_local
);
39 static Sym
* asm_new_label1(TCCState
*s1
, int label
, int is_local
, int sh_num
, int value
);
41 /* If a C name has an _ prepended then only asm labels that start
42 with _ are representable in C, by removing the first _. ASM names
43 without _ at the beginning don't correspond to C names, but we use
44 the global C symbol table to track ASM names as well, so we need to
45 transform those into ones that don't conflict with a C name,
46 so prepend a '.' for them, but force the ELF asm name to be set. */
47 static int asm2cname(int v
, int *addeddot
)
51 if (!tcc_state
->leading_underscore
)
53 name
= get_tok_str(v
, NULL
);
57 v
= tok_alloc(name
+ 1, strlen(name
) - 1)->tok
;
58 } else if (!strchr(name
, '.')) {
59 int n
= strlen(name
) + 2;
61 snprintf(newname
, sizeof newname
, ".%s", name
);
62 v
= tok_alloc(newname
, n
- 1)->tok
;
68 static Sym
*asm_label_find(int v
)
72 v
= asm2cname(v
, &addeddot
);
74 while (sym
&& sym
->sym_scope
&& !(sym
->type
.t
& VT_STATIC
))
79 static Sym
*asm_label_push(int v
)
81 int addeddot
, v2
= asm2cname(v
, &addeddot
);
82 /* We always add VT_EXTERN, for sym definition that's tentative
83 (for .set, removed for real defs), for mere references it's correct
85 Sym
*sym
= global_identifier_push(v2
, VT_ASM
| VT_EXTERN
| VT_STATIC
, 0);
91 /* Return a symbol we can use inside the assembler, having name NAME.
92 Symbols from asm and C source share a namespace. If we generate
93 an asm symbol it's also a (file-global) C symbol, but it's
94 either not accessible by name (like "L.123"), or its type information
95 is such that it's not usable without a proper C declaration.
97 Sometimes we need symbols accessible by name from asm, which
98 are anonymous in C, in this case CSYM can be used to transfer
99 all information from that symbol to the (possibly newly created)
101 ST_FUNC Sym
* get_asm_sym(int name
, Sym
*csym
)
103 Sym
*sym
= asm_label_find(name
);
105 sym
= asm_label_push(name
);
112 static Sym
* asm_section_sym(TCCState
*s1
, Section
*sec
)
115 int label
= tok_alloc(buf
,
116 snprintf(buf
, sizeof buf
, "L.%s", sec
->name
)
118 Sym
*sym
= asm_label_find(label
);
119 return sym
? sym
: asm_new_label1(s1
, label
, 1, sec
->sh_num
, 0);
122 /* We do not use the C expression parser to handle symbols. Maybe the
123 C expression parser could be tweaked to do so. */
125 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
135 n
= strtoull(p
, (char **)&p
, 0);
136 if (*p
== 'b' || *p
== 'f') {
137 /* backward or forward label */
138 label
= asm_get_local_label_name(s1
, n
);
139 sym
= asm_label_find(label
);
141 /* backward : find the last corresponding defined label */
142 if (sym
&& (!sym
->c
|| elfsym(sym
)->st_shndx
== SHN_UNDEF
))
145 tcc_error("local label '%d' not found backward", (int)n
);
148 if (!sym
|| (sym
->c
&& elfsym(sym
)->st_shndx
!= SHN_UNDEF
)) {
149 /* if the last label is defined, then define a new one */
150 sym
= asm_label_push(label
);
156 } else if (*p
== '\0') {
161 tcc_error("invalid number syntax");
167 asm_expr_unary(s1
, pe
);
173 asm_expr_unary(s1
, pe
);
175 tcc_error("invalid operation with label");
195 pe
->sym
= asm_section_sym(s1
, cur_text_section
);
200 if (tok
>= TOK_IDENT
) {
202 /* label case : if the label was not found, add one */
203 sym
= get_asm_sym(tok
, NULL
);
205 if (esym
&& esym
->st_shndx
== SHN_ABS
) {
206 /* if absolute symbol, no need to put a symbol value */
207 pe
->v
= esym
->st_value
;
217 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
223 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
228 asm_expr_unary(s1
, pe
);
231 if (op
!= '*' && op
!= '/' && op
!= '%' &&
232 op
!= TOK_SHL
&& op
!= TOK_SAR
)
235 asm_expr_unary(s1
, &e2
);
236 if (pe
->sym
|| e2
.sym
)
237 tcc_error("invalid operation with label");
245 tcc_error("division by zero");
265 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
270 asm_expr_prod(s1
, pe
);
273 if (op
!= '&' && op
!= '|' && op
!= '^')
276 asm_expr_prod(s1
, &e2
);
277 if (pe
->sym
|| e2
.sym
)
278 tcc_error("invalid operation with label");
294 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
299 asm_expr_logic(s1
, pe
);
302 if (op
!= '+' && op
!= '-')
305 asm_expr_logic(s1
, &e2
);
307 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
308 goto cannot_relocate
;
310 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
314 /* NOTE: we are less powerful than gas in that case
315 because we store only one symbol in the expression */
318 } else if (pe
->sym
== e2
.sym
) {
320 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
322 ElfSym
*esym1
, *esym2
;
323 esym1
= elfsym(pe
->sym
);
324 esym2
= elfsym(e2
.sym
);
325 if (esym1
&& esym1
->st_shndx
== esym2
->st_shndx
326 && esym1
->st_shndx
!= SHN_UNDEF
) {
327 /* we also accept defined symbols in the same section */
328 pe
->v
+= esym1
->st_value
- esym2
->st_value
;
330 } else if (esym2
->st_shndx
== cur_text_section
->sh_num
) {
331 /* When subtracting a defined symbol in current section
332 this actually makes the value PC-relative. */
333 pe
->v
-= esym2
->st_value
- ind
- 4;
338 tcc_error("invalid operation with label");
345 static inline void asm_expr_cmp(TCCState
*s1
, ExprValue
*pe
)
350 asm_expr_sum(s1
, pe
);
353 if (op
!= TOK_EQ
&& op
!= TOK_NE
354 && (op
> TOK_GT
|| op
< TOK_ULE
))
357 asm_expr_sum(s1
, &e2
);
358 if (pe
->sym
|| e2
.sym
)
359 tcc_error("invalid operation with label");
362 pe
->v
= pe
->v
== e2
.v
;
365 pe
->v
= pe
->v
!= e2
.v
;
368 pe
->v
= (int64_t)pe
->v
< (int64_t)e2
.v
;
371 pe
->v
= (int64_t)pe
->v
>= (int64_t)e2
.v
;
374 pe
->v
= (int64_t)pe
->v
<= (int64_t)e2
.v
;
377 pe
->v
= (int64_t)pe
->v
> (int64_t)e2
.v
;
382 /* GAS compare results are -1/0 not 1/0. */
383 pe
->v
= -(int64_t)pe
->v
;
387 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
389 asm_expr_cmp(s1
, pe
);
392 ST_FUNC
int asm_int_expr(TCCState
*s1
)
401 static Sym
* asm_new_label1(TCCState
*s1
, int label
, int is_local
,
402 int sh_num
, int value
)
407 sym
= asm_label_find(label
);
410 /* A VT_EXTERN symbol, even if it has a section is considered
411 overridable. This is how we "define" .set targets. Real
412 definitions won't have VT_EXTERN set. */
413 if (esym
&& esym
->st_shndx
!= SHN_UNDEF
) {
414 /* the label is already defined */
416 && (is_local
== 1 || (sym
->type
.t
& VT_EXTERN
)))
418 if (!(sym
->type
.t
& VT_EXTERN
))
419 tcc_error("assembler label '%s' already defined",
420 get_tok_str(label
, NULL
));
424 sym
= asm_label_push(label
);
427 put_extern_sym2(sym
, SHN_UNDEF
, 0, 0, 1);
429 esym
->st_shndx
= sh_num
;
430 esym
->st_value
= value
;
432 sym
->type
.t
&= ~VT_EXTERN
;
436 static Sym
* asm_new_label(TCCState
*s1
, int label
, int is_local
)
438 return asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
441 /* Set the value of LABEL to that of some expression (possibly
442 involving other symbols). LABEL can be overwritten later still. */
443 static Sym
* set_symbol(TCCState
*s1
, int label
)
452 esym
= elfsym(e
.sym
);
455 sym
= asm_new_label1(s1
, label
, 2, esym
? esym
->st_shndx
: SHN_ABS
, n
);
456 elfsym(sym
)->st_other
|= ST_ASM_SET
;
460 static void use_section1(TCCState
*s1
, Section
*sec
)
462 cur_text_section
->data_offset
= ind
;
463 cur_text_section
= sec
;
464 ind
= cur_text_section
->data_offset
;
467 static void use_section(TCCState
*s1
, const char *name
)
470 sec
= find_section(s1
, name
);
471 use_section1(s1
, sec
);
474 static void push_section(TCCState
*s1
, const char *name
)
476 Section
*sec
= find_section(s1
, name
);
477 sec
->prev
= cur_text_section
;
478 use_section1(s1
, sec
);
481 static void pop_section(TCCState
*s1
)
483 Section
*prev
= cur_text_section
->prev
;
485 tcc_error(".popsection without .pushsection");
486 cur_text_section
->prev
= NULL
;
487 use_section1(s1
, prev
);
490 static void asm_parse_directive(TCCState
*s1
, int global
)
492 int n
, offset
, v
, size
, tok1
;
496 /* assembler directive */
497 sec
= cur_text_section
;
499 case TOK_ASMDIR_align
:
500 case TOK_ASMDIR_balign
:
501 case TOK_ASMDIR_p2align
:
502 case TOK_ASMDIR_skip
:
503 case TOK_ASMDIR_space
:
506 n
= asm_int_expr(s1
);
507 if (tok1
== TOK_ASMDIR_p2align
)
510 tcc_error("invalid p2align, must be between 0 and 30");
512 tok1
= TOK_ASMDIR_align
;
514 if (tok1
== TOK_ASMDIR_align
|| tok1
== TOK_ASMDIR_balign
) {
515 if (n
< 0 || (n
& (n
-1)) != 0)
516 tcc_error("alignment must be a positive power of two");
517 offset
= (ind
+ n
- 1) & -n
;
519 /* the section must have a compatible alignment */
520 if (sec
->sh_addralign
< n
)
521 sec
->sh_addralign
= n
;
530 v
= asm_int_expr(s1
);
533 if (sec
->sh_type
!= SHT_NOBITS
) {
534 sec
->data_offset
= ind
;
535 ptr
= section_ptr_add(sec
, size
);
536 memset(ptr
, v
, size
);
540 case TOK_ASMDIR_quad
:
541 #ifdef TCC_TARGET_X86_64
551 if (tok
!= TOK_PPNUM
) {
553 tcc_error("64 bit constant");
555 vl
= strtoll(p
, (char **)&p
, 0);
559 if (sec
->sh_type
!= SHT_NOBITS
) {
560 /* XXX: endianness */
572 case TOK_ASMDIR_byte
:
575 case TOK_ASMDIR_word
:
576 case TOK_ASMDIR_short
:
579 case TOK_ASMDIR_long
:
587 if (sec
->sh_type
!= SHT_NOBITS
) {
590 #ifdef TCC_TARGET_X86_64
591 } else if (size
== 8) {
610 case TOK_ASMDIR_fill
:
612 int repeat
, size
, val
, i
, j
;
613 uint8_t repeat_buf
[8];
615 repeat
= asm_int_expr(s1
);
617 tcc_error("repeat < 0; .fill ignored");
624 size
= asm_int_expr(s1
);
626 tcc_error("size < 0; .fill ignored");
633 val
= asm_int_expr(s1
);
636 /* XXX: endianness */
638 repeat_buf
[1] = val
>> 8;
639 repeat_buf
[2] = val
>> 16;
640 repeat_buf
[3] = val
>> 24;
645 for(i
= 0; i
< repeat
; i
++) {
646 for(j
= 0; j
< size
; j
++) {
652 case TOK_ASMDIR_rept
:
655 TokenString
*init_str
;
657 repeat
= asm_int_expr(s1
);
658 init_str
= tok_str_alloc();
659 while (next(), tok
!= TOK_ASMDIR_endr
) {
661 tcc_error("we at end of file, .endr not found");
662 tok_str_add_tok(init_str
);
664 tok_str_add(init_str
, -1);
665 tok_str_add(init_str
, 0);
666 begin_macro(init_str
, 1);
667 while (repeat
-- > 0) {
668 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
),
670 macro_ptr
= init_str
->str
;
684 esym
= elfsym(e
.sym
);
686 if (esym
->st_shndx
!= cur_text_section
->sh_num
)
687 expect("constant or same-section symbol");
691 tcc_error("attempt to .org backwards");
701 /* Also accept '.set stuff', but don't do anything with this.
702 It's used in GAS to set various features like '.set mips16'. */
704 set_symbol(s1
, tok1
);
706 case TOK_ASMDIR_globl
:
707 case TOK_ASMDIR_global
:
708 case TOK_ASMDIR_weak
:
709 case TOK_ASMDIR_hidden
:
714 sym
= get_asm_sym(tok
, NULL
);
715 if (tok1
!= TOK_ASMDIR_hidden
)
716 sym
->type
.t
&= ~VT_STATIC
;
717 if (tok1
== TOK_ASMDIR_weak
)
719 else if (tok1
== TOK_ASMDIR_hidden
)
720 sym
->a
.visibility
= STV_HIDDEN
;
723 } while (tok
== ',');
725 case TOK_ASMDIR_string
:
726 case TOK_ASMDIR_ascii
:
727 case TOK_ASMDIR_asciz
:
736 expect("string constant");
738 size
= tokc
.str
.size
;
739 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
741 for(i
= 0; i
< size
; i
++)
746 } else if (tok
!= TOK_STR
) {
752 case TOK_ASMDIR_text
:
753 case TOK_ASMDIR_data
:
760 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
761 n
= asm_int_expr(s1
);
765 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
767 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
768 use_section(s1
, sname
);
771 case TOK_ASMDIR_file
:
779 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
781 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
783 if (s1
->warn_unsupported
)
784 tcc_warning("ignoring .file %s", filename
);
789 case TOK_ASMDIR_ident
:
797 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
799 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
801 if (s1
->warn_unsupported
)
802 tcc_warning("ignoring .ident %s", ident
);
807 case TOK_ASMDIR_size
:
812 sym
= asm_label_find(tok
);
814 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
817 /* XXX .size name,label2-label1 */
818 if (s1
->warn_unsupported
)
819 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
823 while (tok
!= TOK_LINEFEED
&& tok
!= ';' && tok
!= CH_EOF
) {
828 case TOK_ASMDIR_type
:
834 sym
= get_asm_sym(tok
, NULL
);
837 if (tok
== TOK_STR
) {
838 newtype
= tokc
.str
.data
;
840 if (tok
== '@' || tok
== '%')
842 newtype
= get_tok_str(tok
, NULL
);
845 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
846 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
848 else if (s1
->warn_unsupported
)
849 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
850 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
855 case TOK_ASMDIR_pushsection
:
856 case TOK_ASMDIR_section
:
859 int old_nb_section
= s1
->nb_sections
;
862 /* XXX: support more options */
865 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
867 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
869 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
873 /* skip section options */
876 expect("string constant");
880 if (tok
== '@' || tok
== '%')
885 last_text_section
= cur_text_section
;
886 if (tok1
== TOK_ASMDIR_section
)
887 use_section(s1
, sname
);
889 push_section(s1
, sname
);
890 /* If we just allocated a new section reset its alignment to
891 1. new_section normally acts for GCC compatibility and
892 sets alignment to PTR_SIZE. The assembler behaves different. */
893 if (old_nb_section
!= s1
->nb_sections
)
894 cur_text_section
->sh_addralign
= 1;
897 case TOK_ASMDIR_previous
:
901 if (!last_text_section
)
902 tcc_error("no previous section referenced");
903 sec
= cur_text_section
;
904 use_section1(s1
, last_text_section
);
905 last_text_section
= sec
;
908 case TOK_ASMDIR_popsection
:
912 #ifdef TCC_TARGET_I386
913 case TOK_ASMDIR_code16
:
919 case TOK_ASMDIR_code32
:
926 #ifdef TCC_TARGET_X86_64
927 /* added for compatibility with GAS */
928 case TOK_ASMDIR_code64
:
933 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
939 /* assemble a file */
940 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
)
943 int saved_parse_flags
= parse_flags
;
945 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
947 parse_flags
|= PARSE_FLAG_PREPROCESS
;
952 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
955 /* horrible gas comment */
956 while (tok
!= TOK_LINEFEED
)
958 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
959 asm_parse_directive(s1
, global
);
960 } else if (tok
== TOK_PPNUM
) {
964 n
= strtoul(p
, (char **)&p
, 10);
967 /* new local label */
968 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
972 } else if (tok
>= TOK_IDENT
) {
973 /* instruction or label */
978 asm_new_label(s1
, opcode
, 0);
981 } else if (tok
== '=') {
982 set_symbol(s1
, opcode
);
985 asm_opcode(s1
, opcode
);
989 if (tok
!= ';' && tok
!= TOK_LINEFEED
)
990 expect("end of line");
991 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
994 parse_flags
= saved_parse_flags
;
998 /* Assemble the current file */
999 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
1002 tcc_debug_start(s1
);
1003 /* default section is text */
1004 cur_text_section
= text_section
;
1005 ind
= cur_text_section
->data_offset
;
1007 ret
= tcc_assemble_internal(s1
, do_preprocess
, 1);
1008 cur_text_section
->data_offset
= ind
;
1013 /********************************************************************/
1014 /* GCC inline asm support */
1016 /* assemble the string 'str' in the current C compilation unit without
1017 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1019 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
, int global
)
1021 const int *saved_macro_ptr
= macro_ptr
;
1022 int dotid
= set_idnum('.', IS_ID
);
1023 int dolid
= set_idnum('$', 0);
1025 tcc_open_bf(s1
, ":asm:", len
);
1026 memcpy(file
->buffer
, str
, len
);
1028 tcc_assemble_internal(s1
, 0, global
);
1031 set_idnum('$', dolid
);
1032 set_idnum('.', dotid
);
1033 macro_ptr
= saved_macro_ptr
;
1036 /* find a constraint by its number or id (gcc 3 extended
1037 syntax). return -1 if not found. Return in *pp in char after the
1039 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1040 const char *name
, const char **pp
)
1048 while (isnum(*name
)) {
1049 index
= (index
* 10) + (*name
) - '0';
1052 if ((unsigned)index
>= nb_operands
)
1054 } else if (*name
== '[') {
1056 p
= strchr(name
, ']');
1058 ts
= tok_alloc(name
, p
- name
);
1059 for(index
= 0; index
< nb_operands
; index
++) {
1060 if (operands
[index
].id
== ts
->tok
)
1077 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1078 CString
*out_str
, CString
*in_str
)
1080 int c
, index
, modifier
;
1095 if (*str
== 'c' || *str
== 'n' ||
1096 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1098 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1099 and make literal operands not be decorated with '$'. */
1102 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1104 tcc_error("invalid operand reference after %%");
1105 op
= &operands
[index
];
1109 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1112 subst_asm_operand(out_str
, &sv
, modifier
);
1115 cstr_ccat(out_str
, c
);
1123 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1130 nb_operands
= *nb_operands_ptr
;
1133 if (nb_operands
>= MAX_ASM_OPERANDS
)
1134 tcc_error("too many asm operands");
1135 op
= &operands
[nb_operands
++];
1139 if (tok
< TOK_IDENT
)
1140 expect("identifier");
1145 parse_mult_str(&astr
, "string constant");
1146 op
->constraint
= tcc_malloc(astr
.size
);
1147 strcpy(op
->constraint
, astr
.data
);
1152 if (!(vtop
->type
.t
& VT_ARRAY
))
1155 /* we want to avoid LLOCAL case, except when the 'm'
1156 constraint is used. Note that it may come from
1157 register storage, so we need to convert (reg)
1159 if ((vtop
->r
& VT_LVAL
) &&
1160 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1161 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1162 !strchr(op
->constraint
, 'm')) {
1174 *nb_operands_ptr
= nb_operands
;
1178 /* parse the GCC asm() instruction */
1179 ST_FUNC
void asm_instr(void)
1181 CString astr
, astr1
;
1182 ASMOperand operands
[MAX_ASM_OPERANDS
];
1183 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1184 uint8_t clobber_regs
[NB_ASM_REGS
];
1187 /* since we always generate the asm() instruction, we can ignore
1189 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1192 parse_asm_str(&astr
);
1196 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1201 parse_asm_operands(operands
, &nb_operands
, 1);
1202 nb_outputs
= nb_operands
;
1207 parse_asm_operands(operands
, &nb_operands
, 0);
1210 /* XXX: handle registers */
1214 expect("string constant");
1215 asm_clobber(clobber_regs
, tokc
.str
.data
);
1228 /* NOTE: we do not eat the ';' so that we can restore the current
1229 token after the assembler parsing */
1233 /* save all values in the memory */
1236 /* compute constraints */
1237 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1238 clobber_regs
, &out_reg
);
1240 /* substitute the operands in the asm string. No substitution is
1241 done if no operands (GCC behaviour) */
1243 printf("asm: \"%s\"\n", (char *)astr
.data
);
1246 subst_asm_operands(operands
, nb_operands
, &astr1
, &astr
);
1252 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1255 /* generate loads */
1256 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1257 clobber_regs
, out_reg
);
1259 /* We don't allow switching section within inline asm to
1260 bleed out to surrounding code. */
1261 sec
= cur_text_section
;
1262 /* assemble the string with tcc internal assembler */
1263 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1, 0);
1264 if (sec
!= cur_text_section
) {
1265 tcc_warning("inline asm tries to change current section");
1266 use_section1(tcc_state
, sec
);
1269 /* restore the current C token */
1272 /* store the output values if needed */
1273 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1274 clobber_regs
, out_reg
);
1276 /* free everything */
1277 for(i
=0;i
<nb_operands
;i
++) {
1280 tcc_free(op
->constraint
);
1286 ST_FUNC
void asm_global_instr(void)
1289 int saved_nocode_wanted
= nocode_wanted
;
1291 /* Global asm blocks are always emitted. */
1294 parse_asm_str(&astr
);
1296 /* NOTE: we do not eat the ';' so that we can restore the current
1297 token after the assembler parsing */
1302 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1304 cur_text_section
= text_section
;
1305 ind
= cur_text_section
->data_offset
;
1307 /* assemble the string with tcc internal assembler */
1308 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1, 1);
1310 cur_text_section
->data_offset
= ind
;
1312 /* restore the current C token */
1316 nocode_wanted
= saved_nocode_wanted
;
1318 #endif /* CONFIG_TCC_ASM */