Subversion Repositories Kolibri OS

Rev

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;inb_ops;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
}