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