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
25 static Section
*last_text_section
; /* to handle .previous asm directive */
28 static int asm_get_prefix_name(TCCState
*s1
, const char *prefix
, unsigned int n
)
31 snprintf(buf
, sizeof(buf
), "%s%u", prefix
, n
);
32 return tok_alloc_const(buf
);
35 ST_FUNC
int asm_get_local_label_name(TCCState
*s1
, unsigned int n
)
37 return asm_get_prefix_name(s1
, "L..", n
);
40 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
);
41 static Sym
* asm_new_label(TCCState
*s1
, int label
, int is_local
);
42 static Sym
* asm_new_label1(TCCState
*s1
, int label
, int is_local
, int sh_num
, int value
);
44 /* If a C name has an _ prepended then only asm labels that start
45 with _ are representable in C, by removing the first _. ASM names
46 without _ at the beginning don't correspond to C names, but we use
47 the global C symbol table to track ASM names as well, so we need to
48 transform those into ones that don't conflict with a C name,
49 so prepend a '.' for them, but force the ELF asm name to be set. */
50 static int asm2cname(int v
, int *addeddot
)
54 if (!tcc_state
->leading_underscore
)
56 name
= get_tok_str(v
, NULL
);
60 v
= tok_alloc_const(name
+ 1);
61 } else if (!strchr(name
, '.')) {
63 snprintf(newname
, sizeof newname
, ".%s", name
);
64 v
= tok_alloc_const(newname
);
70 static Sym
*asm_label_find(int v
)
74 v
= asm2cname(v
, &addeddot
);
76 while (sym
&& sym
->sym_scope
&& !(sym
->type
.t
& VT_STATIC
))
81 static Sym
*asm_label_push(int v
)
83 int addeddot
, v2
= asm2cname(v
, &addeddot
);
84 /* We always add VT_EXTERN, for sym definition that's tentative
85 (for .set, removed for real defs), for mere references it's correct
87 Sym
*sym
= global_identifier_push(v2
, VT_ASM
| VT_EXTERN
| VT_STATIC
, 0);
93 /* Return a symbol we can use inside the assembler, having name NAME.
94 Symbols from asm and C source share a namespace. If we generate
95 an asm symbol it's also a (file-global) C symbol, but it's
96 either not accessible by name (like "L.123"), or its type information
97 is such that it's not usable without a proper C declaration.
99 Sometimes we need symbols accessible by name from asm, which
100 are anonymous in C, in this case CSYM can be used to transfer
101 all information from that symbol to the (possibly newly created)
103 ST_FUNC Sym
* get_asm_sym(int name
, Sym
*csym
)
105 Sym
*sym
= asm_label_find(name
);
107 sym
= asm_label_push(name
);
114 static Sym
* asm_section_sym(TCCState
*s1
, Section
*sec
)
116 char buf
[100]; int label
; Sym
*sym
;
117 snprintf(buf
, sizeof buf
, "L.%s", sec
->name
);
118 label
= tok_alloc_const(buf
);
119 sym
= asm_label_find(label
);
120 return sym
? sym
: asm_new_label1(s1
, label
, 1, sec
->sh_num
, 0);
123 /* We do not use the C expression parser to handle symbols. Maybe the
124 C expression parser could be tweaked to do so. */
126 static void asm_expr_unary(TCCState
*s1
, ExprValue
*pe
)
136 n
= strtoull(p
, (char **)&p
, 0);
137 if (*p
== 'b' || *p
== 'f') {
138 /* backward or forward label */
139 label
= asm_get_local_label_name(s1
, n
);
140 sym
= asm_label_find(label
);
142 /* backward : find the last corresponding defined label */
143 if (sym
&& (!sym
->c
|| elfsym(sym
)->st_shndx
== SHN_UNDEF
))
146 tcc_error("local label '%d' not found backward", (int)n
);
149 if (!sym
|| (sym
->c
&& elfsym(sym
)->st_shndx
!= SHN_UNDEF
)) {
150 /* if the last label is defined, then define a new one */
151 sym
= asm_label_push(label
);
157 } else if (*p
== '\0') {
162 tcc_error("invalid number syntax");
168 asm_expr_unary(s1
, pe
);
174 asm_expr_unary(s1
, pe
);
176 tcc_error("invalid operation with label");
196 pe
->sym
= asm_section_sym(s1
, cur_text_section
);
201 if (tok
>= TOK_IDENT
) {
203 /* label case : if the label was not found, add one */
204 sym
= get_asm_sym(tok
, NULL
);
206 if (esym
&& esym
->st_shndx
== SHN_ABS
) {
207 /* if absolute symbol, no need to put a symbol value */
208 pe
->v
= esym
->st_value
;
218 tcc_error("bad expression syntax [%s]", get_tok_str(tok
, &tokc
));
224 static void asm_expr_prod(TCCState
*s1
, ExprValue
*pe
)
229 asm_expr_unary(s1
, pe
);
232 if (op
!= '*' && op
!= '/' && op
!= '%' &&
233 op
!= TOK_SHL
&& op
!= TOK_SAR
)
236 asm_expr_unary(s1
, &e2
);
237 if (pe
->sym
|| e2
.sym
)
238 tcc_error("invalid operation with label");
246 tcc_error("division by zero");
266 static void asm_expr_logic(TCCState
*s1
, ExprValue
*pe
)
271 asm_expr_prod(s1
, pe
);
274 if (op
!= '&' && op
!= '|' && op
!= '^')
277 asm_expr_prod(s1
, &e2
);
278 if (pe
->sym
|| e2
.sym
)
279 tcc_error("invalid operation with label");
295 static inline void asm_expr_sum(TCCState
*s1
, ExprValue
*pe
)
300 asm_expr_logic(s1
, pe
);
303 if (op
!= '+' && op
!= '-')
306 asm_expr_logic(s1
, &e2
);
308 if (pe
->sym
!= NULL
&& e2
.sym
!= NULL
)
309 goto cannot_relocate
;
311 if (pe
->sym
== NULL
&& e2
.sym
!= NULL
)
315 /* NOTE: we are less powerful than gas in that case
316 because we store only one symbol in the expression */
319 } else if (pe
->sym
== e2
.sym
) {
321 pe
->sym
= NULL
; /* same symbols can be subtracted to NULL */
323 ElfSym
*esym1
, *esym2
;
324 esym1
= elfsym(pe
->sym
);
325 esym2
= elfsym(e2
.sym
);
327 goto cannot_relocate
;
328 if (esym1
&& esym1
->st_shndx
== esym2
->st_shndx
329 && esym1
->st_shndx
!= SHN_UNDEF
) {
330 /* we also accept defined symbols in the same section */
331 pe
->v
+= esym1
->st_value
- esym2
->st_value
;
333 } else if (esym2
->st_shndx
== cur_text_section
->sh_num
) {
334 /* When subtracting a defined symbol in current section
335 this actually makes the value PC-relative. */
336 pe
->v
+= 0 - esym2
->st_value
;
341 tcc_error("invalid operation with label");
348 static inline void asm_expr_cmp(TCCState
*s1
, ExprValue
*pe
)
353 asm_expr_sum(s1
, pe
);
356 if (op
!= TOK_EQ
&& op
!= TOK_NE
357 && (op
> TOK_GT
|| op
< TOK_ULE
))
360 asm_expr_sum(s1
, &e2
);
361 if (pe
->sym
|| e2
.sym
)
362 tcc_error("invalid operation with label");
365 pe
->v
= pe
->v
== e2
.v
;
368 pe
->v
= pe
->v
!= e2
.v
;
371 pe
->v
= (int64_t)pe
->v
< (int64_t)e2
.v
;
374 pe
->v
= (int64_t)pe
->v
>= (int64_t)e2
.v
;
377 pe
->v
= (int64_t)pe
->v
<= (int64_t)e2
.v
;
380 pe
->v
= (int64_t)pe
->v
> (int64_t)e2
.v
;
385 /* GAS compare results are -1/0 not 1/0. */
386 pe
->v
= -(int64_t)pe
->v
;
390 ST_FUNC
void asm_expr(TCCState
*s1
, ExprValue
*pe
)
392 asm_expr_cmp(s1
, pe
);
395 ST_FUNC
int asm_int_expr(TCCState
*s1
)
404 static Sym
* asm_new_label1(TCCState
*s1
, int label
, int is_local
,
405 int sh_num
, int value
)
410 sym
= asm_label_find(label
);
413 /* A VT_EXTERN symbol, even if it has a section is considered
414 overridable. This is how we "define" .set targets. Real
415 definitions won't have VT_EXTERN set. */
416 if (esym
&& esym
->st_shndx
!= SHN_UNDEF
) {
417 /* the label is already defined */
419 && (is_local
== 1 || (sym
->type
.t
& VT_EXTERN
)))
421 if (!(sym
->type
.t
& VT_EXTERN
))
422 tcc_error("assembler label '%s' already defined",
423 get_tok_str(label
, NULL
));
427 sym
= asm_label_push(label
);
430 put_extern_sym2(sym
, SHN_UNDEF
, 0, 0, 1);
432 esym
->st_shndx
= sh_num
;
433 esym
->st_value
= value
;
435 sym
->type
.t
&= ~VT_EXTERN
;
439 static Sym
* asm_new_label(TCCState
*s1
, int label
, int is_local
)
441 return asm_new_label1(s1
, label
, is_local
, cur_text_section
->sh_num
, ind
);
444 /* Set the value of LABEL to that of some expression (possibly
445 involving other symbols). LABEL can be overwritten later still. */
446 static Sym
* set_symbol(TCCState
*s1
, int label
)
455 esym
= elfsym(e
.sym
);
458 sym
= asm_new_label1(s1
, label
, 2, esym
? esym
->st_shndx
: SHN_ABS
, n
);
459 elfsym(sym
)->st_other
|= ST_ASM_SET
;
463 static void use_section1(TCCState
*s1
, Section
*sec
)
465 cur_text_section
->data_offset
= ind
;
466 cur_text_section
= sec
;
467 ind
= cur_text_section
->data_offset
;
470 static void use_section(TCCState
*s1
, const char *name
)
473 sec
= find_section(s1
, name
);
474 use_section1(s1
, sec
);
477 static void push_section(TCCState
*s1
, const char *name
)
479 Section
*sec
= find_section(s1
, name
);
480 sec
->prev
= cur_text_section
;
481 use_section1(s1
, sec
);
484 static void pop_section(TCCState
*s1
)
486 Section
*prev
= cur_text_section
->prev
;
488 tcc_error(".popsection without .pushsection");
489 cur_text_section
->prev
= NULL
;
490 use_section1(s1
, prev
);
493 static void asm_parse_directive(TCCState
*s1
, int global
)
495 int n
, offset
, v
, size
, tok1
;
499 /* assembler directive */
500 sec
= cur_text_section
;
502 case TOK_ASMDIR_align
:
503 case TOK_ASMDIR_balign
:
504 case TOK_ASMDIR_p2align
:
505 case TOK_ASMDIR_skip
:
506 case TOK_ASMDIR_space
:
509 n
= asm_int_expr(s1
);
510 if (tok1
== TOK_ASMDIR_p2align
)
513 tcc_error("invalid p2align, must be between 0 and 30");
515 tok1
= TOK_ASMDIR_align
;
517 if (tok1
== TOK_ASMDIR_align
|| tok1
== TOK_ASMDIR_balign
) {
518 if (n
< 0 || (n
& (n
-1)) != 0)
519 tcc_error("alignment must be a positive power of two");
520 offset
= (ind
+ n
- 1) & -n
;
522 /* the section must have a compatible alignment */
523 if (sec
->sh_addralign
< n
)
524 sec
->sh_addralign
= n
;
533 v
= asm_int_expr(s1
);
536 if (sec
->sh_type
!= SHT_NOBITS
) {
537 sec
->data_offset
= ind
;
538 ptr
= section_ptr_add(sec
, size
);
539 memset(ptr
, v
, size
);
543 case TOK_ASMDIR_quad
:
544 #ifdef TCC_TARGET_X86_64
554 if (tok
!= TOK_PPNUM
) {
556 tcc_error("64 bit constant");
558 vl
= strtoll(p
, (char **)&p
, 0);
562 if (sec
->sh_type
!= SHT_NOBITS
) {
563 /* XXX: endianness */
575 case TOK_ASMDIR_byte
:
578 case TOK_ASMDIR_word
:
579 case TOK_ASMDIR_short
:
582 case TOK_ASMDIR_long
:
590 if (sec
->sh_type
!= SHT_NOBITS
) {
593 #ifdef TCC_TARGET_X86_64
594 } else if (size
== 8) {
613 case TOK_ASMDIR_fill
:
615 int repeat
, size
, val
, i
, j
;
616 uint8_t repeat_buf
[8];
618 repeat
= asm_int_expr(s1
);
620 tcc_error("repeat < 0; .fill ignored");
627 size
= asm_int_expr(s1
);
629 tcc_error("size < 0; .fill ignored");
636 val
= asm_int_expr(s1
);
639 /* XXX: endianness */
641 repeat_buf
[1] = val
>> 8;
642 repeat_buf
[2] = val
>> 16;
643 repeat_buf
[3] = val
>> 24;
648 for(i
= 0; i
< repeat
; i
++) {
649 for(j
= 0; j
< size
; j
++) {
655 case TOK_ASMDIR_rept
:
658 TokenString
*init_str
;
660 repeat
= asm_int_expr(s1
);
661 init_str
= tok_str_alloc();
662 while (next(), tok
!= TOK_ASMDIR_endr
) {
664 tcc_error("we at end of file, .endr not found");
665 tok_str_add_tok(init_str
);
667 tok_str_add(init_str
, TOK_EOF
);
668 begin_macro(init_str
, 1);
669 while (repeat
-- > 0) {
670 tcc_assemble_internal(s1
, (parse_flags
& PARSE_FLAG_PREPROCESS
),
672 macro_ptr
= init_str
->str
;
686 esym
= elfsym(e
.sym
);
688 if (esym
->st_shndx
!= cur_text_section
->sh_num
)
689 expect("constant or same-section symbol");
693 tcc_error("attempt to .org backwards");
703 /* Also accept '.set stuff', but don't do anything with this.
704 It's used in GAS to set various features like '.set mips16'. */
706 set_symbol(s1
, tok1
);
708 case TOK_ASMDIR_globl
:
709 case TOK_ASMDIR_global
:
710 case TOK_ASMDIR_weak
:
711 case TOK_ASMDIR_hidden
:
716 sym
= get_asm_sym(tok
, NULL
);
717 if (tok1
!= TOK_ASMDIR_hidden
)
718 sym
->type
.t
&= ~VT_STATIC
;
719 if (tok1
== TOK_ASMDIR_weak
)
721 else if (tok1
== TOK_ASMDIR_hidden
)
722 sym
->a
.visibility
= STV_HIDDEN
;
725 } while (tok
== ',');
727 case TOK_ASMDIR_string
:
728 case TOK_ASMDIR_ascii
:
729 case TOK_ASMDIR_asciz
:
738 expect("string constant");
740 size
= tokc
.str
.size
;
741 if (t
== TOK_ASMDIR_ascii
&& size
> 0)
743 for(i
= 0; i
< size
; i
++)
748 } else if (tok
!= TOK_STR
) {
754 case TOK_ASMDIR_text
:
755 case TOK_ASMDIR_data
:
762 if (tok
!= ';' && tok
!= TOK_LINEFEED
) {
763 n
= asm_int_expr(s1
);
767 sprintf(sname
, "%s%d", get_tok_str(tok1
, NULL
), n
);
769 sprintf(sname
, "%s", get_tok_str(tok1
, NULL
));
770 use_section(s1
, sname
);
773 case TOK_ASMDIR_file
:
776 parse_flags
&= ~PARSE_FLAG_TOK_STR
;
778 if (tok
== TOK_PPNUM
)
780 if (tok
== TOK_PPSTR
&& tokc
.str
.data
[0] == '"') {
781 tokc
.str
.data
[tokc
.str
.size
- 2] = 0;
782 p
= tokc
.str
.data
+ 1;
783 } else if (tok
>= TOK_IDENT
) {
784 p
= get_tok_str(tok
, &tokc
);
793 case TOK_ASMDIR_ident
:
800 pstrcat(ident
, sizeof(ident
), tokc
.str
.data
);
802 pstrcat(ident
, sizeof(ident
), get_tok_str(tok
, NULL
));
803 tcc_warning_c(warn_unsupported
)("ignoring .ident %s", ident
);
807 case TOK_ASMDIR_size
:
812 sym
= asm_label_find(tok
);
814 tcc_error("label not found: %s", get_tok_str(tok
, NULL
));
816 /* XXX .size name,label2-label1 */
817 tcc_warning_c(warn_unsupported
)("ignoring .size %s,*", get_tok_str(tok
, NULL
));
820 while (tok
!= TOK_LINEFEED
&& tok
!= ';' && tok
!= CH_EOF
) {
825 case TOK_ASMDIR_type
:
831 sym
= get_asm_sym(tok
, NULL
);
834 if (tok
== TOK_STR
) {
835 newtype
= tokc
.str
.data
;
837 if (tok
== '@' || tok
== '%')
839 newtype
= get_tok_str(tok
, NULL
);
842 if (!strcmp(newtype
, "function") || !strcmp(newtype
, "STT_FUNC")) {
843 sym
->type
.t
= (sym
->type
.t
& ~VT_BTYPE
) | VT_FUNC
;
845 ElfSym
*esym
= elfsym(sym
);
846 esym
->st_info
= ELFW(ST_INFO
)(ELFW(ST_BIND
)(esym
->st_info
), STT_FUNC
);
849 tcc_warning_c(warn_unsupported
)("change type of '%s' from 0x%x to '%s' ignored",
850 get_tok_str(sym
->v
, NULL
), sym
->type
.t
, newtype
);
855 case TOK_ASMDIR_pushsection
:
856 case TOK_ASMDIR_section
:
859 int old_nb_section
= s1
->nb_sections
;
862 /* XXX: support more options */
865 while (tok
!= ';' && tok
!= TOK_LINEFEED
&& tok
!= ',') {
867 pstrcat(sname
, sizeof(sname
), tokc
.str
.data
);
869 pstrcat(sname
, sizeof(sname
), get_tok_str(tok
, NULL
));
873 /* skip section options */
876 expect("string constant");
880 if (tok
== '@' || tok
== '%')
885 last_text_section
= cur_text_section
;
886 if (tok1
== TOK_ASMDIR_section
) {
887 use_section(s1
, sname
);
888 /* The section directive supports flags, but they are unsupported.
889 For now, just assume any section contains code. */
890 cur_text_section
->sh_flags
|= SHF_EXECINSTR
;
893 push_section(s1
, sname
);
894 /* If we just allocated a new section reset its alignment to
895 1. new_section normally acts for GCC compatibility and
896 sets alignment to PTR_SIZE. The assembler behaves different. */
897 if (old_nb_section
!= s1
->nb_sections
)
898 cur_text_section
->sh_addralign
= 1;
901 case TOK_ASMDIR_previous
:
905 if (!last_text_section
)
906 tcc_error("no previous section referenced");
907 sec
= cur_text_section
;
908 use_section1(s1
, last_text_section
);
909 last_text_section
= sec
;
912 case TOK_ASMDIR_popsection
:
916 #ifdef TCC_TARGET_I386
917 case TOK_ASMDIR_code16
:
923 case TOK_ASMDIR_code32
:
930 #ifdef TCC_TARGET_X86_64
931 /* added for compatibility with GAS */
932 case TOK_ASMDIR_code64
:
936 #ifdef TCC_TARGET_RISCV64
937 case TOK_ASMDIR_option
:
940 case TOK_ASM_rvc
: /* Will be deprecated soon in favor of arch */
941 case TOK_ASM_norvc
: /* Will be deprecated soon in favor of arch */
945 case TOK_ASM_norelax
:
948 /* TODO: unimplemented */
952 /* TODO: unimplemented, requires extra parsing */
953 tcc_error("unimp .option '.%s'", get_tok_str(tok
, NULL
));
956 tcc_error("unknown .option '.%s'", get_tok_str(tok
, NULL
));
962 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok
, NULL
));
968 /* assemble a file */
969 static int tcc_assemble_internal(TCCState
*s1
, int do_preprocess
, int global
)
972 int saved_parse_flags
= parse_flags
;
974 parse_flags
= PARSE_FLAG_ASM_FILE
| PARSE_FLAG_TOK_STR
;
976 parse_flags
|= PARSE_FLAG_PREPROCESS
;
982 parse_flags
|= PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
985 /* horrible gas comment */
986 while (tok
!= TOK_LINEFEED
)
988 } else if (tok
>= TOK_ASMDIR_FIRST
&& tok
<= TOK_ASMDIR_LAST
) {
989 asm_parse_directive(s1
, global
);
990 } else if (tok
== TOK_PPNUM
) {
994 n
= strtoul(p
, (char **)&p
, 10);
997 /* new local label */
998 asm_new_label(s1
, asm_get_local_label_name(s1
, n
), 1);
1002 } else if (tok
>= TOK_IDENT
) {
1003 /* instruction or label */
1008 asm_new_label(s1
, opcode
, 0);
1011 } else if (tok
== '=') {
1012 set_symbol(s1
, opcode
);
1015 asm_opcode(s1
, opcode
);
1019 if (tok
!= ';' && tok
!= TOK_LINEFEED
)
1020 expect("end of line");
1021 parse_flags
&= ~PARSE_FLAG_LINEFEED
; /* XXX: suppress that hack */
1024 parse_flags
= saved_parse_flags
;
1028 /* Assemble the current file */
1029 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
1032 tcc_debug_start(s1
);
1033 /* default section is text */
1034 cur_text_section
= text_section
;
1035 ind
= cur_text_section
->data_offset
;
1037 ret
= tcc_assemble_internal(s1
, do_preprocess
, 1);
1038 cur_text_section
->data_offset
= ind
;
1043 /********************************************************************/
1044 /* GCC inline asm support */
1046 /* assemble the string 'str' in the current C compilation unit without
1048 static void tcc_assemble_inline(TCCState
*s1
, const char *str
, int len
, int global
)
1050 const int *saved_macro_ptr
= macro_ptr
;
1051 int dotid
= set_idnum('.', IS_ID
);
1052 #ifndef TCC_TARGET_RISCV64
1053 int dolid
= set_idnum('$', 0);
1056 tcc_open_bf(s1
, ":asm:", len
);
1057 memcpy(file
->buffer
, str
, len
);
1059 tcc_assemble_internal(s1
, 0, global
);
1062 #ifndef TCC_TARGET_RISCV64
1063 set_idnum('$', dolid
);
1065 set_idnum('.', dotid
);
1066 macro_ptr
= saved_macro_ptr
;
1069 /* find a constraint by its number or id (gcc 3 extended
1070 syntax). return -1 if not found. Return in *pp in char after the
1072 ST_FUNC
int find_constraint(ASMOperand
*operands
, int nb_operands
,
1073 const char *name
, const char **pp
)
1081 while (isnum(*name
)) {
1082 index
= (index
* 10) + (*name
) - '0';
1085 if ((unsigned)index
>= nb_operands
)
1087 } else if (*name
== '[') {
1089 p
= strchr(name
, ']');
1091 ts
= tok_alloc(name
, p
- name
);
1092 for(index
= 0; index
< nb_operands
; index
++) {
1093 if (operands
[index
].id
== ts
->tok
)
1110 static void subst_asm_operands(ASMOperand
*operands
, int nb_operands
,
1111 CString
*out_str
, const char *str
)
1113 int c
, index
, modifier
;
1125 if (*str
== 'c' || *str
== 'n' ||
1126 *str
== 'b' || *str
== 'w' || *str
== 'h' || *str
== 'k' ||
1127 *str
== 'q' || *str
== 'l' ||
1128 #ifdef TCC_TARGET_RISCV64
1131 /* P in GCC would add "@PLT" to symbol refs in PIC mode,
1132 and make literal operands not be decorated with '$'. */
1135 index
= find_constraint(operands
, nb_operands
, str
, &str
);
1137 tcc_error("invalid operand reference after %%");
1138 op
= &operands
[index
];
1139 if (modifier
== 'l') {
1140 cstr_cat(out_str
, get_tok_str(op
->is_label
, NULL
), -1);
1145 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& op
->is_memory
)
1148 subst_asm_operand(out_str
, &sv
, modifier
);
1152 cstr_ccat(out_str
, c
);
1160 static void parse_asm_operands(ASMOperand
*operands
, int *nb_operands_ptr
,
1168 nb_operands
= *nb_operands_ptr
;
1170 if (nb_operands
>= MAX_ASM_OPERANDS
)
1171 tcc_error("too many asm operands");
1172 op
= &operands
[nb_operands
++];
1176 if (tok
< TOK_IDENT
)
1177 expect("identifier");
1182 astr
= parse_mult_str("string constant")->data
;
1183 pstrcpy(op
->constraint
, sizeof op
->constraint
, astr
);
1187 if (!(vtop
->type
.t
& VT_ARRAY
))
1190 /* we want to avoid LLOCAL case, except when the 'm'
1191 constraint is used. Note that it may come from
1192 register storage, so we need to convert (reg)
1194 if ((vtop
->r
& VT_LVAL
) &&
1195 ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
||
1196 (vtop
->r
& VT_VALMASK
) < VT_CONST
) &&
1197 !strchr(op
->constraint
, 'm')) {
1209 *nb_operands_ptr
= nb_operands
;
1213 /* parse the GCC asm() instruction */
1214 ST_FUNC
void asm_instr(void)
1216 CString astr
, *astr1
;
1218 ASMOperand operands
[MAX_ASM_OPERANDS
];
1219 int nb_outputs
, nb_operands
, i
, must_subst
, out_reg
, nb_labels
;
1220 uint8_t clobber_regs
[NB_ASM_REGS
];
1223 /* since we always generate the asm() instruction, we can ignore
1225 while (tok
== TOK_VOLATILE1
|| tok
== TOK_VOLATILE2
|| tok
== TOK_VOLATILE3
1226 || tok
== TOK_GOTO
) {
1230 astr1
= parse_asm_str();
1232 cstr_cat(&astr
, astr1
->data
, astr1
->size
);
1238 memset(clobber_regs
, 0, sizeof(clobber_regs
));
1243 parse_asm_operands(operands
, &nb_operands
, 1);
1244 nb_outputs
= nb_operands
;
1249 parse_asm_operands(operands
, &nb_operands
, 0);
1252 /* XXX: handle registers */
1258 expect("string constant");
1259 asm_clobber(clobber_regs
, tokc
.str
.data
);
1274 if (nb_operands
+ nb_labels
>= MAX_ASM_OPERANDS
)
1275 tcc_error("too many asm operands");
1276 if (tok
< TOK_UIDENT
)
1277 expect("label identifier");
1278 operands
[nb_operands
+ nb_labels
++].id
= tok
;
1280 csym
= label_find(tok
);
1282 csym
= label_push(&global_label_stack
, tok
,
1285 if (csym
->r
== LABEL_DECLARED
)
1286 csym
->r
= LABEL_FORWARD
;
1289 asmname
= asm_get_prefix_name(tcc_state
, "LG.",
1292 put_extern_sym2(csym
, SHN_UNDEF
, 0, 0, 1);
1293 get_asm_sym(asmname
, csym
);
1294 operands
[nb_operands
+ nb_labels
- 1].is_label
= asmname
;
1305 /* NOTE: we do not eat the ';' so that we can restore the current
1306 token after the assembler parsing */
1310 /* save all values in the memory */
1313 /* compute constraints */
1314 asm_compute_constraints(operands
, nb_operands
, nb_outputs
,
1315 clobber_regs
, &out_reg
);
1317 /* substitute the operands in the asm string. No substitution is
1318 done if no operands (GCC behaviour) */
1320 printf("asm: \"%s\"\n", (char *)astr
.data
);
1324 cstr_cat(astr1
, astr
.data
, astr
.size
);
1326 subst_asm_operands(operands
, nb_operands
+ nb_labels
, &astr
, astr1
->data
);
1330 printf("subst_asm: \"%s\"\n", (char *)astr
.data
);
1333 /* generate loads */
1334 asm_gen_code(operands
, nb_operands
, nb_outputs
, 0,
1335 clobber_regs
, out_reg
);
1337 /* We don't allow switching section within inline asm to
1338 bleed out to surrounding code. */
1339 sec
= cur_text_section
;
1340 /* assemble the string with tcc internal assembler */
1341 tcc_assemble_inline(tcc_state
, astr
.data
, astr
.size
- 1, 0);
1343 if (sec
!= cur_text_section
) {
1344 tcc_warning("inline asm tries to change current section");
1345 use_section1(tcc_state
, sec
);
1348 /* restore the current C token */
1351 /* store the output values if needed */
1352 asm_gen_code(operands
, nb_operands
, nb_outputs
, 1,
1353 clobber_regs
, out_reg
);
1355 /* free everything */
1356 for(i
=0;i
<nb_operands
;i
++) {
1362 ST_FUNC
void asm_global_instr(void)
1365 int saved_nocode_wanted
= nocode_wanted
;
1367 /* Global asm blocks are always emitted. */
1370 astr
= parse_asm_str();
1372 /* NOTE: we do not eat the ';' so that we can restore the current
1373 token after the assembler parsing */
1378 printf("asm_global: \"%s\"\n", (char *)astr
.data
);
1380 cur_text_section
= text_section
;
1381 ind
= cur_text_section
->data_offset
;
1383 /* assemble the string with tcc internal assembler */
1384 tcc_assemble_inline(tcc_state
, astr
->data
, astr
->size
- 1, 1);
1386 cur_text_section
->data_offset
= ind
;
1388 /* restore the current C token */
1391 nocode_wanted
= saved_nocode_wanted
;
1394 /********************************************************/
1396 ST_FUNC
int tcc_assemble(TCCState
*s1
, int do_preprocess
)
1398 tcc_error("asm not supported");
1401 ST_FUNC
void asm_instr(void)
1403 tcc_error("inline asm() not supported");
1406 ST_FUNC
void asm_global_instr(void)
1408 tcc_error("inline asm() not supported");
1410 #endif /* CONFIG_TCC_ASM */