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
, int global
);
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
, int global
)
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
),
649 macro_ptr
= init_str
->str
;
652 restore_parse_state(&saved_parse_state
);
663 if (e
.sym
->r
!= cur_text_section
->sh_num
)
664 expect("constant or same-section symbol");
668 tcc_error("attempt to .org backwards");
678 /* Also accept '.set stuff', but don't do anything with this.
679 It's used in GAS to set various features like '.set mips16'. */
681 set_symbol(s1
, tok1
);
683 case TOK_ASMDIR_globl
:
684 case TOK_ASMDIR_global
:
685 case TOK_ASMDIR_weak
:
686 case TOK_ASMDIR_hidden
:
692 sym
= get_asm_sym(tok
, NULL
);
693 if (tok1
!= TOK_ASMDIR_hidden
)
694 sym
->type
.t
&= ~VT_STATIC
;
695 if (tok1
== TOK_ASMDIR_weak
)
696 sym
->type
.t
|= VT_WEAK
;
697 else if (tok1
== TOK_ASMDIR_hidden
)
698 sym
->type
.t
|= STV_HIDDEN
<< VT_VIS_SHIFT
;
700 } while (tok
== ',');
702 case TOK_ASMDIR_string
:
703 case TOK_ASMDIR_ascii
:
704 case TOK_ASMDIR_asciz
:
713 expect("string constant");
715 size
= tokc
.str
.size
;
716 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
718 for(i
= 0; i
< size
; i
++)
723 } else if (tok
!= TOK_STR
) {
729 case TOK_ASMDIR_text
:
730 case TOK_ASMDIR_data
:
737 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
738 n
= asm_int_expr(s1
);
742 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
744 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
745 use_section(s1
, sname
);
748 case TOK_ASMDIR_file
:
756 pstrcat(filename
, sizeof(filename
), tokc
.str
.data
);
758 pstrcat(filename
, sizeof(filename
), get_tok_str(tok
, NULL
));
760 if (s1
->warn_unsupported
)
761 tcc_warning("ignoring .file %s", filename
);
766 case TOK_ASMDIR_ident
:
774 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
776 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
778 if (s1
->warn_unsupported
)
779 tcc_warning("ignoring .ident %s", ident
);
784 case TOK_ASMDIR_size
:
789 sym
= label_find(tok
);
791 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
794 /* XXX .size name,label2-label1 */
795 if (s1
->warn_unsupported
)
796 tcc_warning("ignoring .size %s,*", get_tok_str(tok
, NULL
));
800 while (tok
!= TOK_LINEFEED
&& tok
!= ';' && tok
!= CH_EOF
) {
805 case TOK_ASMDIR_type
:
811 sym
= get_asm_sym(tok
, NULL
);
814 if (tok
== TOK_STR
) {
815 newtype
= tokc
.str
.data
;
817 if (tok
== '@' || tok
== '%')
819 newtype
= get_tok_str(tok
, NULL
);
822 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
823 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
825 else if (s1
->warn_unsupported
)
826 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
827 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
832 case TOK_ASMDIR_pushsection
:
833 case TOK_ASMDIR_section
:
836 int old_nb_section
= s1
->nb_sections
;
839 /* XXX: support more options */
842 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
844 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
846 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
850 /* skip section options */
853 expect("string constant");
857 if (tok
== '@' || tok
== '%')
862 last_text_section
= cur_text_section
;
863 if (tok1
== TOK_ASMDIR_section
)
864 use_section(s1
, sname
);
866 push_section(s1
, sname
);
867 /* If we just allocated a new section reset its alignment to
868 1. new_section normally acts for GCC compatibility and
869 sets alignment to PTR_SIZE. The assembler behaves different. */
870 if (old_nb_section
!= s1
->nb_sections
)
871 cur_text_section
->sh_addralign
= 1;
874 case TOK_ASMDIR_previous
:
878 if (!last_text_section
)
879 tcc_error("no previous section referenced");
880 sec
= cur_text_section
;
881 use_section1(s1
, last_text_section
);
882 last_text_section
= sec
;
885 case TOK_ASMDIR_popsection
:
889 #ifdef TCC_TARGET_I386
890 case TOK_ASMDIR_code16
:
896 case TOK_ASMDIR_code32
:
903 #ifdef TCC_TARGET_X86_64
904 /* added for compatibility with GAS */
905 case TOK_ASMDIR_code64
:
910 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
916 /* assemble a file */
917 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
)
921 /* XXX: undefine C labels */
923 ch
= file
->buf_ptr
[0];
924 tok_flags
= TOK_FLAG_BOL
| TOK_FLAG_BOF
;
925 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
926 set_idnum('.', IS_ID
);
928 parse_flags
|= PARSE_FLAG_PREPROCESS
;
933 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
936 /* horrible gas comment */
937 while (tok
!= TOK_LINEFEED
)
939 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
940 asm_parse_directive(s1
, global
);
941 } else if (tok
== TOK_PPNUM
) {
946 n
= strtoul(p
, (char **)&p
, 10);
949 /* new local label */
950 sym
= asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
951 /* Remove the marker for tentative definitions. */
952 sym
->type
.t
&= ~VT_EXTERN
;
956 } else if (tok
>= TOK_IDENT
) {
957 /* instruction or label */
961 /* handle "extern void vide(void); __asm__("vide: ret");" as
962 "__asm__("globl vide\nvide: ret");" */
963 Sym
*sym
= sym_find(opcode
);
964 if (sym
&& (sym
->type
.t
& VT_EXTERN
) && global
) {
965 sym
= label_find(opcode
);
967 sym
= label_push(&s1
->asm_labels
, opcode
, 0);
968 sym
->type
.t
= VT_VOID
| VT_EXTERN
;
972 sym
= asm_new_label(s1
, opcode
, 0);
973 sym
->type
.t
&= ~VT_EXTERN
;
976 } else if (tok
== '=') {
977 set_symbol(s1
, opcode
);
980 asm_opcode(s1
, opcode
);
984 if (tok
!= ';' && tok
!= TOK_LINEFEED
){
985 expect("end of line");
987 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
996 /* Assemble the current file */
997 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
1002 preprocess_start(s1
);
1004 /* default section is text */
1005 cur_text_section
= text_section
;
1006 ind
= cur_text_section
->data_offset
;
1008 define_start
= define_stack
;
1010 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
1011 symbols can be safely used */
1012 put_elf_sym(symtab_section
, 0, 0,
1013 ELFW(ST_INFO
)(STB_LOCAL
, STT_FILE
), 0,
1014 SHN_ABS
, file
->filename
);
1016 ret
= tcc_assemble_internal(s1
, do_preprocess
, 1);
1018 cur_text_section
->data_offset
= ind
;
1020 free_defines(define_start
);
1025 /********************************************************************/
1026 /* GCC inline asm support */
1028 /* assemble the string 'str' in the current C compilation unit without
1029 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1031 static void tcc_assemble_inline(TCCState
*s1
, char *str
, int len
, int global
)
1033 int saved_parse_flags
;
1034 const int *saved_macro_ptr
;
1036 saved_parse_flags
= parse_flags
;
1037 saved_macro_ptr
= macro_ptr
;
1039 tcc_open_bf(s1
, ":asm:", len
);
1040 memcpy(file
->buffer
, str
, len
);
1043 tcc_assemble_internal(s1
, 0, global
);
1046 parse_flags
= saved_parse_flags
;
1047 set_idnum('.', (parse_flags
& PARSE_FLAG_ASM_FILE
) ? IS_ID
: 0);
1048 macro_ptr
= saved_macro_ptr
;
1051 /* find a constraint by its number or id (gcc 3 extended
1052 syntax). return -1 if not found. Return in *pp in char after the
1054 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1055 const char *name
, const char **pp
)
1063 while (isnum(*name
)) {
1064 index
= (index
* 10) + (*name
) - '0';
1067 if ((unsigned)index
>= nb_operands
)
1069 } else if (*name
== '[') {
1071 p
= strchr(name
, ']');
1073 ts
= tok_alloc(name
, p
- name
);
1074 for(index
= 0; index
< nb_operands
; index
++) {
1075 if (operands
[index
].id
== ts
->tok
)
1092 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1094 CString
*out_str
, CString
*in_str
)
1096 int c
, index
, modifier
;
1111 if (*str
== 'c' || *str
== 'n' ||
1112 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1114 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1115 and make literal operands not be decorated with '$'. */
1118 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1120 tcc_error("invalid operand reference after %%");
1121 op
= &operands
[index
];
1125 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1128 subst_asm_operand(out_str
, &sv
, modifier
);
1131 cstr_ccat(out_str
, c
);
1139 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1146 nb_operands
= *nb_operands_ptr
;
1149 if (nb_operands
>= MAX_ASM_OPERANDS
)
1150 tcc_error("too many asm operands");
1151 op
= &operands
[nb_operands
++];
1155 if (tok
< TOK_IDENT
)
1156 expect("identifier");
1161 parse_mult_str(&astr
, "string constant");
1162 op
->constraint
= tcc_malloc(astr
.size
);
1163 strcpy(op
->constraint
, astr
.data
);
1168 if (!(vtop
->type
.t
& VT_ARRAY
))
1171 /* we want to avoid LLOCAL case, except when the 'm'
1172 constraint is used. Note that it may come from
1173 register storage, so we need to convert (reg)
1175 if ((vtop
->r
& VT_LVAL
) &&
1176 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1177 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1178 !strchr(op
->constraint
, 'm')) {
1190 *nb_operands_ptr
= nb_operands
;
1194 /* parse the GCC asm() instruction */
1195 ST_FUNC
void asm_instr(void)
1197 CString astr
, astr1
;
1198 ASMOperand operands
[MAX_ASM_OPERANDS
];
1199 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
;
1200 uint8_t clobber_regs
[NB_ASM_REGS
];
1203 /* since we always generate the asm() instruction, we can ignore
1205 if (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
) {
1208 parse_asm_str(&astr
);
1212 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1217 parse_asm_operands(operands
, &nb_operands
, 1);
1218 nb_outputs
= nb_operands
;
1223 parse_asm_operands(operands
, &nb_operands
, 0);
1226 /* XXX: handle registers */
1230 expect("string constant");
1231 asm_clobber(clobber_regs
, tokc
.str
.data
);
1244 /* NOTE: we do not eat the ';' so that we can restore the current
1245 token after the assembler parsing */
1249 /* save all values in the memory */
1252 /* compute constraints */
1253 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1254 clobber_regs
, &out_reg
);
1256 /* substitute the operands in the asm string. No substitution is
1257 done if no operands (GCC behaviour) */
1259 printf("asm: \"%s\"\n", (char *)astr
.data
);
1262 subst_asm_operands(operands
, nb_operands
, nb_outputs
, &astr1
, &astr
);
1268 printf("subst_asm: \"%s\"\n", (char *)astr1
.data
);
1271 /* generate loads */
1272 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1273 clobber_regs
, out_reg
);
1275 /* assemble the string with tcc internal assembler */
1276 tcc_assemble_inline(tcc_state
, astr1
.data
, astr1
.size
- 1, 0);
1278 /* restore the current C token */
1281 /* store the output values if needed */
1282 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1283 clobber_regs
, out_reg
);
1285 /* free everything */
1286 for(i
=0;i
<nb_operands
;i
++) {
1289 tcc_free(op
->constraint
);
1295 ST_FUNC
void asm_global_instr(void)
1298 int saved_nocode_wanted
= nocode_wanted
;
1300 /* Global asm blocks are always emitted. */
1303 parse_asm_str(&astr
);
1305 /* NOTE: we do not eat the ';' so that we can restore the current
1306 token after the assembler parsing */
1311 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1313 cur_text_section
= text_section
;
1314 ind
= cur_text_section
->data_offset
;
1316 /* assemble the string with tcc internal assembler */
1317 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1, 1);
1319 cur_text_section
->data_offset
= ind
;
1321 /* restore the current C token */
1325 nocode_wanted
= saved_nocode_wanted
;
1327 #endif /* CONFIG_TCC_ASM */