fix-mixed-struct (patch by Pip Cet)
[tinycc.git] / tccasm.c
Commit [+]AuthorDateLineData
7893a9ec bellard2003-01-06 20:21:42 +00001/*
2 * GAS like assembler for TCC
3 *
c9c05ca5 bellard2004-10-27 21:38:03 +00004 * Copyright (c) 2001-2004 Fabrice Bellard
7893a9ec bellard2003-01-06 20:21:42 +00005 *
8f5e44a4 bellard2003-05-24 14:11:17 +00006 * 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.
7893a9ec bellard2003-01-06 20:21:42 +000010 *
8f5e44a4 bellard2003-05-24 14:11:17 +000011 * 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.
7893a9ec bellard2003-01-06 20:21:42 +000015 *
8f5e44a4 bellard2003-05-24 14:11:17 +000016 * 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
7893a9ec bellard2003-01-06 20:21:42 +000019 */
20
88a3ccab grischka2009-12-20 01:53:49 +010021#include "tcc.h"
519a9040 Thomas Preud'homme2012-01-06 18:14:34 +010022#ifdef CONFIG_TCC_ASM
88a3ccab grischka2009-12-20 01:53:49 +010023
24ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
7893a9ec bellard2003-01-06 20:21:42 +000025{
26 char buf[64];
27 TokenSym *ts;
28
29 snprintf(buf, sizeof(buf), "L..%u", n);
30 ts = tok_alloc(buf, strlen(buf));
31 return ts->tok;
32}
33
88a3ccab grischka2009-12-20 01:53:49 +010034ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
0bd402d2 bellard2004-10-18 00:19:51 +000035
7893a9ec bellard2003-01-06 20:21:42 +000036/* We do not use the C expression parser to handle symbols. Maybe the
37 C expression parser could be tweaked to do so. */
38
39static void asm_expr_unary(TCCState *s1, ExprValue *pe)
40{
41 Sym *sym;
42 int op, n, label;
43 const char *p;
44
45 switch(tok) {
46 case TOK_PPNUM:
47 p = tokc.cstr->data;
0eef2354 bellard2003-04-21 15:21:19 +000048 n = strtoul(p, (char **)&p, 0);
7893a9ec bellard2003-01-06 20:21:42 +000049 if (*p == 'b' || *p == 'f') {
50 /* backward or forward label */
51 label = asm_get_local_label_name(s1, n);
52 sym = label_find(label);
53 if (*p == 'b') {
54 /* backward : find the last corresponding defined label */
55 if (sym && sym->r == 0)
56 sym = sym->prev_tok;
57 if (!sym)
bf374a5f grischka2011-08-11 17:07:56 +020058 tcc_error("local label '%d' not found backward", n);
7893a9ec bellard2003-01-06 20:21:42 +000059 } else {
60 /* forward */
61 if (!sym || sym->r) {
62 /* if the last label is defined, then define a new one */
63 sym = label_push(&s1->asm_labels, label, 0);
64 sym->type.t = VT_STATIC | VT_VOID;
65 }
66 }
67 pe->v = 0;
68 pe->sym = sym;
69 } else if (*p == '\0') {
70 pe->v = n;
71 pe->sym = NULL;
72 } else {
bf374a5f grischka2011-08-11 17:07:56 +020073 tcc_error("invalid number syntax");
7893a9ec bellard2003-01-06 20:21:42 +000074 }
75 next();
76 break;
77 case '+':
78 next();
79 asm_expr_unary(s1, pe);
80 break;
81 case '-':
82 case '~':
83 op = tok;
84 next();
85 asm_expr_unary(s1, pe);
86 if (pe->sym)
bf374a5f grischka2011-08-11 17:07:56 +020087 tcc_error("invalid operation with label");
7893a9ec bellard2003-01-06 20:21:42 +000088 if (op == '-')
89 pe->v = -pe->v;
90 else
91 pe->v = ~pe->v;
92 break;
71f119fe bellard2003-04-28 21:23:53 +000093 case TOK_CCHAR:
94 case TOK_LCHAR:
95 pe->v = tokc.i;
96 pe->sym = NULL;
97 next();
98 break;
0bd402d2 bellard2004-10-18 00:19:51 +000099 case '(':
100 next();
101 asm_expr(s1, pe);
102 skip(')');
103 break;
7893a9ec bellard2003-01-06 20:21:42 +0000104 default:
105 if (tok >= TOK_IDENT) {
106 /* label case : if the label was not found, add one */
107 sym = label_find(tok);
108 if (!sym) {
109 sym = label_push(&s1->asm_labels, tok, 0);
110 /* NOTE: by default, the symbol is global */
111 sym->type.t = VT_VOID;
112 }
0bd402d2 bellard2004-10-18 00:19:51 +0000113 if (sym->r == SHN_ABS) {
114 /* if absolute symbol, no need to put a symbol value */
f88350b6 grischka2009-07-18 22:07:51 +0200115 pe->v = sym->jnext;
0bd402d2 bellard2004-10-18 00:19:51 +0000116 pe->sym = NULL;
117 } else {
118 pe->v = 0;
119 pe->sym = sym;
120 }
7893a9ec bellard2003-01-06 20:21:42 +0000121 next();
122 } else {
bf374a5f grischka2011-08-11 17:07:56 +0200123 tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
7893a9ec bellard2003-01-06 20:21:42 +0000124 }
125 break;
126 }
127}
128
129static void asm_expr_prod(TCCState *s1, ExprValue *pe)
130{
131 int op;
132 ExprValue e2;
133
134 asm_expr_unary(s1, pe);
135 for(;;) {
136 op = tok;
137 if (op != '*' && op != '/' && op != '%' &&
138 op != TOK_SHL && op != TOK_SAR)
139 break;
140 next();
141 asm_expr_unary(s1, &e2);
142 if (pe->sym || e2.sym)
bf374a5f grischka2011-08-11 17:07:56 +0200143 tcc_error("invalid operation with label");
7893a9ec bellard2003-01-06 20:21:42 +0000144 switch(op) {
145 case '*':
146 pe->v *= e2.v;
147 break;
148 case '/':
149 if (e2.v == 0) {
150 div_error:
bf374a5f grischka2011-08-11 17:07:56 +0200151 tcc_error("division by zero");
7893a9ec bellard2003-01-06 20:21:42 +0000152 }
153 pe->v /= e2.v;
154 break;
155 case '%':
156 if (e2.v == 0)
157 goto div_error;
158 pe->v %= e2.v;
159 break;
160 case TOK_SHL:
161 pe->v <<= e2.v;
162 break;
163 default:
164 case TOK_SAR:
165 pe->v >>= e2.v;
166 break;
167 }
168 }
169}
170
171static void asm_expr_logic(TCCState *s1, ExprValue *pe)
172{
173 int op;
174 ExprValue e2;
175
176 asm_expr_prod(s1, pe);
177 for(;;) {
178 op = tok;
179 if (op != '&' && op != '|' && op != '^')
180 break;
181 next();
182 asm_expr_prod(s1, &e2);
183 if (pe->sym || e2.sym)
bf374a5f grischka2011-08-11 17:07:56 +0200184 tcc_error("invalid operation with label");
7893a9ec bellard2003-01-06 20:21:42 +0000185 switch(op) {
186 case '&':
187 pe->v &= e2.v;
188 break;
189 case '|':
190 pe->v |= e2.v;
191 break;
192 default:
193 case '^':
194 pe->v ^= e2.v;
195 break;
196 }
197 }
198}
199
200static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
201{
202 int op;
203 ExprValue e2;
204
205 asm_expr_logic(s1, pe);
206 for(;;) {
207 op = tok;
208 if (op != '+' && op != '-')
209 break;
210 next();
211 asm_expr_logic(s1, &e2);
212 if (op == '+') {
213 if (pe->sym != NULL && e2.sym != NULL)
214 goto cannot_relocate;
215 pe->v += e2.v;
216 if (pe->sym == NULL && e2.sym != NULL)
217 pe->sym = e2.sym;
218 } else {
219 pe->v -= e2.v;
220 /* NOTE: we are less powerful than gas in that case
221 because we store only one symbol in the expression */
222 if (!pe->sym && !e2.sym) {
223 /* OK */
224 } else if (pe->sym && !e2.sym) {
225 /* OK */
226 } else if (pe->sym && e2.sym) {
227 if (pe->sym == e2.sym) {
228 /* OK */
229 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
230 /* we also accept defined symbols in the same section */
f88350b6 grischka2009-07-18 22:07:51 +0200231 pe->v += pe->sym->jnext - e2.sym->jnext;
7893a9ec bellard2003-01-06 20:21:42 +0000232 } else {
233 goto cannot_relocate;
234 }
3e9a7e9d Vincent Lefevre2014-04-07 13:31:00 +0200235 pe->sym = NULL; /* same symbols can be subtracted to NULL */
7893a9ec bellard2003-01-06 20:21:42 +0000236 } else {
237 cannot_relocate:
bf374a5f grischka2011-08-11 17:07:56 +0200238 tcc_error("invalid operation with label");
7893a9ec bellard2003-01-06 20:21:42 +0000239 }
240 }
241 }
242}
243
88a3ccab grischka2009-12-20 01:53:49 +0100244ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
7893a9ec bellard2003-01-06 20:21:42 +0000245{
246 asm_expr_sum(s1, pe);
247}
248
88a3ccab grischka2009-12-20 01:53:49 +0100249ST_FUNC int asm_int_expr(TCCState *s1)
7893a9ec bellard2003-01-06 20:21:42 +0000250{
251 ExprValue e;
252 asm_expr(s1, &e);
253 if (e.sym)
254 expect("constant");
255 return e.v;
256}
257
258/* NOTE: the same name space as C labels is used to avoid using too
259 much memory when storing labels in TokenStrings */
0bd402d2 bellard2004-10-18 00:19:51 +0000260static void asm_new_label1(TCCState *s1, int label, int is_local,
261 int sh_num, int value)
7893a9ec bellard2003-01-06 20:21:42 +0000262{
263 Sym *sym;
264
265 sym = label_find(label);
266 if (sym) {
267 if (sym->r) {
268 /* the label is already defined */
269 if (!is_local) {
bf374a5f grischka2011-08-11 17:07:56 +0200270 tcc_error("assembler label '%s' already defined",
7893a9ec bellard2003-01-06 20:21:42 +0000271 get_tok_str(label, NULL));
272 } else {
273 /* redefinition of local labels is possible */
274 goto new_label;
275 }
276 }
277 } else {
278 new_label:
279 sym = label_push(&s1->asm_labels, label, 0);
280 sym->type.t = VT_STATIC | VT_VOID;
281 }
0bd402d2 bellard2004-10-18 00:19:51 +0000282 sym->r = sh_num;
f88350b6 grischka2009-07-18 22:07:51 +0200283 sym->jnext = value;
0bd402d2 bellard2004-10-18 00:19:51 +0000284}
285
286static void asm_new_label(TCCState *s1, int label, int is_local)
287{
288 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
7893a9ec bellard2003-01-06 20:21:42 +0000289}
290
291static void asm_free_labels(TCCState *st)
292{
293 Sym *s, *s1;
0bd402d2 bellard2004-10-18 00:19:51 +0000294 Section *sec;
0c8447db seyko2015-04-21 06:34:35 +0300295
7893a9ec bellard2003-01-06 20:21:42 +0000296 for(s = st->asm_labels; s != NULL; s = s1) {
297 s1 = s->prev;
298 /* define symbol value in object file */
299 if (s->r) {
0bd402d2 bellard2004-10-18 00:19:51 +0000300 if (s->r == SHN_ABS)
301 sec = SECTION_ABS;
302 else
303 sec = st->sections[s->r];
f88350b6 grischka2009-07-18 22:07:51 +0200304 put_extern_sym2(s, sec, s->jnext, 0, 0);
7893a9ec bellard2003-01-06 20:21:42 +0000305 }
306 /* remove label */
307 table_ident[s->v - TOK_IDENT]->sym_label = NULL;
be5e9cb9 bellard2004-11-07 15:45:40 +0000308 sym_free(s);
7893a9ec bellard2003-01-06 20:21:42 +0000309 }
310 st->asm_labels = NULL;
311}
312
0bd402d2 bellard2004-10-18 00:19:51 +0000313static void use_section1(TCCState *s1, Section *sec)
71f119fe bellard2003-04-28 21:23:53 +0000314{
71f119fe bellard2003-04-28 21:23:53 +0000315 cur_text_section->data_offset = ind;
316 cur_text_section = sec;
317 ind = cur_text_section->data_offset;
318}
319
0bd402d2 bellard2004-10-18 00:19:51 +0000320static void use_section(TCCState *s1, const char *name)
321{
322 Section *sec;
323 sec = find_section(s1, name);
324 use_section1(s1, sec);
325}
326
7893a9ec bellard2003-01-06 20:21:42 +0000327static void asm_parse_directive(TCCState *s1)
328{
329 int n, offset, v, size, tok1;
330 Section *sec;
331 uint8_t *ptr;
332
333 /* assembler directive */
334 next();
335 sec = cur_text_section;
336 switch(tok) {
337 case TOK_ASM_align:
ff783b94 Reimar Döffinger2015-01-20 08:49:49 +0100338 case TOK_ASM_p2align:
7893a9ec bellard2003-01-06 20:21:42 +0000339 case TOK_ASM_skip:
340 case TOK_ASM_space:
341 tok1 = tok;
342 next();
343 n = asm_int_expr(s1);
ff783b94
RD
Reimar Döffinger2015-01-20 08:49:49 +0100344 if (tok1 == TOK_ASM_p2align)
345 {
346 if (n < 0 || n > 30)
347 tcc_error("invalid p2align, must be between 0 and 30");
348 n = 1 << n;
349 tok1 = TOK_ASM_align;
350 }
7893a9ec bellard2003-01-06 20:21:42 +0000351 if (tok1 == TOK_ASM_align) {
352 if (n < 0 || (n & (n-1)) != 0)
bf374a5f grischka2011-08-11 17:07:56 +0200353 tcc_error("alignment must be a positive power of two");
7893a9ec bellard2003-01-06 20:21:42 +0000354 offset = (ind + n - 1) & -n;
355 size = offset - ind;
0bd402d2 bellard2004-10-18 00:19:51 +0000356 /* the section must have a compatible alignment */
357 if (sec->sh_addralign < n)
358 sec->sh_addralign = n;
7893a9ec bellard2003-01-06 20:21:42 +0000359 } else {
360 size = n;
361 }
362 v = 0;
363 if (tok == ',') {
364 next();
365 v = asm_int_expr(s1);
366 }
0bd402d2 bellard2004-10-18 00:19:51 +0000367 zero_pad:
7893a9ec bellard2003-01-06 20:21:42 +0000368 if (sec->sh_type != SHT_NOBITS) {
369 sec->data_offset = ind;
370 ptr = section_ptr_add(sec, size);
371 memset(ptr, v, size);
372 }
373 ind += size;
374 break;
e657dfb4 bellard2004-10-25 18:54:29 +0000375 case TOK_ASM_quad:
376 next();
377 for(;;) {
378 uint64_t vl;
379 const char *p;
380
381 p = tokc.cstr->data;
382 if (tok != TOK_PPNUM) {
383 error_constant:
bf374a5f grischka2011-08-11 17:07:56 +0200384 tcc_error("64 bit constant");
e657dfb4 bellard2004-10-25 18:54:29 +0000385 }
386 vl = strtoll(p, (char **)&p, 0);
387 if (*p != '\0')
388 goto error_constant;
389 next();
390 if (sec->sh_type != SHT_NOBITS) {
391 /* XXX: endianness */
392 gen_le32(vl);
393 gen_le32(vl >> 32);
394 } else {
395 ind += 8;
396 }
397 if (tok != ',')
398 break;
399 next();
400 }
401 break;
7893a9ec bellard2003-01-06 20:21:42 +0000402 case TOK_ASM_byte:
403 size = 1;
404 goto asm_data;
405 case TOK_ASM_word:
406 case TOK_SHORT:
407 size = 2;
408 goto asm_data;
409 case TOK_LONG:
410 case TOK_INT:
411 size = 4;
412 asm_data:
413 next();
414 for(;;) {
415 ExprValue e;
416 asm_expr(s1, &e);
417 if (sec->sh_type != SHT_NOBITS) {
418 if (size == 4) {
419 gen_expr32(&e);
420 } else {
421 if (e.sym)
422 expect("constant");
423 if (size == 1)
424 g(e.v);
425 else
426 gen_le16(e.v);
427 }
428 } else {
429 ind += size;
430 }
431 if (tok != ',')
432 break;
433 next();
434 }
435 break;
0bd402d2 bellard2004-10-18 00:19:51 +0000436 case TOK_ASM_fill:
437 {
438 int repeat, size, val, i, j;
439 uint8_t repeat_buf[8];
440 next();
441 repeat = asm_int_expr(s1);
442 if (repeat < 0) {
bf374a5f grischka2011-08-11 17:07:56 +0200443 tcc_error("repeat < 0; .fill ignored");
0bd402d2 bellard2004-10-18 00:19:51 +0000444 break;
445 }
446 size = 1;
447 val = 0;
448 if (tok == ',') {
449 next();
450 size = asm_int_expr(s1);
451 if (size < 0) {
bf374a5f grischka2011-08-11 17:07:56 +0200452 tcc_error("size < 0; .fill ignored");
0bd402d2 bellard2004-10-18 00:19:51 +0000453 break;
454 }
455 if (size > 8)
456 size = 8;
457 if (tok == ',') {
458 next();
459 val = asm_int_expr(s1);
460 }
461 }
462 /* XXX: endianness */
463 repeat_buf[0] = val;
464 repeat_buf[1] = val >> 8;
465 repeat_buf[2] = val >> 16;
466 repeat_buf[3] = val >> 24;
467 repeat_buf[4] = 0;
468 repeat_buf[5] = 0;
469 repeat_buf[6] = 0;
470 repeat_buf[7] = 0;
471 for(i = 0; i < repeat; i++) {
472 for(j = 0; j < size; j++) {
473 g(repeat_buf[j]);
474 }
475 }
476 }
477 break;
478 case TOK_ASM_org:
479 {
480 unsigned long n;
481 next();
482 /* XXX: handle section symbols too */
483 n = asm_int_expr(s1);
484 if (n < ind)
bf374a5f grischka2011-08-11 17:07:56 +0200485 tcc_error("attempt to .org backwards");
0bd402d2 bellard2004-10-18 00:19:51 +0000486 v = 0;
487 size = n - ind;
488 goto zero_pad;
489 }
490 break;
71f119fe bellard2003-04-28 21:23:53 +0000491 case TOK_ASM_globl:
8da6027e bellard2004-10-02 14:01:26 +0000492 case TOK_ASM_global:
c59d3426 Joe Soroka2011-02-01 08:43:54 -0800493 case TOK_ASM_weak:
a9fda392 Michael Matz2014-04-14 03:33:50 +0200494 case TOK_ASM_hidden:
c59d3426 Joe Soroka2011-02-01 08:43:54 -0800495 tok1 = tok;
f43fafc6 Joe Soroka2011-01-20 02:00:50 -0800496 do {
71f119fe bellard2003-04-28 21:23:53 +0000497 Sym *sym;
498
499 next();
500 sym = label_find(tok);
501 if (!sym) {
502 sym = label_push(&s1->asm_labels, tok, 0);
503 sym->type.t = VT_VOID;
504 }
a9fda392
MM
Michael Matz2014-04-14 03:33:50 +0200505 if (tok1 != TOK_ASM_hidden)
506 sym->type.t &= ~VT_STATIC;
c59d3426
JS
Joe Soroka2011-02-01 08:43:54 -0800507 if (tok1 == TOK_ASM_weak)
508 sym->type.t |= VT_WEAK;
a9fda392
MM
Michael Matz2014-04-14 03:33:50 +0200509 else if (tok1 == TOK_ASM_hidden)
510 sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
71f119fe bellard2003-04-28 21:23:53 +0000511 next();
f43fafc6 Joe Soroka2011-01-20 02:00:50 -0800512 } while (tok == ',');
71f119fe bellard2003-04-28 21:23:53 +0000513 break;
514 case TOK_ASM_string:
eb794711 bellard2004-10-23 22:52:42 +0000515 case TOK_ASM_ascii:
516 case TOK_ASM_asciz:
71f119fe bellard2003-04-28 21:23:53 +0000517 {
518 const uint8_t *p;
eb794711 bellard2004-10-23 22:52:42 +0000519 int i, size, t;
71f119fe bellard2003-04-28 21:23:53 +0000520
eb794711 bellard2004-10-23 22:52:42 +0000521 t = tok;
71f119fe bellard2003-04-28 21:23:53 +0000522 next();
eb794711 bellard2004-10-23 22:52:42 +0000523 for(;;) {
524 if (tok != TOK_STR)
525 expect("string constant");
526 p = tokc.cstr->data;
527 size = tokc.cstr->size;
528 if (t == TOK_ASM_ascii && size > 0)
529 size--;
530 for(i = 0; i < size; i++)
531 g(p[i]);
532 next();
533 if (tok == ',') {
534 next();
535 } else if (tok != TOK_STR) {
536 break;
537 }
538 }
71f119fe bellard2003-04-28 21:23:53 +0000539 }
540 break;
541 case TOK_ASM_text:
542 case TOK_ASM_data:
543 case TOK_ASM_bss:
544 {
545 char sname[64];
546 tok1 = tok;
547 n = 0;
548 next();
549 if (tok != ';' && tok != TOK_LINEFEED) {
550 n = asm_int_expr(s1);
551 next();
552 }
553 sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
554 use_section(s1, sname);
555 }
556 break;
9ff7a0bc
DR
Detlef Riekenberg2010-04-05 12:19:49 +0200557 case TOK_ASM_file:
558 {
559 char filename[512];
560
561 filename[0] = '\0';
562 next();
563
564 if (tok == TOK_STR)
565 pstrcat(filename, sizeof(filename), tokc.cstr->data);
566 else
567 pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
568
569 if (s1->warn_unsupported)
bf374a5f grischka2011-08-11 17:07:56 +0200570 tcc_warning("ignoring .file %s", filename);
9ff7a0bc
DR
Detlef Riekenberg2010-04-05 12:19:49 +0200571
572 next();
573 }
574 break;
a135dd50
DR
Detlef Riekenberg2010-04-05 12:45:52 +0200575 case TOK_ASM_ident:
576 {
577 char ident[256];
578
579 ident[0] = '\0';
580 next();
581
582 if (tok == TOK_STR)
583 pstrcat(ident, sizeof(ident), tokc.cstr->data);
584 else
585 pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
586
587 if (s1->warn_unsupported)
bf374a5f grischka2011-08-11 17:07:56 +0200588 tcc_warning("ignoring .ident %s", ident);
a135dd50
DR
Detlef Riekenberg2010-04-05 12:45:52 +0200589
590 next();
591 }
592 break;
558258a3
DR
Detlef Riekenberg2010-04-05 12:43:51 +0200593 case TOK_ASM_size:
594 {
595 Sym *sym;
596
597 next();
598 sym = label_find(tok);
599 if (!sym) {
bf374a5f grischka2011-08-11 17:07:56 +0200600 tcc_error("label not found: %s", get_tok_str(tok, NULL));
558258a3
DR
Detlef Riekenberg2010-04-05 12:43:51 +0200601 }
602
558258a3
DR
Detlef Riekenberg2010-04-05 12:43:51 +0200603 /* XXX .size name,label2-label1 */
604 if (s1->warn_unsupported)
bf374a5f grischka2011-08-11 17:07:56 +0200605 tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
558258a3 Detlef Riekenberg2010-04-05 12:43:51 +0200606
a9fda392
MM
Michael Matz2014-04-14 03:33:50 +0200607 next();
608 skip(',');
558258a3
DR
Detlef Riekenberg2010-04-05 12:43:51 +0200609 while (tok != '\n' && tok != CH_EOF) {
610 next();
611 }
612 }
613 break;
6825c5db
DR
Detlef Riekenberg2010-04-05 12:31:45 +0200614 case TOK_ASM_type:
615 {
616 Sym *sym;
bec84fa0 Joe Soroka2011-02-24 09:24:02 -0800617 const char *newtype;
6825c5db
DR
Detlef Riekenberg2010-04-05 12:31:45 +0200618
619 next();
620 sym = label_find(tok);
621 if (!sym) {
622 sym = label_push(&s1->asm_labels, tok, 0);
623 sym->type.t = VT_VOID;
624 }
625
626 next();
627 skip(',');
bec84fa0
JS
Joe Soroka2011-02-24 09:24:02 -0800628 if (tok == TOK_STR) {
629 newtype = tokc.cstr->data;
630 } else {
631 if (tok == '@' || tok == '%')
632 skip(tok);
633 newtype = get_tok_str(tok, NULL);
634 }
6825c5db Detlef Riekenberg2010-04-05 12:31:45 +0200635
bec84fa0 Joe Soroka2011-02-24 09:24:02 -0800636 if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
a9fda392 Michael Matz2014-04-14 03:33:50 +0200637 sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
6825c5db
DR
Detlef Riekenberg2010-04-05 12:31:45 +0200638 }
639 else if (s1->warn_unsupported)
bf374a5f grischka2011-08-11 17:07:56 +0200640 tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
6825c5db
DR
Detlef Riekenberg2010-04-05 12:31:45 +0200641 get_tok_str(sym->v, NULL), sym->type.t, newtype);
642
643 next();
644 }
645 break;
71f119fe bellard2003-04-28 21:23:53 +0000646 case TOK_SECTION1:
647 {
648 char sname[256];
649
650 /* XXX: support more options */
651 next();
652 sname[0] = '\0';
653 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
654 if (tok == TOK_STR)
655 pstrcat(sname, sizeof(sname), tokc.cstr->data);
656 else
657 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
658 next();
659 }
0bd402d2 bellard2004-10-18 00:19:51 +0000660 if (tok == ',') {
661 /* skip section options */
662 next();
663 if (tok != TOK_STR)
664 expect("string constant");
665 next();
666 }
667 last_text_section = cur_text_section;
71f119fe bellard2003-04-28 21:23:53 +0000668 use_section(s1, sname);
669 }
670 break;
0bd402d2 bellard2004-10-18 00:19:51 +0000671 case TOK_ASM_previous:
672 {
673 Section *sec;
674 next();
675 if (!last_text_section)
bf374a5f grischka2011-08-11 17:07:56 +0200676 tcc_error("no previous section referenced");
0bd402d2 bellard2004-10-18 00:19:51 +0000677 sec = cur_text_section;
678 use_section1(s1, last_text_section);
679 last_text_section = sec;
680 }
681 break;
0d768b97
FF
Frederic Feret2009-08-27 09:34:35 +0200682#ifdef TCC_TARGET_I386
683 case TOK_ASM_code16:
684 {
685 next();
686 s1->seg_size = 16;
687 }
688 break;
689 case TOK_ASM_code32:
690 {
691 next();
692 s1->seg_size = 32;
693 }
694 break;
695#endif
526c4645
FF
Frederic Feret2009-08-27 09:53:50 +0200696#ifdef TCC_TARGET_X86_64
697 /* added for compatibility with GAS */
698 case TOK_ASM_code64:
699 next();
700 break;
701#endif
7893a9ec bellard2003-01-06 20:21:42 +0000702 default:
bf374a5f grischka2011-08-11 17:07:56 +0200703 tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
7893a9ec bellard2003-01-06 20:21:42 +0000704 break;
705 }
706}
707
708
709/* assemble a file */
710static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
711{
712 int opcode;
713
714#if 0
715 /* print stats about opcodes */
716 {
717 const ASMInstr *pa;
718 int freq[4];
719 int op_vals[500];
720 int nb_op_vals, i, j;
721
722 nb_op_vals = 0;
723 memset(freq, 0, sizeof(freq));
724 for(pa = asm_instrs; pa->sym != 0; pa++) {
725 freq[pa->nb_ops]++;
726 for(i=0;i<pa->nb_ops;i++) {
727 for(j=0;j<nb_op_vals;j++) {
728 if (pa->op_type[i] == op_vals[j])
729 goto found;
730 }
731 op_vals[nb_op_vals++] = pa->op_type[i];
732 found: ;
733 }
734 }
735 for(i=0;i<nb_op_vals;i++) {
736 int v = op_vals[i];
737 if ((v & (v - 1)) != 0)
738 printf("%3d: %08x\n", i, v);
739 }
740 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
741 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
742 freq[0], freq[1], freq[2], freq[3]);
743 }
744#endif
745
746 /* XXX: undefine C labels */
747
748 ch = file->buf_ptr[0];
749 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
30df3189 grischka2015-05-09 14:29:39 +0200750 parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
7893a9ec bellard2003-01-06 20:21:42 +0000751 if (do_preprocess)
752 parse_flags |= PARSE_FLAG_PREPROCESS;
753 next();
754 for(;;) {
755 if (tok == TOK_EOF)
756 break;
757 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
758 redo:
759 if (tok == '#') {
760 /* horrible gas comment */
761 while (tok != TOK_LINEFEED)
762 next();
763 } else if (tok == '.') {
764 asm_parse_directive(s1);
765 } else if (tok == TOK_PPNUM) {
766 const char *p;
767 int n;
768 p = tokc.cstr->data;
0eef2354 bellard2003-04-21 15:21:19 +0000769 n = strtoul(p, (char **)&p, 10);
7893a9ec bellard2003-01-06 20:21:42 +0000770 if (*p != '\0')
771 expect("':'");
772 /* new local label */
773 asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
774 next();
775 skip(':');
776 goto redo;
777 } else if (tok >= TOK_IDENT) {
778 /* instruction or label */
779 opcode = tok;
780 next();
781 if (tok == ':') {
782 /* new label */
6eac6b72 Daniel Glöckner2010-05-06 21:42:37 +0200783 asm_new_label(s1, opcode, 0);
7893a9ec bellard2003-01-06 20:21:42 +0000784 next();
785 goto redo;
0bd402d2 bellard2004-10-18 00:19:51 +0000786 } else if (tok == '=') {
787 int n;
788 next();
789 n = asm_int_expr(s1);
790 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
791 goto redo;
7893a9ec bellard2003-01-06 20:21:42 +0000792 } else {
793 asm_opcode(s1, opcode);
794 }
795 }
796 /* end of line */
797 if (tok != ';' && tok != TOK_LINEFEED){
798 expect("end of line");
799 }
800 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
801 next();
802 }
803
804 asm_free_labels(s1);
805
806 return 0;
807}
808
809/* Assemble the current file */
88a3ccab grischka2009-12-20 01:53:49 +0100810ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
7893a9ec bellard2003-01-06 20:21:42 +0000811{
0bd402d2 bellard2004-10-18 00:19:51 +0000812 Sym *define_start;
7893a9ec bellard2003-01-06 20:21:42 +0000813 int ret;
814
815 preprocess_init(s1);
816
817 /* default section is text */
818 cur_text_section = text_section;
819 ind = cur_text_section->data_offset;
820
0bd402d2 bellard2004-10-18 00:19:51 +0000821 define_start = define_stack;
822
b5e22384 grischka2009-12-19 19:12:33 +0100823 /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
824 symbols can be safely used */
825 put_elf_sym(symtab_section, 0, 0,
826 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
827 SHN_ABS, file->filename);
828
7893a9ec bellard2003-01-06 20:21:42 +0000829 ret = tcc_assemble_internal(s1, do_preprocess);
830
831 cur_text_section->data_offset = ind;
0bd402d2 bellard2004-10-18 00:19:51 +0000832
833 free_defines(define_start);
834
7893a9ec bellard2003-01-06 20:21:42 +0000835 return ret;
836}
837
838/********************************************************************/
839/* GCC inline asm support */
840
841/* assemble the string 'str' in the current C compilation unit without
842 C preprocessing. NOTE: str is modified by modifying the '\0' at the
843 end */
844static void tcc_assemble_inline(TCCState *s1, char *str, int len)
845{
4e5170d4 grischka2010-01-14 20:58:03 +0100846 int saved_parse_flags;
847 const int *saved_macro_ptr;
7893a9ec bellard2003-01-06 20:21:42 +0000848
7893a9ec bellard2003-01-06 20:21:42 +0000849 saved_parse_flags = parse_flags;
850 saved_macro_ptr = macro_ptr;
e97bf88b grischka2010-11-25 13:29:15 +0100851
944627c4 grischka2013-02-14 06:53:07 +0100852 tcc_open_bf(s1, ":asm:", len);
e97bf88b grischka2010-11-25 13:29:15 +0100853 memcpy(file->buffer, str, len);
854
7893a9ec bellard2003-01-06 20:21:42 +0000855 macro_ptr = NULL;
7893a9ec bellard2003-01-06 20:21:42 +0000856 tcc_assemble_internal(s1, 0);
e97bf88b grischka2010-11-25 13:29:15 +0100857 tcc_close();
7893a9ec bellard2003-01-06 20:21:42 +0000858
859 parse_flags = saved_parse_flags;
860 macro_ptr = saved_macro_ptr;
7893a9ec bellard2003-01-06 20:21:42 +0000861}
862
863/* find a constraint by its number or id (gcc 3 extended
864 syntax). return -1 if not found. Return in *pp in char after the
865 constraint */
88a3ccab grischka2009-12-20 01:53:49 +0100866ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
7893a9ec bellard2003-01-06 20:21:42 +0000867 const char *name, const char **pp)
868{
869 int index;
870 TokenSym *ts;
871 const char *p;
872
873 if (isnum(*name)) {
874 index = 0;
875 while (isnum(*name)) {
876 index = (index * 10) + (*name) - '0';
877 name++;
878 }
879 if ((unsigned)index >= nb_operands)
880 index = -1;
881 } else if (*name == '[') {
882 name++;
883 p = strchr(name, ']');
884 if (p) {
885 ts = tok_alloc(name, p - name);
886 for(index = 0; index < nb_operands; index++) {
887 if (operands[index].id == ts->tok)
888 goto found;
889 }
890 index = -1;
891 found:
892 name = p + 1;
893 } else {
894 index = -1;
895 }
896 } else {
897 index = -1;
898 }
899 if (pp)
900 *pp = name;
901 return index;
902}
903
904static void subst_asm_operands(ASMOperand *operands, int nb_operands,
905 int nb_outputs,
906 CString *out_str, CString *in_str)
907{
908 int c, index, modifier;
909 const char *str;
910 ASMOperand *op;
911 SValue sv;
912
913 cstr_new(out_str);
914 str = in_str->data;
915 for(;;) {
916 c = *str++;
917 if (c == '%') {
918 if (*str == '%') {
919 str++;
920 goto add_char;
921 }
922 modifier = 0;
923 if (*str == 'c' || *str == 'n' ||
924 *str == 'b' || *str == 'w' || *str == 'h')
925 modifier = *str++;
926 index = find_constraint(operands, nb_operands, str, &str);
927 if (index < 0)
bf374a5f grischka2011-08-11 17:07:56 +0200928 tcc_error("invalid operand reference after %%");
7893a9ec bellard2003-01-06 20:21:42 +0000929 op = &operands[index];
930 sv = *op->vt;
931 if (op->reg >= 0) {
932 sv.r = op->reg;
d778bde7 grischka2007-11-21 17:16:31 +0000933 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
7893a9ec bellard2003-01-06 20:21:42 +0000934 sv.r |= VT_LVAL;
935 }
936 subst_asm_operand(out_str, &sv, modifier);
937 } else {
938 add_char:
939 cstr_ccat(out_str, c);
940 if (c == '\0')
941 break;
942 }
943 }
944}
945
946
947static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
948 int is_output)
949{
950 ASMOperand *op;
951 int nb_operands;
952
953 if (tok != ':') {
954 nb_operands = *nb_operands_ptr;
955 for(;;) {
956 if (nb_operands >= MAX_ASM_OPERANDS)
bf374a5f grischka2011-08-11 17:07:56 +0200957 tcc_error("too many asm operands");
7893a9ec bellard2003-01-06 20:21:42 +0000958 op = &operands[nb_operands++];
959 op->id = 0;
960 if (tok == '[') {
961 next();
962 if (tok < TOK_IDENT)
963 expect("identifier");
964 op->id = tok;
965 next();
966 skip(']');
967 }
968 if (tok != TOK_STR)
969 expect("string constant");
970 op->constraint = tcc_malloc(tokc.cstr->size);
971 strcpy(op->constraint, tokc.cstr->data);
972 next();
973 skip('(');
974 gexpr();
975 if (is_output) {
976 test_lvalue();
977 } else {
0bd402d2 bellard2004-10-18 00:19:51 +0000978 /* we want to avoid LLOCAL case, except when the 'm'
979 constraint is used. Note that it may come from
980 register storage, so we need to convert (reg)
7893a9ec bellard2003-01-06 20:21:42 +0000981 case */
982 if ((vtop->r & VT_LVAL) &&
983 ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
0bd402d2 bellard2004-10-18 00:19:51 +0000984 (vtop->r & VT_VALMASK) < VT_CONST) &&
985 !strchr(op->constraint, 'm')) {
7893a9ec bellard2003-01-06 20:21:42 +0000986 gv(RC_INT);
987 }
988 }
989 op->vt = vtop;
990 skip(')');
991 if (tok == ',') {
992 next();
993 } else {
994 break;
995 }
996 }
997 *nb_operands_ptr = nb_operands;
998 }
999}
1000
1001/* parse the GCC asm() instruction */
88a3ccab grischka2009-12-20 01:53:49 +01001002ST_FUNC void asm_instr(void)
7893a9ec bellard2003-01-06 20:21:42 +00001003{
1004 CString astr, astr1;
1005 ASMOperand operands[MAX_ASM_OPERANDS];
ee06ef9d Thomas Preud'homme2011-05-16 14:15:32 +02001006 int nb_outputs, nb_operands, i, must_subst, out_reg;
7893a9ec bellard2003-01-06 20:21:42 +00001007 uint8_t clobber_regs[NB_ASM_REGS];
1008
1009 next();
1010 /* since we always generate the asm() instruction, we can ignore
1011 volatile */
1012 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
1013 next();
1014 }
0bd402d2 bellard2004-10-18 00:19:51 +00001015 parse_asm_str(&astr);
7893a9ec bellard2003-01-06 20:21:42 +00001016 nb_operands = 0;
1017 nb_outputs = 0;
0bd402d2 bellard2004-10-18 00:19:51 +00001018 must_subst = 0;
7893a9ec bellard2003-01-06 20:21:42 +00001019 memset(clobber_regs, 0, sizeof(clobber_regs));
1020 if (tok == ':') {
1021 next();
0bd402d2 bellard2004-10-18 00:19:51 +00001022 must_subst = 1;
7893a9ec bellard2003-01-06 20:21:42 +00001023 /* output args */
1024 parse_asm_operands(operands, &nb_operands, 1);
1025 nb_outputs = nb_operands;
1026 if (tok == ':') {
1027 next();
7b8e2839 bellard2005-09-03 22:23:59 +00001028 if (tok != ')') {
1029 /* input args */
1030 parse_asm_operands(operands, &nb_operands, 0);
1031 if (tok == ':') {
1032 /* clobber list */
1033 /* XXX: handle registers */
7893a9ec bellard2003-01-06 20:21:42 +00001034 next();
7b8e2839 bellard2005-09-03 22:23:59 +00001035 for(;;) {
1036 if (tok != TOK_STR)
1037 expect("string constant");
1038 asm_clobber(clobber_regs, tokc.cstr->data);
7893a9ec bellard2003-01-06 20:21:42 +00001039 next();
7b8e2839 bellard2005-09-03 22:23:59 +00001040 if (tok == ',') {
1041 next();
1042 } else {
1043 break;
1044 }
7893a9ec bellard2003-01-06 20:21:42 +00001045 }
1046 }
1047 }
1048 }
1049 }
1050 skip(')');
1051 /* NOTE: we do not eat the ';' so that we can restore the current
1052 token after the assembler parsing */
1053 if (tok != ';')
1054 expect("';'");
7893a9ec bellard2003-01-06 20:21:42 +00001055
1056 /* save all values in the memory */
1057 save_regs(0);
1058
1059 /* compute constraints */
0bd402d2 bellard2004-10-18 00:19:51 +00001060 asm_compute_constraints(operands, nb_operands, nb_outputs,
1061 clobber_regs, &out_reg);
7893a9ec bellard2003-01-06 20:21:42 +00001062
6e197e3d bellard2003-04-14 22:22:34 +00001063 /* substitute the operands in the asm string. No substitution is
1064 done if no operands (GCC behaviour) */
7893a9ec bellard2003-01-06 20:21:42 +00001065#ifdef ASM_DEBUG
1066 printf("asm: \"%s\"\n", (char *)astr.data);
1067#endif
0bd402d2 bellard2004-10-18 00:19:51 +00001068 if (must_subst) {
6e197e3d bellard2003-04-14 22:22:34 +00001069 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
1070 cstr_free(&astr);
1071 } else {
1072 astr1 = astr;
1073 }
7893a9ec bellard2003-01-06 20:21:42 +00001074#ifdef ASM_DEBUG
1075 printf("subst_asm: \"%s\"\n", (char *)astr1.data);
1076#endif
1077
1078 /* generate loads */
0bd402d2 bellard2004-10-18 00:19:51 +00001079 asm_gen_code(operands, nb_operands, nb_outputs, 0,
1080 clobber_regs, out_reg);
7893a9ec bellard2003-01-06 20:21:42 +00001081
1082 /* assemble the string with tcc internal assembler */
1083 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
1084
1085 /* restore the current C token */
1086 next();
1087
1088 /* store the output values if needed */
0bd402d2 bellard2004-10-18 00:19:51 +00001089 asm_gen_code(operands, nb_operands, nb_outputs, 1,
1090 clobber_regs, out_reg);
7893a9ec bellard2003-01-06 20:21:42 +00001091
1092 /* free everything */
1093 for(i=0;i<nb_operands;i++) {
1094 ASMOperand *op;
1095 op = &operands[i];
1096 tcc_free(op->constraint);
1097 vpop();
1098 }
1099 cstr_free(&astr1);
1100}
1101
88a3ccab grischka2009-12-20 01:53:49 +01001102ST_FUNC void asm_global_instr(void)
0bd402d2 bellard2004-10-18 00:19:51 +00001103{
1104 CString astr;
1105
1106 next();
1107 parse_asm_str(&astr);
1108 skip(')');
1109 /* NOTE: we do not eat the ';' so that we can restore the current
1110 token after the assembler parsing */
1111 if (tok != ';')
1112 expect("';'");
1113
1114#ifdef ASM_DEBUG
1115 printf("asm_global: \"%s\"\n", (char *)astr.data);
1116#endif
1117 cur_text_section = text_section;
1118 ind = cur_text_section->data_offset;
1119
1120 /* assemble the string with tcc internal assembler */
1121 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
1122
1123 cur_text_section->data_offset = ind;
1124
1125 /* restore the current C token */
1126 next();
1127
1128 cstr_free(&astr);
1129}
519a9040 Thomas Preud'homme2012-01-06 18:14:34 +01001130#endif /* CONFIG_TCC_ASM */