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 /* 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
->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. */
77 sym
->type
.t
|= VT_IMPORT
;
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
= strtoul(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
->type
.t
& VT_IMPORT
)) {
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
)
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
;
631 ParseState saved_parse_state
= {0};
633 repeat
= asm_int_expr(s1
);
634 init_str
= tok_str_alloc();
636 while ((tok
!= TOK_ASMDIR_endr
) && (tok
!= CH_EOF
)) {
637 tok_str_add_tok(init_str
);
640 if (tok
== CH_EOF
) tcc_error("we at end of file, .endr not found");
642 tok_str_add(init_str
, -1);
643 tok_str_add(init_str
, 0);
644 save_parse_state(&saved_parse_state
);
645 begin_macro(init_str
, 1);
646 while (repeat
-- > 0) {
647 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
));
648 macro_ptr
= init_str
->str
;
651 restore_parse_state(&saved_parse_state
);
662 if (e
.sym
->r
!= cur_text_section
->sh_num
)
663 expect("constant or same-section symbol");
667 tcc_error("attempt to .org backwards");
677 /* Also accept '.set stuff', but don't do anything with this.
678 It's used in GAS to set various features like '.set mips16'. */
680 set_symbol(s1
, tok1
);
682 case TOK_ASMDIR_globl
:
683 case TOK_ASMDIR_global
:
684 case TOK_ASMDIR_weak
:
685 case TOK_ASMDIR_hidden
:
691 sym
= get_asm_sym(tok
, NULL
);
692 if (tok1
!= TOK_ASMDIR_hidden
)
693 sym
->type
.t
&= ~VT_STATIC
;
694 if (tok1
== TOK_ASMDIR_weak
)
695 sym
->type
.t
|= VT_WEAK
;
696 else if (tok1
== TOK_ASMDIR_hidden
)
697 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
699 } while (tok
== ',');
701 case TOK_ASMDIR_string
:
702 case TOK_ASMDIR_ascii
:
703 case TOK_ASMDIR_asciz
:
712 expect("string constant");
714 size
= tokc
.str
.size
;
715 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
717 for(i
= 0; i
< size
; i
++)
722 } else if (tok
!= TOK_STR
) {
728 case TOK_ASMDIR_text
:
729 case TOK_ASMDIR_data
:
736 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
737 n
= asm_int_expr(s1
);
741 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
743 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
744 use_section(s1
, sname
);
747 case TOK_ASMDIR_file
:
755 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
757 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
759 if (s1
->warn_unsupported
)
760 tcc_warning("ignoring .file %s", filename
);
765 case TOK_ASMDIR_ident
:
773 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
775 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
777 if (s1
->warn_unsupported
)
778 tcc_warning("ignoring .ident %s", ident
);
783 case TOK_ASMDIR_size
:
788 sym
= label_find(tok
);
790 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
793 /* XXX .size name,label2-label1 */
794 if (s1
->warn_unsupported
)
795 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
799 while (tok
!= TOK_LINEFEED
&& tok
!= ';' && tok
!= CH_EOF
) {
804 case TOK_ASMDIR_type
:
810 sym
= get_asm_sym(tok
, NULL
);
813 if (tok
== TOK_STR
) {
814 newtype
= tokc
.str
.data
;
816 if (tok
== '@' || tok
== '%')
818 newtype
= get_tok_str(tok
, NULL
);
821 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
822 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
824 else if (s1
->warn_unsupported
)
825 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
826 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
831 case TOK_ASMDIR_pushsection
:
832 case TOK_ASMDIR_section
:
835 int old_nb_section
= s1
->nb_sections
;
838 /* XXX: support more options */
841 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
843 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
845 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
849 /* skip section options */
852 expect("string constant");
856 if (tok
== '@' || tok
== '%')
861 last_text_section
= cur_text_section
;
862 if (tok1
== TOK_ASMDIR_section
)
863 use_section(s1
, sname
);
865 push_section(s1
, sname
);
866 /* If we just allocated a new section reset its alignment to
867 1. new_section normally acts for GCC compatibility and
868 sets alignment to PTR_SIZE. The assembler behaves different. */
869 if (old_nb_section
!= s1
->nb_sections
)
870 cur_text_section
->sh_addralign
= 1;
873 case TOK_ASMDIR_previous
:
877 if (!last_text_section
)
878 tcc_error("no previous section referenced");
879 sec
= cur_text_section
;
880 use_section1(s1
, last_text_section
);
881 last_text_section
= sec
;
884 case TOK_ASMDIR_popsection
:
888 #ifdef TCC_TARGET_I386
889 case TOK_ASMDIR_code16
:
895 case TOK_ASMDIR_code32
:
902 #ifdef TCC_TARGET_X86_64
903 /* added for compatibility with GAS */
904 case TOK_ASMDIR_code64
:
909 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
915 /* assemble a file */
916 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
)
920 /* XXX: undefine C labels */
922 ch
= file
->buf_ptr
[0];
923 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
924 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
925 set_idnum('.', IS_ID
);
927 parse_flags
|= PARSE_FLAG_PREPROCESS
;
932 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
935 /* horrible gas comment */
936 while (tok
!= TOK_LINEFEED
)
938 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
939 asm_parse_directive(s1
);
940 } else if (tok
== TOK_PPNUM
) {
945 n
= strtoul(p
, (char **)&p
, 10);
948 /* new local label */
949 sym
= asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
950 /* Remove the marker for tentative definitions. */
951 sym
->type
.t
&= ~VT_EXTERN
;
955 } else if (tok
>= TOK_IDENT
) {
956 /* instruction or label */
960 /* handle "extern void vide(void); __asm__("vide: ret");" as
961 "__asm__("globl vide\nvide: ret");" */
962 Sym
*sym
= sym_find(opcode
);
963 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && nocode_wanted
) {
964 sym
= label_find(opcode
);
966 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
967 sym
->type
.t
= VT_VOID
| VT_EXTERN
;
971 sym
= asm_new_label(s1
, opcode
, 0);
972 sym
->type
.t
&= ~VT_EXTERN
;
975 } else if (tok
== '=') {
976 set_symbol(s1
, opcode
);
979 asm_opcode(s1
, opcode
);
983 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
984 expect("end of line");
986 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
995 /* Assemble the current file */
996 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
1001 preprocess_start(s1
);
1003 /* default section is text */
1004 cur_text_section
= text_section
;
1005 ind
= cur_text_section
->data_offset
;
1007 define_start
= define_stack
;
1009 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
1010 symbols can be safely used */
1011 put_elf_sym(symtab_section
, 0, 0,
1012 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
1013 SHN_ABS
, file
->filename
);
1015 ret
= tcc_assemble_internal(s1
, do_preprocess
);
1017 cur_text_section
->data_offset
= ind
;
1019 free_defines(define_start
);
1024 /********************************************************************/
1025 /* GCC inline asm support */
1027 /* assemble the string 'str' in the current C compilation unit without
1028 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1030 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
1032 int saved_parse_flags
;
1033 const int *saved_macro_ptr
;
1035 saved_parse_flags
= parse_flags
;
1036 saved_macro_ptr
= macro_ptr
;
1038 tcc_open_bf(s1
, ":asm:", len
);
1039 memcpy(file
->buffer
, str
, len
);
1042 tcc_assemble_internal(s1
, 0);
1045 parse_flags
= saved_parse_flags
;
1046 set_idnum('.', (parse_flags
& PARSE_FLAG_ASM_FILE
) ? IS_ID
: 0);
1047 macro_ptr
= saved_macro_ptr
;
1050 /* find a constraint by its number or id (gcc 3 extended
1051 syntax). return -1 if not found. Return in *pp in char after the
1053 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1054 const char *name
, const char **pp
)
1062 while (isnum(*name
)) {
1063 index
= (index
* 10) + (*name
) - '0';
1066 if ((unsigned)index
>= nb_operands
)
1068 } else if (*name
== '[') {
1070 p
= strchr(name
, ']');
1072 ts
= tok_alloc(name
, p
- name
);
1073 for(index
= 0; index
< nb_operands
; index
++) {
1074 if (operands
[index
].id
== ts
->tok
)
1091 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1093 CString
*out_str
, CString
*in_str
)
1095 int c
, index
, modifier
;
1110 if (*str
== 'c' || *str
== 'n' ||
1111 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1113 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1114 and make literal operands not be decorated with '$'. */
1117 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1119 tcc_error("invalid operand reference after %%");
1120 op
= &operands
[index
];
1124 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1127 subst_asm_operand(out_str
, &sv
, modifier
);
1130 cstr_ccat(out_str
, c
);
1138 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1145 nb_operands
= *nb_operands_ptr
;
1148 if (nb_operands
>= MAX_ASM_OPERANDS
)
1149 tcc_error("too many asm operands");
1150 op
= &operands
[nb_operands
++];
1154 if (tok
< TOK_IDENT
)
1155 expect("identifier");
1160 parse_mult_str(&astr
, "string constant");
1161 op
->constraint
= tcc_malloc(astr
.size
);
1162 strcpy(op
->constraint
, astr
.data
);
1167 if (!(vtop
->type
.t
& VT_ARRAY
))
1170 /* we want to avoid LLOCAL case, except when the 'm'
1171 constraint is used. Note that it may come from
1172 register storage, so we need to convert (reg)
1174 if ((vtop
->r
& VT_LVAL
) &&
1175 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1176 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1177 !strchr(op
->constraint
, 'm')) {
1189 *nb_operands_ptr
= nb_operands
;
1193 /* parse the GCC asm() instruction */
1194 ST_FUNC
void asm_instr(void)
1196 CString astr
, astr1
;
1197 ASMOperand operands
[MAX_ASM_OPERANDS
];
1198 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1199 uint8_t clobber_regs
[NB_ASM_REGS
];
1202 /* since we always generate the asm() instruction, we can ignore
1204 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1207 parse_asm_str(&astr
);
1211 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1216 parse_asm_operands(operands
, &nb_operands
, 1);
1217 nb_outputs
= nb_operands
;
1222 parse_asm_operands(operands
, &nb_operands
, 0);
1225 /* XXX: handle registers */
1229 expect("string constant");
1230 asm_clobber(clobber_regs
, tokc
.str
.data
);
1243 /* NOTE: we do not eat the ';' so that we can restore the current
1244 token after the assembler parsing */
1248 /* save all values in the memory */
1251 /* compute constraints */
1252 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1253 clobber_regs
, &out_reg
);
1255 /* substitute the operands in the asm string. No substitution is
1256 done if no operands (GCC behaviour) */
1258 printf("asm: \"%s\"\n", (char *)astr
.data
);
1261 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1267 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1270 /* generate loads */
1271 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1272 clobber_regs
, out_reg
);
1274 /* assemble the string with tcc internal assembler */
1275 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1277 /* restore the current C token */
1280 /* store the output values if needed */
1281 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1282 clobber_regs
, out_reg
);
1284 /* free everything */
1285 for(i
=0;i
<nb_operands
;i
++) {
1288 tcc_free(op
->constraint
);
1294 ST_FUNC
void asm_global_instr(void)
1299 parse_asm_str(&astr
);
1301 /* NOTE: we do not eat the ';' so that we can restore the current
1302 token after the assembler parsing */
1307 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1309 cur_text_section
= text_section
;
1310 ind
= cur_text_section
->data_offset
;
1312 /* assemble the string with tcc internal assembler */
1313 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1315 cur_text_section
->data_offset
= ind
;
1317 /* restore the current C token */
1322 #endif /* CONFIG_TCC_ASM */