Subversion Repositories Kolibri OS

Rev

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;inb_ops;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 */