Mercurial > hg > tinycc
annotate tccasm.c @ 594:2365d90138f5 default tip
Bugfix from Sean Matthews.
author | Rob Landley <rob@landley.net> |
---|---|
date | Thu, 24 Apr 2008 16:05:02 -0500 |
parents | 7fc19a001568 |
children |
rev | line source |
---|---|
217 | 1 /* |
2 * GAS like assembler for TCC | |
3 * | |
331 | 4 * Copyright (c) 2001-2004 Fabrice Bellard |
217 | 5 * |
499
2b451d2e68ea
Exercise LGPL clause 3 and convert more notices from LGPL to GPLv2. (If you
Rob Landley <rob@landley.net>
parents:
481
diff
changeset
|
6 * Licensed under GPLv2, see file LICENSE in this tarball. |
217 | 7 */ |
8 | |
9 static int asm_get_local_label_name(TCCState *s1, unsigned int n) | |
10 { | |
11 char buf[64]; | |
12 TokenSym *ts; | |
13 | |
14 snprintf(buf, sizeof(buf), "L..%u", n); | |
15 ts = tok_alloc(buf, strlen(buf)); | |
16 return ts->tok; | |
17 } | |
18 | |
318 | 19 static void asm_expr(TCCState *s1, ExprValue *pe); |
20 | |
217 | 21 /* We do not use the C expression parser to handle symbols. Maybe the |
22 C expression parser could be tweaked to do so. */ | |
23 | |
24 static void asm_expr_unary(TCCState *s1, ExprValue *pe) | |
25 { | |
26 Sym *sym; | |
27 int op, n, label; | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
28 char *p; |
217 | 29 |
30 switch(tok) { | |
31 case TOK_PPNUM: | |
32 p = tokc.cstr->data; | |
250 | 33 n = strtoul(p, (char **)&p, 0); |
217 | 34 if (*p == 'b' || *p == 'f') { |
35 /* backward or forward label */ | |
36 label = asm_get_local_label_name(s1, n); | |
37 sym = label_find(label); | |
38 if (*p == 'b') { | |
39 /* backward : find the last corresponding defined label */ | |
40 if (sym && sym->r == 0) | |
41 sym = sym->prev_tok; | |
42 if (!sym) | |
43 error("local label '%d' not found backward", n); | |
44 } else { | |
45 /* forward */ | |
46 if (!sym || sym->r) { | |
47 /* if the last label is defined, then define a new one */ | |
48 sym = label_push(&s1->asm_labels, label, 0); | |
49 sym->type.t = VT_STATIC | VT_VOID; | |
50 } | |
51 } | |
52 pe->v = 0; | |
53 pe->sym = sym; | |
54 } else if (*p == '\0') { | |
55 pe->v = n; | |
56 pe->sym = NULL; | |
57 } else { | |
58 error("invalid number syntax"); | |
59 } | |
60 next(); | |
61 break; | |
62 case '+': | |
63 next(); | |
64 asm_expr_unary(s1, pe); | |
65 break; | |
66 case '-': | |
67 case '~': | |
68 op = tok; | |
69 next(); | |
70 asm_expr_unary(s1, pe); | |
71 if (pe->sym) | |
72 error("invalid operation with label"); | |
73 if (op == '-') | |
74 pe->v = -pe->v; | |
75 else | |
76 pe->v = ~pe->v; | |
77 break; | |
258 | 78 case TOK_CCHAR: |
79 case TOK_LCHAR: | |
80 pe->v = tokc.i; | |
81 pe->sym = NULL; | |
82 next(); | |
83 break; | |
318 | 84 case '(': |
85 next(); | |
86 asm_expr(s1, pe); | |
87 skip(')'); | |
88 break; | |
217 | 89 default: |
90 if (tok >= TOK_IDENT) { | |
91 /* label case : if the label was not found, add one */ | |
92 sym = label_find(tok); | |
93 if (!sym) { | |
94 sym = label_push(&s1->asm_labels, tok, 0); | |
95 /* NOTE: by default, the symbol is global */ | |
96 sym->type.t = VT_VOID; | |
97 } | |
318 | 98 if (sym->r == SHN_ABS) { |
99 /* if absolute symbol, no need to put a symbol value */ | |
100 pe->v = (long)sym->next; | |
101 pe->sym = NULL; | |
102 } else { | |
103 pe->v = 0; | |
104 pe->sym = sym; | |
105 } | |
217 | 106 next(); |
107 } else { | |
108 error("bad expression syntax [%s]", get_tok_str(tok, &tokc)); | |
109 } | |
110 break; | |
111 } | |
112 } | |
113 | |
114 static void asm_expr_prod(TCCState *s1, ExprValue *pe) | |
115 { | |
116 int op; | |
117 ExprValue e2; | |
118 | |
119 asm_expr_unary(s1, pe); | |
120 for(;;) { | |
121 op = tok; | |
122 if (op != '*' && op != '/' && op != '%' && | |
123 op != TOK_SHL && op != TOK_SAR) | |
124 break; | |
125 next(); | |
126 asm_expr_unary(s1, &e2); | |
127 if (pe->sym || e2.sym) | |
128 error("invalid operation with label"); | |
129 switch(op) { | |
130 case '*': | |
131 pe->v *= e2.v; | |
132 break; | |
133 case '/': | |
134 if (e2.v == 0) { | |
135 div_error: | |
136 error("division by zero"); | |
137 } | |
138 pe->v /= e2.v; | |
139 break; | |
140 case '%': | |
141 if (e2.v == 0) | |
142 goto div_error; | |
143 pe->v %= e2.v; | |
144 break; | |
145 case TOK_SHL: | |
146 pe->v <<= e2.v; | |
147 break; | |
148 default: | |
149 case TOK_SAR: | |
150 pe->v >>= e2.v; | |
151 break; | |
152 } | |
153 } | |
154 } | |
155 | |
156 static void asm_expr_logic(TCCState *s1, ExprValue *pe) | |
157 { | |
158 int op; | |
159 ExprValue e2; | |
160 | |
161 asm_expr_prod(s1, pe); | |
162 for(;;) { | |
163 op = tok; | |
164 if (op != '&' && op != '|' && op != '^') | |
165 break; | |
166 next(); | |
167 asm_expr_prod(s1, &e2); | |
168 if (pe->sym || e2.sym) | |
169 error("invalid operation with label"); | |
170 switch(op) { | |
171 case '&': | |
172 pe->v &= e2.v; | |
173 break; | |
174 case '|': | |
175 pe->v |= e2.v; | |
176 break; | |
177 default: | |
178 case '^': | |
179 pe->v ^= e2.v; | |
180 break; | |
181 } | |
182 } | |
183 } | |
184 | |
185 static inline void asm_expr_sum(TCCState *s1, ExprValue *pe) | |
186 { | |
187 int op; | |
188 ExprValue e2; | |
189 | |
190 asm_expr_logic(s1, pe); | |
191 for(;;) { | |
192 op = tok; | |
193 if (op != '+' && op != '-') | |
194 break; | |
195 next(); | |
196 asm_expr_logic(s1, &e2); | |
197 if (op == '+') { | |
198 if (pe->sym != NULL && e2.sym != NULL) | |
199 goto cannot_relocate; | |
200 pe->v += e2.v; | |
201 if (pe->sym == NULL && e2.sym != NULL) | |
202 pe->sym = e2.sym; | |
203 } else { | |
204 pe->v -= e2.v; | |
205 /* NOTE: we are less powerful than gas in that case | |
206 because we store only one symbol in the expression */ | |
207 if (!pe->sym && !e2.sym) { | |
208 /* OK */ | |
209 } else if (pe->sym && !e2.sym) { | |
210 /* OK */ | |
211 } else if (pe->sym && e2.sym) { | |
212 if (pe->sym == e2.sym) { | |
213 /* OK */ | |
214 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) { | |
215 /* we also accept defined symbols in the same section */ | |
216 pe->v += (long)pe->sym->next - (long)e2.sym->next; | |
217 } else { | |
218 goto cannot_relocate; | |
219 } | |
220 pe->sym = NULL; /* same symbols can be substracted to NULL */ | |
221 } else { | |
222 cannot_relocate: | |
223 error("invalid operation with label"); | |
224 } | |
225 } | |
226 } | |
227 } | |
228 | |
229 static void asm_expr(TCCState *s1, ExprValue *pe) | |
230 { | |
231 asm_expr_sum(s1, pe); | |
232 } | |
233 | |
234 static int asm_int_expr(TCCState *s1) | |
235 { | |
236 ExprValue e; | |
237 asm_expr(s1, &e); | |
238 if (e.sym) | |
239 expect("constant"); | |
240 return e.v; | |
241 } | |
242 | |
243 /* NOTE: the same name space as C labels is used to avoid using too | |
244 much memory when storing labels in TokenStrings */ | |
318 | 245 static void asm_new_label1(TCCState *s1, int label, int is_local, |
246 int sh_num, int value) | |
217 | 247 { |
248 Sym *sym; | |
249 | |
250 sym = label_find(label); | |
251 if (sym) { | |
252 if (sym->r) { | |
253 /* the label is already defined */ | |
254 if (!is_local) { | |
255 error("assembler label '%s' already defined", | |
256 get_tok_str(label, NULL)); | |
257 } else { | |
258 /* redefinition of local labels is possible */ | |
259 goto new_label; | |
260 } | |
261 } | |
262 } else { | |
263 new_label: | |
264 sym = label_push(&s1->asm_labels, label, 0); | |
265 sym->type.t = VT_STATIC | VT_VOID; | |
266 } | |
318 | 267 sym->r = sh_num; |
268 sym->next = (void *)value; | |
269 } | |
270 | |
271 static void asm_new_label(TCCState *s1, int label, int is_local) | |
272 { | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
273 asm_new_label1(s1, label, is_local, cur_text_section->sh_num, gen_ind); |
217 | 274 } |
275 | |
276 static void asm_free_labels(TCCState *st) | |
277 { | |
278 Sym *s, *s1; | |
318 | 279 Section *sec; |
280 | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
281 for(s = st->asm_labels; s; s = s1) { |
217 | 282 s1 = s->prev; |
283 /* define symbol value in object file */ | |
284 if (s->r) { | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
285 if (s->r == SHN_ABS) sec = SECTION_ABS; |
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
286 else sec = st->sections[s->r]; |
373 | 287 put_extern_sym2(s, sec, (long)s->next, 0, 0); |
217 | 288 } |
289 /* remove label */ | |
551
d8b3fa09ca5d
One of the members of "struct Sym" is a token. Rename it from "v" to "token", and change local variables
Rob Landley <rob@landley.net>
parents:
507
diff
changeset
|
290 table_ident[s->token - TOK_IDENT]->sym_label = NULL; |
341 | 291 sym_free(s); |
217 | 292 } |
293 st->asm_labels = NULL; | |
294 } | |
295 | |
318 | 296 static void use_section1(TCCState *s1, Section *sec) |
297 { | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
298 cur_text_section->data_offset = gen_ind; |
318 | 299 cur_text_section = sec; |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
300 gen_ind = cur_text_section->data_offset; |
318 | 301 } |
302 | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
303 static void use_section(TCCState *s1, char *name) |
258 | 304 { |
305 Section *sec; | |
306 sec = find_section(s1, name); | |
318 | 307 use_section1(s1, sec); |
258 | 308 } |
309 | |
217 | 310 static void asm_parse_directive(TCCState *s1) |
311 { | |
312 int n, offset, v, size, tok1; | |
313 Section *sec; | |
314 uint8_t *ptr; | |
315 | |
316 /* assembler directive */ | |
317 next(); | |
318 sec = cur_text_section; | |
319 switch(tok) { | |
320 case TOK_ASM_align: | |
321 case TOK_ASM_skip: | |
322 case TOK_ASM_space: | |
323 tok1 = tok; | |
324 next(); | |
325 n = asm_int_expr(s1); | |
326 if (tok1 == TOK_ASM_align) { | |
327 if (n < 0 || (n & (n-1)) != 0) | |
328 error("alignment must be a positive power of two"); | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
329 offset = (gen_ind + n - 1) & -n; |
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
330 size = offset - gen_ind; |
318 | 331 /* the section must have a compatible alignment */ |
332 if (sec->sh_addralign < n) | |
333 sec->sh_addralign = n; | |
217 | 334 } else { |
335 size = n; | |
336 } | |
337 v = 0; | |
338 if (tok == ',') { | |
339 next(); | |
340 v = asm_int_expr(s1); | |
341 } | |
318 | 342 zero_pad: |
217 | 343 if (sec->sh_type != SHT_NOBITS) { |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
344 sec->data_offset = gen_ind; |
217 | 345 ptr = section_ptr_add(sec, size); |
346 memset(ptr, v, size); | |
347 } | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
348 gen_ind += size; |
217 | 349 break; |
328 | 350 case TOK_ASM_quad: |
351 next(); | |
352 for(;;) { | |
353 uint64_t vl; | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
354 char *p; |
328 | 355 |
356 p = tokc.cstr->data; | |
357 if (tok != TOK_PPNUM) { | |
358 error_constant: | |
359 error("64 bit constant"); | |
360 } | |
361 vl = strtoll(p, (char **)&p, 0); | |
362 if (*p != '\0') | |
363 goto error_constant; | |
364 next(); | |
365 if (sec->sh_type != SHT_NOBITS) { | |
366 /* XXX: endianness */ | |
367 gen_le32(vl); | |
368 gen_le32(vl >> 32); | |
369 } else { | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
370 gen_ind += 8; |
328 | 371 } |
372 if (tok != ',') | |
373 break; | |
374 next(); | |
375 } | |
376 break; | |
217 | 377 case TOK_ASM_byte: |
378 size = 1; | |
379 goto asm_data; | |
380 case TOK_ASM_word: | |
381 case TOK_SHORT: | |
382 size = 2; | |
383 goto asm_data; | |
384 case TOK_LONG: | |
385 case TOK_INT: | |
386 size = 4; | |
387 asm_data: | |
388 next(); | |
389 for(;;) { | |
390 ExprValue e; | |
391 asm_expr(s1, &e); | |
392 if (sec->sh_type != SHT_NOBITS) { | |
393 if (size == 4) { | |
394 gen_expr32(&e); | |
395 } else { | |
396 if (e.sym) | |
397 expect("constant"); | |
398 if (size == 1) | |
572
9414324cc68a
Rename g() to gen_byte(). Much easier to grep for, and a step towards making
Rob Landley <rob@landley.net>
parents:
553
diff
changeset
|
399 gen_byte(e.v); |
217 | 400 else |
401 gen_le16(e.v); | |
402 } | |
403 } else { | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
404 gen_ind += size; |
217 | 405 } |
406 if (tok != ',') | |
407 break; | |
408 next(); | |
409 } | |
410 break; | |
318 | 411 case TOK_ASM_fill: |
412 { | |
413 int repeat, size, val, i, j; | |
414 uint8_t repeat_buf[8]; | |
415 next(); | |
416 repeat = asm_int_expr(s1); | |
417 if (repeat < 0) { | |
418 error("repeat < 0; .fill ignored"); | |
419 break; | |
420 } | |
421 size = 1; | |
422 val = 0; | |
423 if (tok == ',') { | |
424 next(); | |
425 size = asm_int_expr(s1); | |
426 if (size < 0) { | |
427 error("size < 0; .fill ignored"); | |
428 break; | |
429 } | |
430 if (size > 8) | |
431 size = 8; | |
432 if (tok == ',') { | |
433 next(); | |
434 val = asm_int_expr(s1); | |
435 } | |
436 } | |
437 /* XXX: endianness */ | |
438 repeat_buf[0] = val; | |
439 repeat_buf[1] = val >> 8; | |
440 repeat_buf[2] = val >> 16; | |
441 repeat_buf[3] = val >> 24; | |
442 repeat_buf[4] = 0; | |
443 repeat_buf[5] = 0; | |
444 repeat_buf[6] = 0; | |
445 repeat_buf[7] = 0; | |
446 for(i = 0; i < repeat; i++) { | |
447 for(j = 0; j < size; j++) { | |
572
9414324cc68a
Rename g() to gen_byte(). Much easier to grep for, and a step towards making
Rob Landley <rob@landley.net>
parents:
553
diff
changeset
|
448 gen_byte(repeat_buf[j]); |
318 | 449 } |
450 } | |
451 } | |
452 break; | |
453 case TOK_ASM_org: | |
454 { | |
455 unsigned long n; | |
456 next(); | |
457 /* XXX: handle section symbols too */ | |
458 n = asm_int_expr(s1); | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
459 if (n < gen_ind) |
318 | 460 error("attempt to .org backwards"); |
461 v = 0; | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
462 size = n - gen_ind; |
318 | 463 goto zero_pad; |
464 } | |
465 break; | |
258 | 466 case TOK_ASM_globl: |
302 | 467 case TOK_ASM_global: |
258 | 468 { |
469 Sym *sym; | |
470 | |
471 next(); | |
472 sym = label_find(tok); | |
473 if (!sym) { | |
474 sym = label_push(&s1->asm_labels, tok, 0); | |
475 sym->type.t = VT_VOID; | |
476 } | |
477 sym->type.t &= ~VT_STATIC; | |
478 next(); | |
479 } | |
480 break; | |
481 case TOK_ASM_string: | |
323 | 482 case TOK_ASM_ascii: |
483 case TOK_ASM_asciz: | |
258 | 484 { |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
485 uint8_t *p; |
323 | 486 int i, size, t; |
258 | 487 |
323 | 488 t = tok; |
258 | 489 next(); |
323 | 490 for(;;) { |
491 if (tok != TOK_STR) | |
492 expect("string constant"); | |
493 p = tokc.cstr->data; | |
494 size = tokc.cstr->size; | |
495 if (t == TOK_ASM_ascii && size > 0) | |
496 size--; | |
497 for(i = 0; i < size; i++) | |
572
9414324cc68a
Rename g() to gen_byte(). Much easier to grep for, and a step towards making
Rob Landley <rob@landley.net>
parents:
553
diff
changeset
|
498 gen_byte(p[i]); |
323 | 499 next(); |
500 if (tok == ',') { | |
501 next(); | |
502 } else if (tok != TOK_STR) { | |
503 break; | |
504 } | |
505 } | |
258 | 506 } |
507 break; | |
508 case TOK_ASM_text: | |
509 case TOK_ASM_data: | |
510 case TOK_ASM_bss: | |
511 { | |
512 char sname[64]; | |
513 tok1 = tok; | |
514 n = 0; | |
515 next(); | |
516 if (tok != ';' && tok != TOK_LINEFEED) { | |
517 n = asm_int_expr(s1); | |
518 next(); | |
519 } | |
520 sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n); | |
521 use_section(s1, sname); | |
522 } | |
523 break; | |
524 case TOK_SECTION1: | |
525 { | |
526 char sname[256]; | |
527 | |
528 /* XXX: support more options */ | |
529 next(); | |
530 sname[0] = '\0'; | |
531 while (tok != ';' && tok != TOK_LINEFEED && tok != ',') { | |
532 if (tok == TOK_STR) | |
533 pstrcat(sname, sizeof(sname), tokc.cstr->data); | |
534 else | |
535 pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL)); | |
536 next(); | |
537 } | |
318 | 538 if (tok == ',') { |
539 /* skip section options */ | |
540 next(); | |
541 if (tok != TOK_STR) | |
542 expect("string constant"); | |
543 next(); | |
544 } | |
545 last_text_section = cur_text_section; | |
258 | 546 use_section(s1, sname); |
547 } | |
548 break; | |
318 | 549 case TOK_ASM_previous: |
550 { | |
551 Section *sec; | |
552 next(); | |
553 if (!last_text_section) | |
554 error("no previous section referenced"); | |
555 sec = cur_text_section; | |
556 use_section1(s1, last_text_section); | |
557 last_text_section = sec; | |
558 } | |
559 break; | |
217 | 560 default: |
258 | 561 error("unknown assembler directive '.%s'", get_tok_str(tok, NULL)); |
217 | 562 break; |
563 } | |
564 } | |
565 | |
566 | |
567 /* assemble a file */ | |
568 static int tcc_assemble_internal(TCCState *s1, int do_preprocess) | |
569 { | |
570 int opcode; | |
571 | |
572 #if 0 | |
573 /* print stats about opcodes */ | |
574 { | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
575 ASMInstr *pa; |
217 | 576 int freq[4]; |
577 int op_vals[500]; | |
578 int nb_op_vals, i, j; | |
579 | |
580 nb_op_vals = 0; | |
581 memset(freq, 0, sizeof(freq)); | |
582 for(pa = asm_instrs; pa->sym != 0; pa++) { | |
583 freq[pa->nb_ops]++; | |
584 for(i=0;i<pa->nb_ops;i++) { | |
585 for(j=0;j<nb_op_vals;j++) { | |
586 if (pa->op_type[i] == op_vals[j]) | |
587 goto found; | |
588 } | |
589 op_vals[nb_op_vals++] = pa->op_type[i]; | |
590 found: ; | |
591 } | |
592 } | |
593 for(i=0;i<nb_op_vals;i++) { | |
594 int v = op_vals[i]; | |
595 if ((v & (v - 1)) != 0) | |
596 printf("%3d: %08x\n", i, v); | |
597 } | |
598 printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n", | |
599 sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr), | |
600 freq[0], freq[1], freq[2], freq[3]); | |
601 } | |
602 #endif | |
603 | |
604 /* XXX: undefine C labels */ | |
605 | |
479
7909d3c7e712
Replace global "ch" with "fch" so it's slightly easier to grep for. (It's the
Rob Landley <rob@landley.net>
parents:
475
diff
changeset
|
606 fch = file->buf_ptr[0]; |
453
a70ac19d704b
Stringize patch from Harald van Dijk:
Rob Landley <rob@landley.net>
parents:
388
diff
changeset
|
607 next_tok_flags = TOK_FLAG_BOW | TOK_FLAG_BOL | TOK_FLAG_BOF; |
318 | 608 parse_flags = PARSE_FLAG_ASM_COMMENTS; |
217 | 609 if (do_preprocess) |
610 parse_flags |= PARSE_FLAG_PREPROCESS; | |
611 next(); | |
612 for(;;) { | |
613 if (tok == TOK_EOF) | |
614 break; | |
615 parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */ | |
616 redo: | |
617 if (tok == '#') { | |
618 /* horrible gas comment */ | |
619 while (tok != TOK_LINEFEED) | |
620 next(); | |
621 } else if (tok == '.') { | |
622 asm_parse_directive(s1); | |
623 } else if (tok == TOK_PPNUM) { | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
624 char *p; |
217 | 625 int n; |
626 p = tokc.cstr->data; | |
250 | 627 n = strtoul(p, (char **)&p, 10); |
217 | 628 if (*p != '\0') |
629 expect("':'"); | |
630 /* new local label */ | |
631 asm_new_label(s1, asm_get_local_label_name(s1, n), 1); | |
632 next(); | |
633 skip(':'); | |
634 goto redo; | |
635 } else if (tok >= TOK_IDENT) { | |
636 /* instruction or label */ | |
637 opcode = tok; | |
638 next(); | |
639 if (tok == ':') { | |
640 /* new label */ | |
641 asm_new_label(s1, opcode, 0); | |
642 next(); | |
643 goto redo; | |
318 | 644 } else if (tok == '=') { |
645 int n; | |
646 next(); | |
647 n = asm_int_expr(s1); | |
648 asm_new_label1(s1, opcode, 0, SHN_ABS, n); | |
649 goto redo; | |
217 | 650 } else { |
651 asm_opcode(s1, opcode); | |
652 } | |
653 } | |
654 /* end of line */ | |
655 if (tok != ';' && tok != TOK_LINEFEED){ | |
656 expect("end of line"); | |
657 } | |
658 parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */ | |
659 next(); | |
660 } | |
661 | |
662 asm_free_labels(s1); | |
663 | |
664 return 0; | |
665 } | |
666 | |
667 /* Assemble the current file */ | |
668 static int tcc_assemble(TCCState *s1, int do_preprocess) | |
669 { | |
318 | 670 Sym *define_start; |
217 | 671 int ret; |
672 | |
673 preprocess_init(s1); | |
674 | |
675 /* default section is text */ | |
676 cur_text_section = text_section; | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
677 gen_ind = cur_text_section->data_offset; |
217 | 678 |
318 | 679 define_start = define_stack; |
680 | |
217 | 681 ret = tcc_assemble_internal(s1, do_preprocess); |
682 | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
683 cur_text_section->data_offset = gen_ind; |
318 | 684 |
685 free_defines(define_start); | |
686 | |
217 | 687 return ret; |
688 } | |
689 | |
690 /********************************************************************/ | |
691 /* GCC inline asm support */ | |
692 | |
693 /* assemble the string 'str' in the current C compilation unit without | |
694 C preprocessing. NOTE: str is modified by modifying the '\0' at the | |
695 end */ | |
696 static void tcc_assemble_inline(TCCState *s1, char *str, int len) | |
697 { | |
698 BufferedFile *bf, *saved_file; | |
699 int saved_parse_flags, *saved_macro_ptr; | |
700 | |
507
67453c7d623e
Clean up malloc() and friends to xmalloc/xzmalloc/xstrdup, and remove redundant
Rob Landley <rob@landley.net>
parents:
499
diff
changeset
|
701 bf = xzmalloc(sizeof(BufferedFile)); |
217 | 702 bf->fd = -1; |
703 bf->buf_ptr = str; | |
704 bf->buf_end = str + len; | |
705 str[len] = CH_EOB; | |
706 /* same name as current file so that errors are correctly | |
707 reported */ | |
708 pstrcpy(bf->filename, sizeof(bf->filename), file->filename); | |
709 bf->line_num = file->line_num; | |
710 saved_file = file; | |
711 file = bf; | |
712 saved_parse_flags = parse_flags; | |
713 saved_macro_ptr = macro_ptr; | |
714 macro_ptr = NULL; | |
715 | |
716 tcc_assemble_internal(s1, 0); | |
717 | |
718 parse_flags = saved_parse_flags; | |
719 macro_ptr = saved_macro_ptr; | |
720 file = saved_file; | |
481
0f5c38ddf450
Remove MEM_DEBUG and now-useless tcc_free() wrapper. Now, would anyone like to
Rob Landley <rob@landley.net>
parents:
479
diff
changeset
|
721 free(bf); |
217 | 722 } |
723 | |
724 /* find a constraint by its number or id (gcc 3 extended | |
725 syntax). return -1 if not found. Return in *pp in char after the | |
726 constraint */ | |
727 static int find_constraint(ASMOperand *operands, int nb_operands, | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
728 char *name, char **pp) |
217 | 729 { |
730 int index; | |
731 TokenSym *ts; | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
732 char *p; |
217 | 733 |
734 if (isnum(*name)) { | |
735 index = 0; | |
736 while (isnum(*name)) { | |
737 index = (index * 10) + (*name) - '0'; | |
738 name++; | |
739 } | |
740 if ((unsigned)index >= nb_operands) | |
741 index = -1; | |
742 } else if (*name == '[') { | |
743 name++; | |
744 p = strchr(name, ']'); | |
745 if (p) { | |
746 ts = tok_alloc(name, p - name); | |
747 for(index = 0; index < nb_operands; index++) { | |
748 if (operands[index].id == ts->tok) | |
749 goto found; | |
750 } | |
751 index = -1; | |
752 found: | |
753 name = p + 1; | |
754 } else { | |
755 index = -1; | |
756 } | |
757 } else { | |
758 index = -1; | |
759 } | |
760 if (pp) | |
761 *pp = name; | |
762 return index; | |
763 } | |
764 | |
765 static void subst_asm_operands(ASMOperand *operands, int nb_operands, | |
766 int nb_outputs, | |
767 CString *out_str, CString *in_str) | |
768 { | |
769 int c, index, modifier; | |
553
4533aa54ffcf
More simplification and attacking warnings.
Rob Landley <rob@landley.net>
parents:
551
diff
changeset
|
770 char *str; |
217 | 771 ASMOperand *op; |
772 SValue sv; | |
773 | |
774 cstr_new(out_str); | |
775 str = in_str->data; | |
776 for(;;) { | |
777 c = *str++; | |
778 if (c == '%') { | |
779 if (*str == '%') { | |
780 str++; | |
781 goto add_char; | |
782 } | |
783 modifier = 0; | |
784 if (*str == 'c' || *str == 'n' || | |
785 *str == 'b' || *str == 'w' || *str == 'h') | |
786 modifier = *str++; | |
787 index = find_constraint(operands, nb_operands, str, &str); | |
788 if (index < 0) | |
789 error("invalid operand reference after %%"); | |
790 op = &operands[index]; | |
791 sv = *op->vt; | |
792 if (op->reg >= 0) { | |
793 sv.r = op->reg; | |
475
91c4c7d64f5e
From Joshua Phillips, fix dereferences used in inline assembly output.
Rob Landley <rob@landley.net>
parents:
453
diff
changeset
|
794 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory) |
217 | 795 sv.r |= VT_LVAL; |
796 } | |
797 subst_asm_operand(out_str, &sv, modifier); | |
798 } else { | |
799 add_char: | |
800 cstr_ccat(out_str, c); | |
801 if (c == '\0') | |
802 break; | |
803 } | |
804 } | |
805 } | |
806 | |
807 | |
808 static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, | |
809 int is_output) | |
810 { | |
811 ASMOperand *op; | |
812 int nb_operands; | |
813 | |
814 if (tok != ':') { | |
815 nb_operands = *nb_operands_ptr; | |
816 for(;;) { | |
817 if (nb_operands >= MAX_ASM_OPERANDS) | |
818 error("too many asm operands"); | |
819 op = &operands[nb_operands++]; | |
820 op->id = 0; | |
821 if (tok == '[') { | |
822 next(); | |
823 if (tok < TOK_IDENT) | |
824 expect("identifier"); | |
825 op->id = tok; | |
826 next(); | |
827 skip(']'); | |
828 } | |
829 if (tok != TOK_STR) | |
830 expect("string constant"); | |
507
67453c7d623e
Clean up malloc() and friends to xmalloc/xzmalloc/xstrdup, and remove redundant
Rob Landley <rob@landley.net>
parents:
499
diff
changeset
|
831 op->constraint = xmalloc(tokc.cstr->size); |
217 | 832 strcpy(op->constraint, tokc.cstr->data); |
833 next(); | |
834 skip('('); | |
835 gexpr(); | |
836 if (is_output) { | |
837 test_lvalue(); | |
838 } else { | |
318 | 839 /* we want to avoid LLOCAL case, except when the 'm' |
840 constraint is used. Note that it may come from | |
841 register storage, so we need to convert (reg) | |
217 | 842 case */ |
843 if ((vtop->r & VT_LVAL) && | |
844 ((vtop->r & VT_VALMASK) == VT_LLOCAL || | |
318 | 845 (vtop->r & VT_VALMASK) < VT_CONST) && |
846 !strchr(op->constraint, 'm')) { | |
217 | 847 gv(RC_INT); |
848 } | |
849 } | |
850 op->vt = vtop; | |
851 skip(')'); | |
852 if (tok == ',') { | |
853 next(); | |
854 } else { | |
855 break; | |
856 } | |
857 } | |
858 *nb_operands_ptr = nb_operands; | |
859 } | |
860 } | |
861 | |
318 | 862 static void parse_asm_str(CString *astr) |
863 { | |
864 skip('('); | |
865 /* read the string */ | |
866 if (tok != TOK_STR) | |
867 expect("string constant"); | |
868 cstr_new(astr); | |
869 while (tok == TOK_STR) { | |
870 /* XXX: add \0 handling too ? */ | |
871 cstr_cat(astr, tokc.cstr->data); | |
872 next(); | |
873 } | |
874 cstr_ccat(astr, '\0'); | |
875 } | |
876 | |
217 | 877 /* parse the GCC asm() instruction */ |
878 static void asm_instr(void) | |
879 { | |
880 CString astr, astr1; | |
881 ASMOperand operands[MAX_ASM_OPERANDS]; | |
318 | 882 int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg; |
217 | 883 uint8_t clobber_regs[NB_ASM_REGS]; |
884 | |
885 next(); | |
886 /* since we always generate the asm() instruction, we can ignore | |
887 volatile */ | |
888 if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) { | |
889 next(); | |
890 } | |
318 | 891 parse_asm_str(&astr); |
217 | 892 nb_operands = 0; |
893 nb_outputs = 0; | |
318 | 894 must_subst = 0; |
217 | 895 memset(clobber_regs, 0, sizeof(clobber_regs)); |
896 if (tok == ':') { | |
897 next(); | |
318 | 898 must_subst = 1; |
217 | 899 /* output args */ |
900 parse_asm_operands(operands, &nb_operands, 1); | |
901 nb_outputs = nb_operands; | |
902 if (tok == ':') { | |
903 next(); | |
388 | 904 if (tok != ')') { |
905 /* input args */ | |
906 parse_asm_operands(operands, &nb_operands, 0); | |
907 if (tok == ':') { | |
908 /* clobber list */ | |
909 /* XXX: handle registers */ | |
217 | 910 next(); |
388 | 911 for(;;) { |
912 if (tok != TOK_STR) | |
913 expect("string constant"); | |
914 asm_clobber(clobber_regs, tokc.cstr->data); | |
217 | 915 next(); |
388 | 916 if (tok == ',') { |
917 next(); | |
918 } else { | |
919 break; | |
920 } | |
217 | 921 } |
922 } | |
923 } | |
924 } | |
925 } | |
926 skip(')'); | |
927 /* NOTE: we do not eat the ';' so that we can restore the current | |
928 token after the assembler parsing */ | |
929 if (tok != ';') | |
930 expect("';'"); | |
931 nb_inputs = nb_operands - nb_outputs; | |
932 | |
933 /* save all values in the memory */ | |
934 save_regs(0); | |
935 | |
936 /* compute constraints */ | |
318 | 937 asm_compute_constraints(operands, nb_operands, nb_outputs, |
938 clobber_regs, &out_reg); | |
217 | 939 |
242 | 940 /* substitute the operands in the asm string. No substitution is |
941 done if no operands (GCC behaviour) */ | |
217 | 942 #ifdef ASM_DEBUG |
943 printf("asm: \"%s\"\n", (char *)astr.data); | |
944 #endif | |
318 | 945 if (must_subst) { |
242 | 946 subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr); |
947 cstr_free(&astr); | |
948 } else { | |
949 astr1 = astr; | |
950 } | |
217 | 951 #ifdef ASM_DEBUG |
952 printf("subst_asm: \"%s\"\n", (char *)astr1.data); | |
953 #endif | |
954 | |
955 /* generate loads */ | |
318 | 956 asm_gen_code(operands, nb_operands, nb_outputs, 0, |
957 clobber_regs, out_reg); | |
217 | 958 |
959 /* assemble the string with tcc internal assembler */ | |
960 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1); | |
961 | |
962 /* restore the current C token */ | |
963 next(); | |
964 | |
965 /* store the output values if needed */ | |
318 | 966 asm_gen_code(operands, nb_operands, nb_outputs, 1, |
967 clobber_regs, out_reg); | |
217 | 968 |
969 /* free everything */ | |
970 for(i=0;i<nb_operands;i++) { | |
971 ASMOperand *op; | |
972 op = &operands[i]; | |
481
0f5c38ddf450
Remove MEM_DEBUG and now-useless tcc_free() wrapper. Now, would anyone like to
Rob Landley <rob@landley.net>
parents:
479
diff
changeset
|
973 free(op->constraint); |
217 | 974 vpop(); |
975 } | |
976 cstr_free(&astr1); | |
977 } | |
978 | |
318 | 979 static void asm_global_instr(void) |
980 { | |
981 CString astr; | |
982 | |
983 next(); | |
984 parse_asm_str(&astr); | |
985 skip(')'); | |
986 /* NOTE: we do not eat the ';' so that we can restore the current | |
987 token after the assembler parsing */ | |
988 if (tok != ';') | |
989 expect("';'"); | |
990 | |
991 #ifdef ASM_DEBUG | |
992 printf("asm_global: \"%s\"\n", (char *)astr.data); | |
993 #endif | |
994 cur_text_section = text_section; | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
995 gen_ind = cur_text_section->data_offset; |
318 | 996 |
997 /* assemble the string with tcc internal assembler */ | |
998 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1); | |
999 | |
585
7fc19a001568
Rename global gen.c variable ind to gen_ind.
Rob Landley <rob@landley.net>
parents:
572
diff
changeset
|
1000 cur_text_section->data_offset = gen_ind; |
318 | 1001 |
1002 /* restore the current C token */ | |
1003 next(); | |
1004 | |
1005 cstr_free(&astr); | |
1006 } |