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
)
918 int saved_nocode_wanted
;
921 saved_nocode_wanted
= nocode_wanted
;
924 /* XXX: undefine C labels */
926 ch
= file
->buf_ptr
[0];
927 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
928 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
929 set_idnum('.', IS_ID
);
931 parse_flags
|= PARSE_FLAG_PREPROCESS
;
936 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
939 /* horrible gas comment */
940 while (tok
!= TOK_LINEFEED
)
942 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
943 asm_parse_directive(s1
);
944 } else if (tok
== TOK_PPNUM
) {
949 n
= strtoul(p
, (char **)&p
, 10);
952 /* new local label */
953 sym
= asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
954 /* Remove the marker for tentative definitions. */
955 sym
->type
.t
&= ~VT_EXTERN
;
959 } else if (tok
>= TOK_IDENT
) {
960 /* instruction or label */
964 /* handle "extern void vide(void); __asm__("vide: ret");" as
965 "__asm__("globl vide\nvide: ret");" */
966 Sym
*sym
= sym_find(opcode
);
967 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && saved_nocode_wanted
) {
968 sym
= label_find(opcode
);
970 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
971 sym
->type
.t
= VT_VOID
| VT_EXTERN
;
975 sym
= asm_new_label(s1
, opcode
, 0);
976 sym
->type
.t
&= ~VT_EXTERN
;
979 } else if (tok
== '=') {
980 set_symbol(s1
, opcode
);
983 asm_opcode(s1
, opcode
);
987 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
988 expect("end of line");
990 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
996 nocode_wanted
= saved_nocode_wanted
;
1000 /* Assemble the current file */
1001 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
1006 preprocess_start(s1
);
1008 /* default section is text */
1009 cur_text_section
= text_section
;
1010 ind
= cur_text_section
->data_offset
;
1012 define_start
= define_stack
;
1014 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
1015 symbols can be safely used */
1016 put_elf_sym(symtab_section
, 0, 0,
1017 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
1018 SHN_ABS
, file
->filename
);
1020 ret
= tcc_assemble_internal(s1
, do_preprocess
);
1022 cur_text_section
->data_offset
= ind
;
1024 free_defines(define_start
);
1029 /********************************************************************/
1030 /* GCC inline asm support */
1032 /* assemble the string 'str' in the current C compilation unit without
1033 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1035 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
)
1037 int saved_parse_flags
;
1038 const int *saved_macro_ptr
;
1040 saved_parse_flags
= parse_flags
;
1041 saved_macro_ptr
= macro_ptr
;
1043 tcc_open_bf(s1
, ":asm:", len
);
1044 memcpy(file
->buffer
, str
, len
);
1047 tcc_assemble_internal(s1
, 0);
1050 parse_flags
= saved_parse_flags
;
1051 set_idnum('.', (parse_flags
& PARSE_FLAG_ASM_FILE
) ? IS_ID
: 0);
1052 macro_ptr
= saved_macro_ptr
;
1055 /* find a constraint by its number or id (gcc 3 extended
1056 syntax). return -1 if not found. Return in *pp in char after the
1058 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1059 const char *name
, const char **pp
)
1067 while (isnum(*name
)) {
1068 index
= (index
* 10) + (*name
) - '0';
1071 if ((unsigned)index
>= nb_operands
)
1073 } else if (*name
== '[') {
1075 p
= strchr(name
, ']');
1077 ts
= tok_alloc(name
, p
- name
);
1078 for(index
= 0; index
< nb_operands
; index
++) {
1079 if (operands
[index
].id
== ts
->tok
)
1096 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1098 CString
*out_str
, CString
*in_str
)
1100 int c
, index
, modifier
;
1115 if (*str
== 'c' || *str
== 'n' ||
1116 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1118 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1119 and make literal operands not be decorated with '$'. */
1122 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1124 tcc_error("invalid operand reference after %%");
1125 op
= &operands
[index
];
1129 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1132 subst_asm_operand(out_str
, &sv
, modifier
);
1135 cstr_ccat(out_str
, c
);
1143 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1150 nb_operands
= *nb_operands_ptr
;
1153 if (nb_operands
>= MAX_ASM_OPERANDS
)
1154 tcc_error("too many asm operands");
1155 op
= &operands
[nb_operands
++];
1159 if (tok
< TOK_IDENT
)
1160 expect("identifier");
1165 parse_mult_str(&astr
, "string constant");
1166 op
->constraint
= tcc_malloc(astr
.size
);
1167 strcpy(op
->constraint
, astr
.data
);
1172 if (!(vtop
->type
.t
& VT_ARRAY
))
1175 /* we want to avoid LLOCAL case, except when the 'm'
1176 constraint is used. Note that it may come from
1177 register storage, so we need to convert (reg)
1179 if ((vtop
->r
& VT_LVAL
) &&
1180 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1181 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1182 !strchr(op
->constraint
, 'm')) {
1194 *nb_operands_ptr
= nb_operands
;
1198 /* parse the GCC asm() instruction */
1199 ST_FUNC
void asm_instr(void)
1201 CString astr
, astr1
;
1202 ASMOperand operands
[MAX_ASM_OPERANDS
];
1203 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1204 uint8_t clobber_regs
[NB_ASM_REGS
];
1207 /* since we always generate the asm() instruction, we can ignore
1209 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1212 parse_asm_str(&astr
);
1216 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1221 parse_asm_operands(operands
, &nb_operands
, 1);
1222 nb_outputs
= nb_operands
;
1227 parse_asm_operands(operands
, &nb_operands
, 0);
1230 /* XXX: handle registers */
1234 expect("string constant");
1235 asm_clobber(clobber_regs
, tokc
.str
.data
);
1248 /* NOTE: we do not eat the ';' so that we can restore the current
1249 token after the assembler parsing */
1253 /* save all values in the memory */
1256 /* compute constraints */
1257 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1258 clobber_regs
, &out_reg
);
1260 /* substitute the operands in the asm string. No substitution is
1261 done if no operands (GCC behaviour) */
1263 printf("asm: \"%s\"\n", (char *)astr
.data
);
1266 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1272 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1275 /* generate loads */
1276 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1277 clobber_regs
, out_reg
);
1279 /* assemble the string with tcc internal assembler */
1280 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1);
1282 /* restore the current C token */
1285 /* store the output values if needed */
1286 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1287 clobber_regs
, out_reg
);
1289 /* free everything */
1290 for(i
=0;i
<nb_operands
;i
++) {
1293 tcc_free(op
->constraint
);
1299 ST_FUNC
void asm_global_instr(void)
1304 parse_asm_str(&astr
);
1306 /* NOTE: we do not eat the ';' so that we can restore the current
1307 token after the assembler parsing */
1312 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1314 cur_text_section
= text_section
;
1315 ind
= cur_text_section
->data_offset
;
1317 /* assemble the string with tcc internal assembler */
1318 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1);
1320 cur_text_section
->data_offset
= ind
;
1322 /* restore the current C token */
1327 #endif /* CONFIG_TCC_ASM */