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
);
38 /* We do not use the C expression parser to handle symbols. Maybe the
39 C expression parser could be tweaked to do so. */
41 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
51 n
= strtoul(p
, (char **)&p
, 0);
52 if (*p
== 'b' || *p
== 'f') {
53 /* backward or forward label */
54 label
= asm_get_local_label_name(s1
, n
);
55 sym
= label_find(label
);
57 /* backward : find the last corresponding defined label */
58 if (sym
&& sym
->r
== 0)
61 tcc_error("local label '%d' not found backward", n
);
65 /* if the last label is defined, then define a new one */
66 sym
= label_push(&s1
->asm_labels
, label
, 0);
67 sym
->type
.t
= VT_STATIC
| VT_VOID
;
73 } else if (*p
== '\0') {
78 tcc_error("invalid number syntax");
84 asm_expr_unary(s1
, pe
);
90 asm_expr_unary(s1
, pe
);
92 tcc_error("invalid operation with label");
114 sym_dot
.type
.t
= VT_VOID
| VT_STATIC
;
115 sym_dot
.r
= cur_text_section
->sh_num
;
120 if (tok
>= TOK_IDENT
) {
121 /* label case : if the label was not found, add one */
122 sym
= label_find(tok
);
124 sym
= label_push(&s1
->asm_labels
, tok
, 0);
125 /* NOTE: by default, the symbol is global */
126 sym
->type
.t
= VT_VOID
;
128 if (sym
->r
== SHN_ABS
) {
129 /* if absolute symbol, no need to put a symbol value */
140 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
146 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
151 asm_expr_unary(s1
, pe
);
154 if (op
!= '*' && op
!= '/' && op
!= '%' &&
155 op
!= TOK_SHL
&& op
!= TOK_SAR
)
158 asm_expr_unary(s1
, &e2
);
159 if (pe
->sym
|| e2
.sym
)
160 tcc_error("invalid operation with label");
168 tcc_error("division by zero");
188 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
193 asm_expr_prod(s1
, pe
);
196 if (op
!= '&' && op
!= '|' && op
!= '^')
199 asm_expr_prod(s1
, &e2
);
200 if (pe
->sym
|| e2
.sym
)
201 tcc_error("invalid operation with label");
217 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
222 asm_expr_logic(s1
, pe
);
225 if (op
!= '+' && op
!= '-')
228 asm_expr_logic(s1
, &e2
);
230 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
231 goto cannot_relocate
;
233 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
237 /* NOTE: we are less powerful than gas in that case
238 because we store only one symbol in the expression */
241 } else if (pe
->sym
== e2
.sym
) {
243 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
244 } else if (pe
->sym
&& pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
245 /* we also accept defined symbols in the same section */
246 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
248 } else if (e2
.sym
->r
== cur_text_section
->sh_num
) {
249 /* When subtracting a defined symbol in current section
250 this actually makes the value PC-relative. */
251 pe
->v
-= e2
.sym
->jnext
- ind
- 4;
256 tcc_error("invalid operation with label");
262 static inline void asm_expr_cmp(TCCState
*s1
, ExprValue
*pe
)
267 asm_expr_sum(s1
, pe
);
270 if (op
!= TOK_EQ
&& op
!= TOK_NE
271 && (op
> TOK_GT
|| op
< TOK_ULE
))
274 asm_expr_sum(s1
, &e2
);
275 if (pe
->sym
|| e2
.sym
)
276 tcc_error("invalid operation with label");
279 pe
->v
= pe
->v
== e2
.v
;
282 pe
->v
= pe
->v
!= e2
.v
;
285 pe
->v
= (int64_t)pe
->v
< (int64_t)e2
.v
;
288 pe
->v
= (int64_t)pe
->v
>= (int64_t)e2
.v
;
291 pe
->v
= (int64_t)pe
->v
<= (int64_t)e2
.v
;
294 pe
->v
= (int64_t)pe
->v
> (int64_t)e2
.v
;
299 /* GAS compare results are -1/0 not 1/0. */
300 pe
->v
= -(int64_t)pe
->v
;
304 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
306 asm_expr_cmp(s1
, pe
);
309 ST_FUNC
int asm_int_expr(TCCState
*s1
)
318 /* NOTE: the same name space as C labels is used to avoid using too
319 much memory when storing labels in TokenStrings */
320 static void asm_new_label1(TCCState
*s1
, int label
, int is_local
,
321 int sh_num
, int value
)
325 sym
= label_find(label
);
328 /* the label is already defined */
330 tcc_error("assembler label '%s' already defined",
331 get_tok_str(label
, NULL
));
333 /* redefinition of local labels is possible */
339 sym
= label_push(&s1
->asm_labels
, label
, 0);
340 sym
->type
.t
= VT_STATIC
| VT_VOID
;
346 static void asm_new_label(TCCState
*s1
, int label
, int is_local
)
348 asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
351 static void asm_free_labels(TCCState
*st
)
356 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
358 /* define symbol value in object file */
363 sec
= st
->sections
[s
->r
];
364 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
367 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
370 st
->asm_labels
= NULL
;
373 static void use_section1(TCCState
*s1
, Section
*sec
)
375 cur_text_section
->data_offset
= ind
;
376 cur_text_section
= sec
;
377 ind
= cur_text_section
->data_offset
;
380 static void use_section(TCCState
*s1
, const char *name
)
383 sec
= find_section(s1
, name
);
384 use_section1(s1
, sec
);
387 static void push_section(TCCState
*s1
, const char *name
)
389 Section
*sec
= find_section(s1
, name
);
390 sec
->prev
= cur_text_section
;
391 use_section1(s1
, sec
);
394 static void pop_section(TCCState
*s1
)
396 Section
*prev
= cur_text_section
->prev
;
398 tcc_error(".popsection without .pushsection");
399 cur_text_section
->prev
= NULL
;
400 use_section1(s1
, prev
);
403 static void asm_parse_directive(TCCState
*s1
)
405 int n
, offset
, v
, size
, tok1
;
409 /* assembler directive */
410 sec
= cur_text_section
;
412 case TOK_ASMDIR_align
:
413 case TOK_ASMDIR_balign
:
414 case TOK_ASMDIR_p2align
:
415 case TOK_ASMDIR_skip
:
416 case TOK_ASMDIR_space
:
419 n
= asm_int_expr(s1
);
420 if (tok1
== TOK_ASMDIR_p2align
)
423 tcc_error("invalid p2align, must be between 0 and 30");
425 tok1
= TOK_ASMDIR_align
;
427 if (tok1
== TOK_ASMDIR_align
|| tok1
== TOK_ASMDIR_balign
) {
428 if (n
< 0 || (n
& (n
-1)) != 0)
429 tcc_error("alignment must be a positive power of two");
430 offset
= (ind
+ n
- 1) & -n
;
432 /* the section must have a compatible alignment */
433 if (sec
->sh_addralign
< n
)
434 sec
->sh_addralign
= n
;
443 v
= asm_int_expr(s1
);
446 if (sec
->sh_type
!= SHT_NOBITS
) {
447 sec
->data_offset
= ind
;
448 ptr
= section_ptr_add(sec
, size
);
449 memset(ptr
, v
, size
);
453 case TOK_ASMDIR_quad
:
454 #ifdef TCC_TARGET_X86_64
464 if (tok
!= TOK_PPNUM
) {
466 tcc_error("64 bit constant");
468 vl
= strtoll(p
, (char **)&p
, 0);
472 if (sec
->sh_type
!= SHT_NOBITS
) {
473 /* XXX: endianness */
485 case TOK_ASMDIR_byte
:
488 case TOK_ASMDIR_word
:
489 case TOK_ASMDIR_short
:
492 case TOK_ASMDIR_long
:
500 if (sec
->sh_type
!= SHT_NOBITS
) {
503 #ifdef TCC_TARGET_X86_64
504 } else if (size
== 8) {
523 case TOK_ASMDIR_fill
:
525 int repeat
, size
, val
, i
, j
;
526 uint8_t repeat_buf
[8];
528 repeat
= asm_int_expr(s1
);
530 tcc_error("repeat < 0; .fill ignored");
537 size
= asm_int_expr(s1
);
539 tcc_error("size < 0; .fill ignored");
546 val
= asm_int_expr(s1
);
549 /* XXX: endianness */
551 repeat_buf
[1] = val
>> 8;
552 repeat_buf
[2] = val
>> 16;
553 repeat_buf
[3] = val
>> 24;
558 for(i
= 0; i
< repeat
; i
++) {
559 for(j
= 0; j
< size
; j
++) {
565 case TOK_ASMDIR_rept
:
568 TokenString
*init_str
;
569 ParseState saved_parse_state
= {0};
571 repeat
= asm_int_expr(s1
);
572 init_str
= tok_str_alloc();
574 while ((tok
!= TOK_ASMDIR_endr
) && (tok
!= CH_EOF
)) {
575 tok_str_add_tok(init_str
);
578 if (tok
== CH_EOF
) tcc_error("we at end of file, .endr not found");
580 tok_str_add(init_str
, -1);
581 tok_str_add(init_str
, 0);
582 save_parse_state(&saved_parse_state
);
583 begin_macro(init_str
, 1);
584 while (repeat
-- > 0) {
585 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
));
586 macro_ptr
= init_str
->str
;
589 restore_parse_state(&saved_parse_state
);
600 if (e
.sym
->r
!= cur_text_section
->sh_num
)
601 expect("constant or same-section symbol");
605 tcc_error("attempt to .org backwards");
611 case TOK_ASMDIR_globl
:
612 case TOK_ASMDIR_global
:
613 case TOK_ASMDIR_weak
:
614 case TOK_ASMDIR_hidden
:
620 sym
= label_find(tok
);
622 sym
= label_push(&s1
->asm_labels
, tok
, 0);
623 sym
->type
.t
= VT_VOID
;
625 if (tok1
!= TOK_ASMDIR_hidden
)
626 sym
->type
.t
&= ~VT_STATIC
;
627 if (tok1
== TOK_ASMDIR_weak
)
628 sym
->type
.t
|= VT_WEAK
;
629 else if (tok1
== TOK_ASMDIR_hidden
)
630 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
632 } while (tok
== ',');
634 case TOK_ASMDIR_string
:
635 case TOK_ASMDIR_ascii
:
636 case TOK_ASMDIR_asciz
:
645 expect("string constant");
647 size
= tokc
.str
.size
;
648 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
650 for(i
= 0; i
< size
; i
++)
655 } else if (tok
!= TOK_STR
) {
661 case TOK_ASMDIR_text
:
662 case TOK_ASMDIR_data
:
669 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
670 n
= asm_int_expr(s1
);
674 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
676 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
677 use_section(s1
, sname
);
680 case TOK_ASMDIR_file
:
688 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
690 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
692 if (s1
->warn_unsupported
)
693 tcc_warning("ignoring .file %s", filename
);
698 case TOK_ASMDIR_ident
:
706 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
708 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
710 if (s1
->warn_unsupported
)
711 tcc_warning("ignoring .ident %s", ident
);
716 case TOK_ASMDIR_size
:
721 sym
= label_find(tok
);
723 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
726 /* XXX .size name,label2-label1 */
727 if (s1
->warn_unsupported
)
728 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
732 while (tok
!= '\n' && tok
!= CH_EOF
) {
737 case TOK_ASMDIR_type
:
743 sym
= label_find(tok
);
745 sym
= label_push(&s1
->asm_labels
, tok
, 0);
746 sym
->type
.t
= VT_VOID
;
751 if (tok
== TOK_STR
) {
752 newtype
= tokc
.str
.data
;
754 if (tok
== '@' || tok
== '%')
756 newtype
= get_tok_str(tok
, NULL
);
759 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
760 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
762 else if (s1
->warn_unsupported
)
763 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
764 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
769 case TOK_ASMDIR_pushsection
:
770 case TOK_ASMDIR_section
:
773 int old_nb_section
= s1
->nb_sections
;
776 /* XXX: support more options */
779 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
781 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
783 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
787 /* skip section options */
790 expect("string constant");
794 if (tok
== '@' || tok
== '%')
799 last_text_section
= cur_text_section
;
800 if (tok1
== TOK_ASMDIR_section
)
801 use_section(s1
, sname
);
803 push_section(s1
, sname
);
804 /* If we just allocated a new section reset its alignment to
805 1. new_section normally acts for GCC compatibility and
806 sets alignment to PTR_SIZE. The assembler behaves different. */
807 if (old_nb_section
!= s1
->nb_sections
)
808 cur_text_section
->sh_addralign
= 1;
811 case TOK_ASMDIR_previous
:
815 if (!last_text_section
)
816 tcc_error("no previous section referenced");
817 sec
= cur_text_section
;
818 use_section1(s1
, last_text_section
);
819 last_text_section
= sec
;
822 case TOK_ASMDIR_popsection
:
826 #ifdef TCC_TARGET_I386
827 case TOK_ASMDIR_code16
:
833 case TOK_ASMDIR_code32
:
840 #ifdef TCC_TARGET_X86_64
841 /* added for compatibility with GAS */
842 case TOK_ASMDIR_code64
:
847 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
853 /* assemble a file */
854 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
858 /* XXX: undefine C labels */
860 ch
= file
->buf_ptr
[0];
861 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
862 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
864 parse_flags
|= PARSE_FLAG_PREPROCESS
;
869 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
872 /* horrible gas comment */
873 while (tok
!= TOK_LINEFEED
)
875 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
876 asm_parse_directive(s1
);
877 } else if (tok
== TOK_PPNUM
) {
881 n
= strtoul(p
, (char **)&p
, 10);
884 /* new local label */
885 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
889 } else if (tok
>= TOK_IDENT
) {
890 /* instruction or label */
894 /* handle "extern void vide(void); __asm__("vide: ret");" as
895 "__asm__("globl vide\nvide: ret");" */
896 Sym
*sym
= sym_find(opcode
);
897 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && nocode_wanted
) {
898 sym
= label_find(opcode
);
900 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
901 sym
->type
.t
= VT_VOID
;
905 asm_new_label(s1
, opcode
, 0);
908 } else if (tok
== '=') {
911 n
= asm_int_expr(s1
);
912 asm_new_label1(s1
, opcode
, 0, SHN_ABS
, n
);
915 asm_opcode(s1
, opcode
);
919 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
920 expect("end of line");
922 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
931 /* Assemble the current file */
932 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
937 preprocess_start(s1
);
939 /* default section is text */
940 cur_text_section
= text_section
;
941 ind
= cur_text_section
->data_offset
;
943 define_start
= define_stack
;
945 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
946 symbols can be safely used */
947 put_elf_sym(symtab_section
, 0, 0,
948 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
949 SHN_ABS
, file
->filename
);
951 ret
= tcc_assemble_internal(s1
, do_preprocess
);
953 cur_text_section
->data_offset
= ind
;
955 free_defines(define_start
);
960 /********************************************************************/
961 /* GCC inline asm support */
963 /* assemble the string 'str' in the current C compilation unit without
964 C preprocessing. NOTE: str is modified by modifying the '\0' at the
966 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
968 int saved_parse_flags
;
969 const int *saved_macro_ptr
;
971 saved_parse_flags
= parse_flags
;
972 saved_macro_ptr
= macro_ptr
;
974 tcc_open_bf(s1
, ":asm:", len
);
975 memcpy(file
->buffer
, str
, len
);
978 tcc_assemble_internal(s1
, 0);
981 parse_flags
= saved_parse_flags
;
982 macro_ptr
= saved_macro_ptr
;
985 /* find a constraint by its number or id (gcc 3 extended
986 syntax). return -1 if not found. Return in *pp in char after the
988 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
989 const char *name
, const char **pp
)
997 while (isnum(*name
)) {
998 index
= (index
* 10) + (*name
) - '0';
1001 if ((unsigned)index
>= nb_operands
)
1003 } else if (*name
== '[') {
1005 p
= strchr(name
, ']');
1007 ts
= tok_alloc(name
, p
- name
);
1008 for(index
= 0; index
< nb_operands
; index
++) {
1009 if (operands
[index
].id
== ts
->tok
)
1026 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1028 CString
*out_str
, CString
*in_str
)
1030 int c
, index
, modifier
;
1045 if (*str
== 'c' || *str
== 'n' ||
1046 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1048 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1049 and make literal operands not be decorated with '$'. */
1052 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1054 tcc_error("invalid operand reference after %%");
1055 op
= &operands
[index
];
1059 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1062 subst_asm_operand(out_str
, &sv
, modifier
);
1065 cstr_ccat(out_str
, c
);
1073 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1080 nb_operands
= *nb_operands_ptr
;
1083 if (nb_operands
>= MAX_ASM_OPERANDS
)
1084 tcc_error("too many asm operands");
1085 op
= &operands
[nb_operands
++];
1089 if (tok
< TOK_IDENT
)
1090 expect("identifier");
1095 parse_mult_str(&astr
, "string constant");
1096 op
->constraint
= tcc_malloc(astr
.size
);
1097 strcpy(op
->constraint
, astr
.data
);
1102 if (!(vtop
->type
.t
& VT_ARRAY
))
1105 /* we want to avoid LLOCAL case, except when the 'm'
1106 constraint is used. Note that it may come from
1107 register storage, so we need to convert (reg)
1109 if ((vtop
->r
& VT_LVAL
) &&
1110 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1111 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1112 !strchr(op
->constraint
, 'm')) {
1124 *nb_operands_ptr
= nb_operands
;
1128 /* parse the GCC asm() instruction */
1129 ST_FUNC
void asm_instr(void)
1131 CString astr
, astr1
;
1132 ASMOperand operands
[MAX_ASM_OPERANDS
];
1133 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1134 uint8_t clobber_regs
[NB_ASM_REGS
];
1137 /* since we always generate the asm() instruction, we can ignore
1139 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1142 parse_asm_str(&astr
);
1146 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1151 parse_asm_operands(operands
, &nb_operands
, 1);
1152 nb_outputs
= nb_operands
;
1157 parse_asm_operands(operands
, &nb_operands
, 0);
1160 /* XXX: handle registers */
1164 expect("string constant");
1165 asm_clobber(clobber_regs
, tokc
.str
.data
);
1178 /* NOTE: we do not eat the ';' so that we can restore the current
1179 token after the assembler parsing */
1183 /* save all values in the memory */
1186 /* compute constraints */
1187 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1188 clobber_regs
, &out_reg
);
1190 /* substitute the operands in the asm string. No substitution is
1191 done if no operands (GCC behaviour) */
1193 printf("asm: \"%s\"\n", (char *)astr
.data
);
1196 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1202 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1205 /* generate loads */
1206 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1207 clobber_regs
, out_reg
);
1209 /* assemble the string with tcc internal assembler */
1210 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1212 /* restore the current C token */
1215 /* store the output values if needed */
1216 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1217 clobber_regs
, out_reg
);
1219 /* free everything */
1220 for(i
=0;i
<nb_operands
;i
++) {
1223 tcc_free(op
->constraint
);
1229 ST_FUNC
void asm_global_instr(void)
1234 parse_asm_str(&astr
);
1236 /* NOTE: we do not eat the ';' so that we can restore the current
1237 token after the assembler parsing */
1242 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1244 cur_text_section
= text_section
;
1245 ind
= cur_text_section
->data_offset
;
1247 /* assemble the string with tcc internal assembler */
1248 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1250 cur_text_section
->data_offset
= ind
;
1252 /* restore the current C token */
1257 #endif /* CONFIG_TCC_ASM */