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