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 (void) s1
; /* not used */
30 snprintf(buf
, sizeof(buf
), "L..%u", n
);
31 ts
= tok_alloc(buf
, strlen(buf
));
35 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
);
36 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
);
39 /* Return a symbol we can use inside the assembler, having name NAME.
40 The assembler symbol table is different from the C symbol table
41 (and the Sym members are used differently). But we must be able
42 to look up file-global C symbols from inside the assembler, e.g.
43 for global asm blocks to be able to refer to defined C symbols.
45 This routine gives back either an existing asm-internal
46 symbol, or a new one. In the latter case the new asm-internal
47 symbol is initialized with info from the C symbol table.
49 If CSYM is non-null we take symbol info from it, otherwise
50 we look up NAME in the C symbol table and use that. */
51 ST_FUNC Sym
* get_asm_sym(int name
, Sym
*csym
)
53 Sym
*sym
= label_find(name
);
55 sym
= label_push(&tcc_state
->asm_labels
, name
, 0);
56 sym
->type
.t
= VT_VOID
| VT_EXTERN
;
58 csym
= sym_find(name
);
59 /* We might be called for an asm block from inside a C routine
60 and so might have local decls on the identifier stack. Search
61 for the first global one. */
62 while (csym
&& csym
->scope
)
63 csym
= csym
->prev_tok
;
65 /* Now, if we have a defined global symbol copy over
66 section and offset. */
68 ((csym
->r
& (VT_SYM
|VT_CONST
)) == (VT_SYM
|VT_CONST
)) &&
71 esym
= &((ElfW(Sym
) *)symtab_section
->data
)[csym
->c
];
73 sym
->r
= esym
->st_shndx
;
74 sym
->jnext
= esym
->st_value
;
75 /* XXX can't yet store st_size anywhere. */
76 sym
->type
.t
&= ~VT_EXTERN
;
77 /* Mark that this asm symbol doesn't need to be fed back. */
78 sym
->type
.t
|= VT_IMPORT
;
84 /* We do not use the C expression parser to handle symbols. Maybe the
85 C expression parser could be tweaked to do so. */
87 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
97 n
= strtoull(p
, (char **)&p
, 0);
98 if (*p
== 'b' || *p
== 'f') {
99 /* backward or forward label */
100 label
= asm_get_local_label_name(s1
, n
);
101 sym
= label_find(label
);
103 /* backward : find the last corresponding defined label */
104 if (sym
&& sym
->r
== 0)
107 tcc_error("local label '%d' not found backward", n
);
110 if (!sym
|| sym
->r
) {
111 /* if the last label is defined, then define a new one */
112 sym
= label_push(&s1
->asm_labels
, label
, 0);
113 sym
->type
.t
= VT_STATIC
| VT_VOID
| VT_EXTERN
;
119 } else if (*p
== '\0') {
124 tcc_error("invalid number syntax");
130 asm_expr_unary(s1
, pe
);
136 asm_expr_unary(s1
, pe
);
138 tcc_error("invalid operation with label");
160 sym_dot
.type
.t
= VT_VOID
| VT_STATIC
;
161 sym_dot
.r
= cur_text_section
->sh_num
;
166 if (tok
>= TOK_IDENT
) {
167 /* label case : if the label was not found, add one */
168 sym
= get_asm_sym(tok
, NULL
);
169 if (sym
->r
== SHN_ABS
) {
170 /* if absolute symbol, no need to put a symbol value */
181 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
187 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
192 asm_expr_unary(s1
, pe
);
195 if (op
!= '*' && op
!= '/' && op
!= '%' &&
196 op
!= TOK_SHL
&& op
!= TOK_SAR
)
199 asm_expr_unary(s1
, &e2
);
200 if (pe
->sym
|| e2
.sym
)
201 tcc_error("invalid operation with label");
209 tcc_error("division by zero");
229 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
234 asm_expr_prod(s1
, pe
);
237 if (op
!= '&' && op
!= '|' && op
!= '^')
240 asm_expr_prod(s1
, &e2
);
241 if (pe
->sym
|| e2
.sym
)
242 tcc_error("invalid operation with label");
258 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
263 asm_expr_logic(s1
, pe
);
266 if (op
!= '+' && op
!= '-')
269 asm_expr_logic(s1
, &e2
);
271 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
272 goto cannot_relocate
;
274 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
278 /* NOTE: we are less powerful than gas in that case
279 because we store only one symbol in the expression */
282 } else if (pe
->sym
== e2
.sym
) {
284 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
285 } else if (pe
->sym
&& pe
->sym
->r
== e2
.sym
->r
&& pe
->sym
->r
!= 0) {
286 /* we also accept defined symbols in the same section */
287 pe
->v
+= pe
->sym
->jnext
- e2
.sym
->jnext
;
289 } else if (e2
.sym
->r
== cur_text_section
->sh_num
) {
290 /* When subtracting a defined symbol in current section
291 this actually makes the value PC-relative. */
292 pe
->v
-= e2
.sym
->jnext
- ind
- 4;
297 tcc_error("invalid operation with label");
303 static inline void asm_expr_cmp(TCCState
*s1
, ExprValue
*pe
)
308 asm_expr_sum(s1
, pe
);
311 if (op
!= TOK_EQ
&& op
!= TOK_NE
312 && (op
> TOK_GT
|| op
< TOK_ULE
))
315 asm_expr_sum(s1
, &e2
);
316 if (pe
->sym
|| e2
.sym
)
317 tcc_error("invalid operation with label");
320 pe
->v
= pe
->v
== e2
.v
;
323 pe
->v
= pe
->v
!= e2
.v
;
326 pe
->v
= (int64_t)pe
->v
< (int64_t)e2
.v
;
329 pe
->v
= (int64_t)pe
->v
>= (int64_t)e2
.v
;
332 pe
->v
= (int64_t)pe
->v
<= (int64_t)e2
.v
;
335 pe
->v
= (int64_t)pe
->v
> (int64_t)e2
.v
;
340 /* GAS compare results are -1/0 not 1/0. */
341 pe
->v
= -(int64_t)pe
->v
;
345 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
347 asm_expr_cmp(s1
, pe
);
350 ST_FUNC
int asm_int_expr(TCCState
*s1
)
359 /* NOTE: the same name space as C labels is used to avoid using too
360 much memory when storing labels in TokenStrings */
361 static Sym
* asm_new_label1(TCCState
*s1
, int label
, int is_local
,
362 int sh_num
, int value
)
366 sym
= label_find(label
);
368 /* A VT_EXTERN symbol, even if it has a section is considered
369 overridable. This is how we "define" .set targets. Real
370 definitions won't have VT_EXTERN set. */
371 if (sym
->r
&& !(sym
->type
.t
& VT_EXTERN
)) {
372 /* the label is already defined */
374 tcc_error("assembler label '%s' already defined",
375 get_tok_str(label
, NULL
));
377 /* redefinition of local labels is possible */
383 sym
= label_push(&s1
->asm_labels
, label
, 0);
384 /* If we need a symbol to hold a value, mark it as
385 tentative only (for .set). If this is for a real label
386 we'll remove VT_EXTERN. */
387 sym
->type
.t
= VT_STATIC
| VT_VOID
| VT_EXTERN
;
394 static Sym
* asm_new_label(TCCState
*s1
, int label
, int is_local
)
396 return asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
399 /* Set the value of LABEL to that of some expression (possibly
400 involving other symbols). LABEL can be overwritten later still. */
401 static Sym
* set_symbol(TCCState
*s1
, int label
)
410 return asm_new_label1(s1
, label
, 0, e
.sym
? e
.sym
->r
: SHN_ABS
, n
);
413 static void asm_free_labels(TCCState
*st
)
418 for(s
= st
->asm_labels
; s
!= NULL
; s
= s1
) {
420 /* define symbol value in object file */
421 s
->type
.t
&= ~VT_EXTERN
;
422 if (s
->r
&& !(s
->type
.t
& VT_IMPORT
)) {
426 sec
= st
->sections
[s
->r
];
427 put_extern_sym2(s
, sec
, s
->jnext
, 0, 0);
430 table_ident
[s
->v
- TOK_IDENT
]->sym_label
= NULL
;
433 st
->asm_labels
= NULL
;
436 static void use_section1(TCCState
*s1
, Section
*sec
)
438 (void) s1
; /* not used */
439 cur_text_section
->data_offset
= ind
;
440 cur_text_section
= sec
;
441 ind
= cur_text_section
->data_offset
;
444 static void use_section(TCCState
*s1
, const char *name
)
447 sec
= find_section(s1
, name
);
448 use_section1(s1
, sec
);
451 static void push_section(TCCState
*s1
, const char *name
)
453 Section
*sec
= find_section(s1
, name
);
454 sec
->prev
= cur_text_section
;
455 use_section1(s1
, sec
);
458 static void pop_section(TCCState
*s1
)
460 Section
*prev
= cur_text_section
->prev
;
462 tcc_error(".popsection without .pushsection");
463 cur_text_section
->prev
= NULL
;
464 use_section1(s1
, prev
);
467 static void asm_parse_directive(TCCState
*s1
, int global
)
469 int n
, offset
, v
, size
, tok1
;
473 /* assembler directive */
474 sec
= cur_text_section
;
476 case TOK_ASMDIR_align
:
477 case TOK_ASMDIR_balign
:
478 case TOK_ASMDIR_p2align
:
479 case TOK_ASMDIR_skip
:
480 case TOK_ASMDIR_space
:
483 n
= asm_int_expr(s1
);
484 if (tok1
== TOK_ASMDIR_p2align
)
487 tcc_error("invalid p2align, must be between 0 and 30");
489 tok1
= TOK_ASMDIR_align
;
491 if (tok1
== TOK_ASMDIR_align
|| tok1
== TOK_ASMDIR_balign
) {
492 if (n
< 0 || (n
& (n
-1)) != 0)
493 tcc_error("alignment must be a positive power of two");
494 offset
= (ind
+ n
- 1) & -n
;
496 /* the section must have a compatible alignment */
497 if (sec
->sh_addralign
< n
)
498 sec
->sh_addralign
= n
;
507 v
= asm_int_expr(s1
);
510 if (sec
->sh_type
!= SHT_NOBITS
) {
511 sec
->data_offset
= ind
;
512 ptr
= section_ptr_add(sec
, size
);
513 memset(ptr
, v
, size
);
517 case TOK_ASMDIR_quad
:
518 #ifdef TCC_TARGET_X86_64
528 if (tok
!= TOK_PPNUM
) {
530 tcc_error("64 bit constant");
532 vl
= strtoll(p
, (char **)&p
, 0);
536 if (sec
->sh_type
!= SHT_NOBITS
) {
537 /* XXX: endianness */
549 case TOK_ASMDIR_byte
:
552 case TOK_ASMDIR_word
:
553 case TOK_ASMDIR_short
:
556 case TOK_ASMDIR_long
:
564 if (sec
->sh_type
!= SHT_NOBITS
) {
567 #ifdef TCC_TARGET_X86_64
568 } else if (size
== 8) {
587 case TOK_ASMDIR_fill
:
589 int repeat
, size
, val
, i
, j
;
590 uint8_t repeat_buf
[8];
592 repeat
= asm_int_expr(s1
);
594 tcc_error("repeat < 0; .fill ignored");
601 size
= asm_int_expr(s1
);
603 tcc_error("size < 0; .fill ignored");
610 val
= asm_int_expr(s1
);
613 /* XXX: endianness */
615 repeat_buf
[1] = val
>> 8;
616 repeat_buf
[2] = val
>> 16;
617 repeat_buf
[3] = val
>> 24;
622 for(i
= 0; i
< repeat
; i
++) {
623 for(j
= 0; j
< size
; j
++) {
629 case TOK_ASMDIR_rept
:
632 TokenString
*init_str
;
633 ParseState saved_parse_state
= {0};
635 repeat
= asm_int_expr(s1
);
636 init_str
= tok_str_alloc();
638 while ((tok
!= TOK_ASMDIR_endr
) && (tok
!= CH_EOF
)) {
639 tok_str_add_tok(init_str
);
642 if (tok
== CH_EOF
) tcc_error("we at end of file, .endr not found");
644 tok_str_add(init_str
, -1);
645 tok_str_add(init_str
, 0);
646 save_parse_state(&saved_parse_state
);
647 begin_macro(init_str
, 1);
648 while (repeat
-- > 0) {
649 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
),
651 macro_ptr
= init_str
->str
;
654 restore_parse_state(&saved_parse_state
);
665 if (e
.sym
->r
!= cur_text_section
->sh_num
)
666 expect("constant or same-section symbol");
670 tcc_error("attempt to .org backwards");
680 /* Also accept '.set stuff', but don't do anything with this.
681 It's used in GAS to set various features like '.set mips16'. */
683 set_symbol(s1
, tok1
);
685 case TOK_ASMDIR_globl
:
686 case TOK_ASMDIR_global
:
687 case TOK_ASMDIR_weak
:
688 case TOK_ASMDIR_hidden
:
694 sym
= get_asm_sym(tok
, NULL
);
695 if (tok1
!= TOK_ASMDIR_hidden
)
696 sym
->type
.t
&= ~VT_STATIC
;
697 if (tok1
== TOK_ASMDIR_weak
)
698 sym
->type
.t
|= VT_WEAK
;
699 else if (tok1
== TOK_ASMDIR_hidden
)
700 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
702 } while (tok
== ',');
704 case TOK_ASMDIR_string
:
705 case TOK_ASMDIR_ascii
:
706 case TOK_ASMDIR_asciz
:
715 expect("string constant");
717 size
= tokc
.str
.size
;
718 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
720 for(i
= 0; i
< size
; i
++)
725 } else if (tok
!= TOK_STR
) {
731 case TOK_ASMDIR_text
:
732 case TOK_ASMDIR_data
:
739 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
740 n
= asm_int_expr(s1
);
744 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
746 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
747 use_section(s1
, sname
);
750 case TOK_ASMDIR_file
:
758 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
760 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
762 if (s1
->warn_unsupported
)
763 tcc_warning("ignoring .file %s", filename
);
768 case TOK_ASMDIR_ident
:
776 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
778 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
780 if (s1
->warn_unsupported
)
781 tcc_warning("ignoring .ident %s", ident
);
786 case TOK_ASMDIR_size
:
791 sym
= label_find(tok
);
793 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
796 /* XXX .size name,label2-label1 */
797 if (s1
->warn_unsupported
)
798 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
802 while (tok
!= TOK_LINEFEED
&& tok
!= ';' && tok
!= CH_EOF
) {
807 case TOK_ASMDIR_type
:
813 sym
= get_asm_sym(tok
, NULL
);
816 if (tok
== TOK_STR
) {
817 newtype
= tokc
.str
.data
;
819 if (tok
== '@' || tok
== '%')
821 newtype
= get_tok_str(tok
, NULL
);
824 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
825 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
827 else if (s1
->warn_unsupported
)
828 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
829 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
834 case TOK_ASMDIR_pushsection
:
835 case TOK_ASMDIR_section
:
838 int old_nb_section
= s1
->nb_sections
;
841 /* XXX: support more options */
844 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
846 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
848 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
852 /* skip section options */
855 expect("string constant");
859 if (tok
== '@' || tok
== '%')
864 last_text_section
= cur_text_section
;
865 if (tok1
== TOK_ASMDIR_section
)
866 use_section(s1
, sname
);
868 push_section(s1
, sname
);
869 /* If we just allocated a new section reset its alignment to
870 1. new_section normally acts for GCC compatibility and
871 sets alignment to PTR_SIZE. The assembler behaves different. */
872 if (old_nb_section
!= s1
->nb_sections
)
873 cur_text_section
->sh_addralign
= 1;
876 case TOK_ASMDIR_previous
:
880 if (!last_text_section
)
881 tcc_error("no previous section referenced");
882 sec
= cur_text_section
;
883 use_section1(s1
, last_text_section
);
884 last_text_section
= sec
;
887 case TOK_ASMDIR_popsection
:
891 #ifdef TCC_TARGET_I386
892 case TOK_ASMDIR_code16
:
898 case TOK_ASMDIR_code32
:
905 #ifdef TCC_TARGET_X86_64
906 /* added for compatibility with GAS */
907 case TOK_ASMDIR_code64
:
912 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
918 /* assemble a file */
919 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
)
923 /* XXX: undefine C labels */
925 ch
= file
->buf_ptr
[0];
926 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
927 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
928 set_idnum('.', IS_ID
);
930 parse_flags
|= PARSE_FLAG_PREPROCESS
;
935 /* generate line number info */
936 if (global
&& s1
->do_debug
)
938 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
941 /* horrible gas comment */
942 while (tok
!= TOK_LINEFEED
)
944 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
945 asm_parse_directive(s1
, global
);
946 } else if (tok
== TOK_PPNUM
) {
951 n
= strtoul(p
, (char **)&p
, 10);
954 /* new local label */
955 sym
= asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
956 /* Remove the marker for tentative definitions. */
957 sym
->type
.t
&= ~VT_EXTERN
;
961 } else if (tok
>= TOK_IDENT
) {
962 /* instruction or label */
966 /* handle "extern void vide(void); __asm__("vide: ret");" as
967 "__asm__("globl vide\nvide: ret");" */
968 Sym
*sym
= sym_find(opcode
);
969 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && global
) {
970 sym
= label_find(opcode
);
972 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
973 sym
->type
.t
= VT_VOID
| VT_EXTERN
;
977 sym
= asm_new_label(s1
, opcode
, 0);
978 sym
->type
.t
&= ~VT_EXTERN
;
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 */
998 /* Assemble the current file */
999 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
1004 define_start
= define_stack
;
1005 preprocess_start(s1
);
1006 tcc_debug_start(s1
);
1008 /* default section is text */
1009 cur_text_section
= text_section
;
1010 ind
= cur_text_section
->data_offset
;
1013 ret
= tcc_assemble_internal(s1
, do_preprocess
, 1);
1015 cur_text_section
->data_offset
= ind
;
1018 free_defines(define_start
);
1022 /********************************************************************/
1023 /* GCC inline asm support */
1025 /* assemble the string 'str' in the current C compilation unit without
1026 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1028 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
, int global
)
1030 int saved_parse_flags
;
1031 const int *saved_macro_ptr
;
1033 saved_parse_flags
= parse_flags
;
1034 saved_macro_ptr
= macro_ptr
;
1036 tcc_open_bf(s1
, ":asm:", len
);
1037 memcpy(file
->buffer
, str
, len
);
1040 tcc_assemble_internal(s1
, 0, global
);
1043 parse_flags
= saved_parse_flags
;
1044 set_idnum('.', (parse_flags
& PARSE_FLAG_ASM_FILE
) ? IS_ID
: 0);
1045 macro_ptr
= saved_macro_ptr
;
1048 /* find a constraint by its number or id (gcc 3 extended
1049 syntax). return -1 if not found. Return in *pp in char after the
1051 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1052 const char *name
, const char **pp
)
1060 while (isnum(*name
)) {
1061 index
= (index
* 10) + (*name
) - '0';
1064 if ((unsigned)index
>= nb_operands
)
1066 } else if (*name
== '[') {
1068 p
= strchr(name
, ']');
1070 ts
= tok_alloc(name
, p
- name
);
1071 for(index
= 0; index
< nb_operands
; index
++) {
1072 if (operands
[index
].id
== ts
->tok
)
1089 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1090 CString
*out_str
, CString
*in_str
)
1092 int c
, index
, modifier
;
1107 if (*str
== 'c' || *str
== 'n' ||
1108 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1110 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1111 and make literal operands not be decorated with '$'. */
1114 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1116 tcc_error("invalid operand reference after %%");
1117 op
= &operands
[index
];
1121 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1124 subst_asm_operand(out_str
, &sv
, modifier
);
1127 cstr_ccat(out_str
, c
);
1135 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1142 nb_operands
= *nb_operands_ptr
;
1145 if (nb_operands
>= MAX_ASM_OPERANDS
)
1146 tcc_error("too many asm operands");
1147 op
= &operands
[nb_operands
++];
1151 if (tok
< TOK_IDENT
)
1152 expect("identifier");
1157 parse_mult_str(&astr
, "string constant");
1158 op
->constraint
= tcc_malloc(astr
.size
);
1159 strcpy(op
->constraint
, astr
.data
);
1164 if (!(vtop
->type
.t
& VT_ARRAY
))
1167 /* we want to avoid LLOCAL case, except when the 'm'
1168 constraint is used. Note that it may come from
1169 register storage, so we need to convert (reg)
1171 if ((vtop
->r
& VT_LVAL
) &&
1172 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1173 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1174 !strchr(op
->constraint
, 'm')) {
1186 *nb_operands_ptr
= nb_operands
;
1190 /* parse the GCC asm() instruction */
1191 ST_FUNC
void asm_instr(void)
1193 CString astr
, astr1
;
1194 ASMOperand operands
[MAX_ASM_OPERANDS
];
1195 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1196 uint8_t clobber_regs
[NB_ASM_REGS
];
1199 /* since we always generate the asm() instruction, we can ignore
1201 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1204 parse_asm_str(&astr
);
1208 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1213 parse_asm_operands(operands
, &nb_operands
, 1);
1214 nb_outputs
= nb_operands
;
1219 parse_asm_operands(operands
, &nb_operands
, 0);
1222 /* XXX: handle registers */
1226 expect("string constant");
1227 asm_clobber(clobber_regs
, tokc
.str
.data
);
1240 /* NOTE: we do not eat the ';' so that we can restore the current
1241 token after the assembler parsing */
1245 /* save all values in the memory */
1248 /* compute constraints */
1249 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1250 clobber_regs
, &out_reg
);
1252 /* substitute the operands in the asm string. No substitution is
1253 done if no operands (GCC behaviour) */
1255 printf("asm: \"%s\"\n", (char *)astr
.data
);
1258 subst_asm_operands(operands
, nb_operands
, &astr1
, &astr
);
1264 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1267 /* generate loads */
1268 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1269 clobber_regs
, out_reg
);
1271 /* assemble the string with tcc internal assembler */
1272 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1, 0);
1274 /* restore the current C token */
1277 /* store the output values if needed */
1278 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1279 clobber_regs
, out_reg
);
1281 /* free everything */
1282 for(i
=0;i
<nb_operands
;i
++) {
1285 tcc_free(op
->constraint
);
1291 ST_FUNC
void asm_global_instr(void)
1294 int saved_nocode_wanted
= nocode_wanted
;
1296 /* Global asm blocks are always emitted. */
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, 1);
1315 cur_text_section
->data_offset
= ind
;
1317 /* restore the current C token */
1321 nocode_wanted
= saved_nocode_wanted
;
1323 #endif /* CONFIG_TCC_ASM */