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
)
30 snprintf(buf
, sizeof(buf
), "L..%u", n
);
31 return tok_alloc_const(buf
);
34 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
);
35 static Sym
* asm_new_label(TCCState
*s1
, int label
, int is_local
);
36 static Sym
* asm_new_label1(TCCState
*s1
, int label
, int is_local
, int sh_num
, int value
);
38 /* If a C name has an _ prepended then only asm labels that start
39 with _ are representable in C, by removing the first _. ASM names
40 without _ at the beginning don't correspond to C names, but we use
41 the global C symbol table to track ASM names as well, so we need to
42 transform those into ones that don't conflict with a C name,
43 so prepend a '.' for them, but force the ELF asm name to be set. */
44 static int asm2cname(int v
, int *addeddot
)
48 if (!tcc_state
->leading_underscore
)
50 name
= get_tok_str(v
, NULL
);
54 v
= tok_alloc_const(name
+ 1);
55 } else if (!strchr(name
, '.')) {
57 snprintf(newname
, sizeof newname
, ".%s", name
);
58 v
= tok_alloc_const(newname
);
64 static Sym
*asm_label_find(int v
)
68 v
= asm2cname(v
, &addeddot
);
70 while (sym
&& sym
->sym_scope
&& !(sym
->type
.t
& VT_STATIC
))
75 static Sym
*asm_label_push(int v
)
77 int addeddot
, v2
= asm2cname(v
, &addeddot
);
78 /* We always add VT_EXTERN, for sym definition that's tentative
79 (for .set, removed for real defs), for mere references it's correct
81 Sym
*sym
= global_identifier_push(v2
, VT_ASM
| VT_EXTERN
| VT_STATIC
, 0);
87 /* Return a symbol we can use inside the assembler, having name NAME.
88 Symbols from asm and C source share a namespace. If we generate
89 an asm symbol it's also a (file-global) C symbol, but it's
90 either not accessible by name (like "L.123"), or its type information
91 is such that it's not usable without a proper C declaration.
93 Sometimes we need symbols accessible by name from asm, which
94 are anonymous in C, in this case CSYM can be used to transfer
95 all information from that symbol to the (possibly newly created)
97 ST_FUNC Sym
* get_asm_sym(int name
, Sym
*csym
)
99 Sym
*sym
= asm_label_find(name
);
101 sym
= asm_label_push(name
);
108 static Sym
* asm_section_sym(TCCState
*s1
, Section
*sec
)
110 char buf
[100]; int label
; Sym
*sym
;
111 snprintf(buf
, sizeof buf
, "L.%s", sec
->name
);
112 label
= tok_alloc_const(buf
);
113 sym
= asm_label_find(label
);
114 return sym
? sym
: asm_new_label1(s1
, label
, 1, sec
->sh_num
, 0);
117 /* We do not use the C expression parser to handle symbols. Maybe the
118 C expression parser could be tweaked to do so. */
120 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
130 n
= strtoull(p
, (char **)&p
, 0);
131 if (*p
== 'b' || *p
== 'f') {
132 /* backward or forward label */
133 label
= asm_get_local_label_name(s1
, n
);
134 sym
= asm_label_find(label
);
136 /* backward : find the last corresponding defined label */
137 if (sym
&& (!sym
->c
|| elfsym(sym
)->st_shndx
== SHN_UNDEF
))
140 tcc_error("local label '%d' not found backward", (int)n
);
143 if (!sym
|| (sym
->c
&& elfsym(sym
)->st_shndx
!= SHN_UNDEF
)) {
144 /* if the last label is defined, then define a new one */
145 sym
= asm_label_push(label
);
151 } else if (*p
== '\0') {
156 tcc_error("invalid number syntax");
162 asm_expr_unary(s1
, pe
);
168 asm_expr_unary(s1
, pe
);
170 tcc_error("invalid operation with label");
190 pe
->sym
= asm_section_sym(s1
, cur_text_section
);
195 if (tok
>= TOK_IDENT
) {
197 /* label case : if the label was not found, add one */
198 sym
= get_asm_sym(tok
, NULL
);
200 if (esym
&& esym
->st_shndx
== SHN_ABS
) {
201 /* if absolute symbol, no need to put a symbol value */
202 pe
->v
= esym
->st_value
;
212 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
218 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
223 asm_expr_unary(s1
, pe
);
226 if (op
!= '*' && op
!= '/' && op
!= '%' &&
227 op
!= TOK_SHL
&& op
!= TOK_SAR
)
230 asm_expr_unary(s1
, &e2
);
231 if (pe
->sym
|| e2
.sym
)
232 tcc_error("invalid operation with label");
240 tcc_error("division by zero");
260 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
265 asm_expr_prod(s1
, pe
);
268 if (op
!= '&' && op
!= '|' && op
!= '^')
271 asm_expr_prod(s1
, &e2
);
272 if (pe
->sym
|| e2
.sym
)
273 tcc_error("invalid operation with label");
289 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
294 asm_expr_logic(s1
, pe
);
297 if (op
!= '+' && op
!= '-')
300 asm_expr_logic(s1
, &e2
);
302 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
303 goto cannot_relocate
;
305 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
309 /* NOTE: we are less powerful than gas in that case
310 because we store only one symbol in the expression */
313 } else if (pe
->sym
== e2
.sym
) {
315 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
317 ElfSym
*esym1
, *esym2
;
318 esym1
= elfsym(pe
->sym
);
319 esym2
= elfsym(e2
.sym
);
320 if (esym1
&& esym1
->st_shndx
== esym2
->st_shndx
321 && esym1
->st_shndx
!= SHN_UNDEF
) {
322 /* we also accept defined symbols in the same section */
323 pe
->v
+= esym1
->st_value
- esym2
->st_value
;
325 } else if (esym2
->st_shndx
== cur_text_section
->sh_num
) {
326 /* When subtracting a defined symbol in current section
327 this actually makes the value PC-relative. */
328 pe
->v
-= esym2
->st_value
- ind
- 4;
333 tcc_error("invalid operation with label");
340 static inline void asm_expr_cmp(TCCState
*s1
, ExprValue
*pe
)
345 asm_expr_sum(s1
, pe
);
348 if (op
!= TOK_EQ
&& op
!= TOK_NE
349 && (op
> TOK_GT
|| op
< TOK_ULE
))
352 asm_expr_sum(s1
, &e2
);
353 if (pe
->sym
|| e2
.sym
)
354 tcc_error("invalid operation with label");
357 pe
->v
= pe
->v
== e2
.v
;
360 pe
->v
= pe
->v
!= e2
.v
;
363 pe
->v
= (int64_t)pe
->v
< (int64_t)e2
.v
;
366 pe
->v
= (int64_t)pe
->v
>= (int64_t)e2
.v
;
369 pe
->v
= (int64_t)pe
->v
<= (int64_t)e2
.v
;
372 pe
->v
= (int64_t)pe
->v
> (int64_t)e2
.v
;
377 /* GAS compare results are -1/0 not 1/0. */
378 pe
->v
= -(int64_t)pe
->v
;
382 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
384 asm_expr_cmp(s1
, pe
);
387 ST_FUNC
int asm_int_expr(TCCState
*s1
)
396 static Sym
* asm_new_label1(TCCState
*s1
, int label
, int is_local
,
397 int sh_num
, int value
)
402 sym
= asm_label_find(label
);
405 /* A VT_EXTERN symbol, even if it has a section is considered
406 overridable. This is how we "define" .set targets. Real
407 definitions won't have VT_EXTERN set. */
408 if (esym
&& esym
->st_shndx
!= SHN_UNDEF
) {
409 /* the label is already defined */
411 && (is_local
== 1 || (sym
->type
.t
& VT_EXTERN
)))
413 if (!(sym
->type
.t
& VT_EXTERN
))
414 tcc_error("assembler label '%s' already defined",
415 get_tok_str(label
, NULL
));
419 sym
= asm_label_push(label
);
422 put_extern_sym2(sym
, SHN_UNDEF
, 0, 0, 1);
424 esym
->st_shndx
= sh_num
;
425 esym
->st_value
= value
;
427 sym
->type
.t
&= ~VT_EXTERN
;
431 static Sym
* asm_new_label(TCCState
*s1
, int label
, int is_local
)
433 return asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
436 /* Set the value of LABEL to that of some expression (possibly
437 involving other symbols). LABEL can be overwritten later still. */
438 static Sym
* set_symbol(TCCState
*s1
, int label
)
447 esym
= elfsym(e
.sym
);
450 sym
= asm_new_label1(s1
, label
, 2, esym
? esym
->st_shndx
: SHN_ABS
, n
);
451 elfsym(sym
)->st_other
|= ST_ASM_SET
;
455 static void use_section1(TCCState
*s1
, Section
*sec
)
457 cur_text_section
->data_offset
= ind
;
458 cur_text_section
= sec
;
459 ind
= cur_text_section
->data_offset
;
462 static void use_section(TCCState
*s1
, const char *name
)
465 sec
= find_section(s1
, name
);
466 use_section1(s1
, sec
);
469 static void push_section(TCCState
*s1
, const char *name
)
471 Section
*sec
= find_section(s1
, name
);
472 sec
->prev
= cur_text_section
;
473 use_section1(s1
, sec
);
476 static void pop_section(TCCState
*s1
)
478 Section
*prev
= cur_text_section
->prev
;
480 tcc_error(".popsection without .pushsection");
481 cur_text_section
->prev
= NULL
;
482 use_section1(s1
, prev
);
485 static void asm_parse_directive(TCCState
*s1
, int global
)
487 int n
, offset
, v
, size
, tok1
;
491 /* assembler directive */
492 sec
= cur_text_section
;
494 case TOK_ASMDIR_align
:
495 case TOK_ASMDIR_balign
:
496 case TOK_ASMDIR_p2align
:
497 case TOK_ASMDIR_skip
:
498 case TOK_ASMDIR_space
:
501 n
= asm_int_expr(s1
);
502 if (tok1
== TOK_ASMDIR_p2align
)
505 tcc_error("invalid p2align, must be between 0 and 30");
507 tok1
= TOK_ASMDIR_align
;
509 if (tok1
== TOK_ASMDIR_align
|| tok1
== TOK_ASMDIR_balign
) {
510 if (n
< 0 || (n
& (n
-1)) != 0)
511 tcc_error("alignment must be a positive power of two");
512 offset
= (ind
+ n
- 1) & -n
;
514 /* the section must have a compatible alignment */
515 if (sec
->sh_addralign
< n
)
516 sec
->sh_addralign
= n
;
525 v
= asm_int_expr(s1
);
528 if (sec
->sh_type
!= SHT_NOBITS
) {
529 sec
->data_offset
= ind
;
530 ptr
= section_ptr_add(sec
, size
);
531 memset(ptr
, v
, size
);
535 case TOK_ASMDIR_quad
:
536 #ifdef TCC_TARGET_X86_64
546 if (tok
!= TOK_PPNUM
) {
548 tcc_error("64 bit constant");
550 vl
= strtoll(p
, (char **)&p
, 0);
554 if (sec
->sh_type
!= SHT_NOBITS
) {
555 /* XXX: endianness */
567 case TOK_ASMDIR_byte
:
570 case TOK_ASMDIR_word
:
571 case TOK_ASMDIR_short
:
574 case TOK_ASMDIR_long
:
582 if (sec
->sh_type
!= SHT_NOBITS
) {
585 #ifdef TCC_TARGET_X86_64
586 } else if (size
== 8) {
605 case TOK_ASMDIR_fill
:
607 int repeat
, size
, val
, i
, j
;
608 uint8_t repeat_buf
[8];
610 repeat
= asm_int_expr(s1
);
612 tcc_error("repeat < 0; .fill ignored");
619 size
= asm_int_expr(s1
);
621 tcc_error("size < 0; .fill ignored");
628 val
= asm_int_expr(s1
);
631 /* XXX: endianness */
633 repeat_buf
[1] = val
>> 8;
634 repeat_buf
[2] = val
>> 16;
635 repeat_buf
[3] = val
>> 24;
640 for(i
= 0; i
< repeat
; i
++) {
641 for(j
= 0; j
< size
; j
++) {
647 case TOK_ASMDIR_rept
:
650 TokenString
*init_str
;
652 repeat
= asm_int_expr(s1
);
653 init_str
= tok_str_alloc();
654 while (next(), tok
!= TOK_ASMDIR_endr
) {
656 tcc_error("we at end of file, .endr not found");
657 tok_str_add_tok(init_str
);
659 tok_str_add(init_str
, -1);
660 tok_str_add(init_str
, 0);
661 begin_macro(init_str
, 1);
662 while (repeat
-- > 0) {
663 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
),
665 macro_ptr
= init_str
->str
;
679 esym
= elfsym(e
.sym
);
681 if (esym
->st_shndx
!= 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
:
709 sym
= get_asm_sym(tok
, NULL
);
710 if (tok1
!= TOK_ASMDIR_hidden
)
711 sym
->type
.t
&= ~VT_STATIC
;
712 if (tok1
== TOK_ASMDIR_weak
)
714 else if (tok1
== TOK_ASMDIR_hidden
)
715 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
:
773 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
775 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
776 tcc_warning_c(warn_unsupported
)("ignoring .file %s", filename
);
780 case TOK_ASMDIR_ident
:
787 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
789 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
790 tcc_warning_c(warn_unsupported
)("ignoring .ident %s", ident
);
794 case TOK_ASMDIR_size
:
799 sym
= asm_label_find(tok
);
801 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
803 /* XXX .size name,label2-label1 */
804 tcc_warning_c(warn_unsupported
)("ignoring .size %s,*", get_tok_str(tok
, NULL
));
807 while (tok
!= TOK_LINEFEED
&& tok
!= ';' && tok
!= CH_EOF
) {
812 case TOK_ASMDIR_type
:
818 sym
= get_asm_sym(tok
, NULL
);
821 if (tok
== TOK_STR
) {
822 newtype
= tokc
.str
.data
;
824 if (tok
== '@' || tok
== '%')
826 newtype
= get_tok_str(tok
, NULL
);
829 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
830 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
832 tcc_warning_c(warn_unsupported
)("change type of '%s' from 0x%x to '%s' ignored",
833 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
838 case TOK_ASMDIR_pushsection
:
839 case TOK_ASMDIR_section
:
842 int old_nb_section
= s1
->nb_sections
;
845 /* XXX: support more options */
848 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
850 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
852 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
856 /* skip section options */
859 expect("string constant");
863 if (tok
== '@' || tok
== '%')
868 last_text_section
= cur_text_section
;
869 if (tok1
== TOK_ASMDIR_section
)
870 use_section(s1
, sname
);
872 push_section(s1
, sname
);
873 /* If we just allocated a new section reset its alignment to
874 1. new_section normally acts for GCC compatibility and
875 sets alignment to PTR_SIZE. The assembler behaves different. */
876 if (old_nb_section
!= s1
->nb_sections
)
877 cur_text_section
->sh_addralign
= 1;
880 case TOK_ASMDIR_previous
:
884 if (!last_text_section
)
885 tcc_error("no previous section referenced");
886 sec
= cur_text_section
;
887 use_section1(s1
, last_text_section
);
888 last_text_section
= sec
;
891 case TOK_ASMDIR_popsection
:
895 #ifdef TCC_TARGET_I386
896 case TOK_ASMDIR_code16
:
902 case TOK_ASMDIR_code32
:
909 #ifdef TCC_TARGET_X86_64
910 /* added for compatibility with GAS */
911 case TOK_ASMDIR_code64
:
916 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
922 /* assemble a file */
923 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
)
926 int saved_parse_flags
= parse_flags
;
928 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
930 parse_flags
|= PARSE_FLAG_PREPROCESS
;
935 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
938 /* horrible gas comment */
939 while (tok
!= TOK_LINEFEED
)
941 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
942 asm_parse_directive(s1
, global
);
943 } else if (tok
== TOK_PPNUM
) {
947 n
= strtoul(p
, (char **)&p
, 10);
950 /* new local label */
951 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
955 } else if (tok
>= TOK_IDENT
) {
956 /* instruction or label */
961 asm_new_label(s1
, opcode
, 0);
964 } else if (tok
== '=') {
965 set_symbol(s1
, opcode
);
968 asm_opcode(s1
, opcode
);
972 if (tok
!= ';' && tok
!= TOK_LINEFEED
)
973 expect("end of line");
974 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
977 parse_flags
= saved_parse_flags
;
981 /* Assemble the current file */
982 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
986 /* default section is text */
987 cur_text_section
= text_section
;
988 ind
= cur_text_section
->data_offset
;
990 ret
= tcc_assemble_internal(s1
, do_preprocess
, 1);
991 cur_text_section
->data_offset
= ind
;
996 /********************************************************************/
997 /* GCC inline asm support */
999 /* assemble the string 'str' in the current C compilation unit without
1000 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1002 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
, int global
)
1004 const int *saved_macro_ptr
= macro_ptr
;
1005 int dotid
= set_idnum('.', IS_ID
);
1006 int dolid
= set_idnum('$', 0);
1008 tcc_open_bf(s1
, ":asm:", len
);
1009 memcpy(file
->buffer
, str
, len
);
1011 tcc_assemble_internal(s1
, 0, global
);
1014 set_idnum('$', dolid
);
1015 set_idnum('.', dotid
);
1016 macro_ptr
= saved_macro_ptr
;
1019 /* find a constraint by its number or id (gcc 3 extended
1020 syntax). return -1 if not found. Return in *pp in char after the
1022 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1023 const char *name
, const char **pp
)
1031 while (isnum(*name
)) {
1032 index
= (index
* 10) + (*name
) - '0';
1035 if ((unsigned)index
>= nb_operands
)
1037 } else if (*name
== '[') {
1039 p
= strchr(name
, ']');
1041 ts
= tok_alloc(name
, p
- name
);
1042 for(index
= 0; index
< nb_operands
; index
++) {
1043 if (operands
[index
].id
== ts
->tok
)
1060 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1061 CString
*out_str
, CString
*in_str
)
1063 int c
, index
, modifier
;
1078 if (*str
== 'c' || *str
== 'n' ||
1079 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1081 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1082 and make literal operands not be decorated with '$'. */
1085 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1087 tcc_error("invalid operand reference after %%");
1088 op
= &operands
[index
];
1092 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1095 subst_asm_operand(out_str
, &sv
, modifier
);
1098 cstr_ccat(out_str
, c
);
1106 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1113 nb_operands
= *nb_operands_ptr
;
1116 if (nb_operands
>= MAX_ASM_OPERANDS
)
1117 tcc_error("too many asm operands");
1118 op
= &operands
[nb_operands
++];
1122 if (tok
< TOK_IDENT
)
1123 expect("identifier");
1128 parse_mult_str(&astr
, "string constant");
1129 op
->constraint
= tcc_malloc(astr
.size
);
1130 strcpy(op
->constraint
, astr
.data
);
1135 if (!(vtop
->type
.t
& VT_ARRAY
))
1138 /* we want to avoid LLOCAL case, except when the 'm'
1139 constraint is used. Note that it may come from
1140 register storage, so we need to convert (reg)
1142 if ((vtop
->r
& VT_LVAL
) &&
1143 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1144 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1145 !strchr(op
->constraint
, 'm')) {
1157 *nb_operands_ptr
= nb_operands
;
1161 /* parse the GCC asm() instruction */
1162 ST_FUNC
void asm_instr(void)
1164 CString astr
, astr1
;
1165 ASMOperand operands
[MAX_ASM_OPERANDS
];
1166 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1167 uint8_t clobber_regs
[NB_ASM_REGS
];
1170 /* since we always generate the asm() instruction, we can ignore
1172 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1175 parse_asm_str(&astr
);
1179 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1184 parse_asm_operands(operands
, &nb_operands
, 1);
1185 nb_outputs
= nb_operands
;
1190 parse_asm_operands(operands
, &nb_operands
, 0);
1193 /* XXX: handle registers */
1197 expect("string constant");
1198 asm_clobber(clobber_regs
, tokc
.str
.data
);
1211 /* NOTE: we do not eat the ';' so that we can restore the current
1212 token after the assembler parsing */
1216 /* save all values in the memory */
1219 /* compute constraints */
1220 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1221 clobber_regs
, &out_reg
);
1223 /* substitute the operands in the asm string. No substitution is
1224 done if no operands (GCC behaviour) */
1226 printf("asm: \"%s\"\n", (char *)astr
.data
);
1229 subst_asm_operands(operands
, nb_operands
, &astr1
, &astr
);
1235 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1238 /* generate loads */
1239 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1240 clobber_regs
, out_reg
);
1242 /* We don't allow switching section within inline asm to
1243 bleed out to surrounding code. */
1244 sec
= cur_text_section
;
1245 /* assemble the string with tcc internal assembler */
1246 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1, 0);
1247 if (sec
!= cur_text_section
) {
1248 tcc_warning("inline asm tries to change current section");
1249 use_section1(tcc_state
, sec
);
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
;
1302 /********************************************************/
1304 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
1306 tcc_error("asm not supported");
1309 ST_FUNC
void asm_instr(void)
1311 tcc_error("inline asm() not supported");
1314 ST_FUNC
void asm_global_instr(void)
1316 tcc_error("inline asm() not supported");
1318 #endif /* CONFIG_TCC_ASM */