Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
145 halyavin 1
/*
2
 *  i386 specific functions for TCC assembler
3
 *
4
 *  Copyright (c) 2001, 2002 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
#define MAX_OPERANDS 3
22
 
23
typedef struct ASMInstr {
24
    uint16_t sym;
25
    uint16_t opcode;
26
    uint16_t instr_type;
27
#define OPC_JMP       0x01  /* jmp operand */
28
#define OPC_B         0x02  /* only used zith OPC_WL */
29
#define OPC_WL        0x04  /* accepts w, l or no suffix */
30
#define OPC_BWL       (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
31
#define OPC_REG       0x08 /* register is added to opcode */
32
#define OPC_MODRM     0x10 /* modrm encoding */
33
#define OPC_FWAIT     0x20 /* add fwait opcode */
34
#define OPC_TEST      0x40 /* test opcodes */
35
#define OPC_SHIFT     0x80 /* shift opcodes */
36
#define OPC_D16      0x0100 /* generate data16 prefix */
37
#define OPC_ARITH    0x0200 /* arithmetic opcodes */
38
#define OPC_SHORTJMP 0x0400 /* short jmp operand */
39
#define OPC_FARITH   0x0800 /* FPU arithmetic opcodes */
40
#define OPC_GROUP_SHIFT 13
41
 
42
/* in order to compress the operand type, we use specific operands and
43
   we or only with EA  */
44
#define OPT_REG8  0 /* warning: value is hardcoded from TOK_ASM_xxx */
45
#define OPT_REG16 1 /* warning: value is hardcoded from TOK_ASM_xxx */
46
#define OPT_REG32 2 /* warning: value is hardcoded from TOK_ASM_xxx */
47
#define OPT_MMX   3 /* warning: value is hardcoded from TOK_ASM_xxx */
48
#define OPT_SSE   4 /* warning: value is hardcoded from TOK_ASM_xxx */
49
#define OPT_CR    5 /* warning: value is hardcoded from TOK_ASM_xxx */
50
#define OPT_TR    6 /* warning: value is hardcoded from TOK_ASM_xxx */
51
#define OPT_DB    7 /* warning: value is hardcoded from TOK_ASM_xxx */
52
#define OPT_SEG   8
53
#define OPT_ST    9
54
#define OPT_IM8   10
55
#define OPT_IM8S  11
56
#define OPT_IM16  12
57
#define OPT_IM32  13
58
#define OPT_EAX   14 /* %al, %ax or %eax register */
59
#define OPT_ST0   15 /* %st(0) register */
60
#define OPT_CL    16 /* %cl register */
61
#define OPT_DX    17 /* %dx register */
62
#define OPT_ADDR  18 /* OP_EA with only offset */
63
#define OPT_INDIR 19 /* *(expr) */
64
 
65
/* composite types */
66
#define OPT_COMPOSITE_FIRST   20
67
#define OPT_IM       20 /* IM8 | IM16 | IM32 */
68
#define OPT_REG      21 /* REG8 | REG16 | REG32 */
69
#define OPT_REGW     22 /* REG16 | REG32 */
70
#define OPT_IMW      23 /* IM16 | IM32 */
71
 
72
/* can be ored with any OPT_xxx */
73
#define OPT_EA    0x80
74
 
75
    uint8_t nb_ops;
76
    uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */
77
} ASMInstr;
78
 
79
typedef struct Operand {
80
    uint32_t type;
81
#define OP_REG8   (1 << OPT_REG8)
82
#define OP_REG16  (1 << OPT_REG16)
83
#define OP_REG32  (1 << OPT_REG32)
84
#define OP_MMX    (1 << OPT_MMX)
85
#define OP_SSE    (1 << OPT_SSE)
86
#define OP_CR     (1 << OPT_CR)
87
#define OP_TR     (1 << OPT_TR)
88
#define OP_DB     (1 << OPT_DB)
89
#define OP_SEG    (1 << OPT_SEG)
90
#define OP_ST     (1 << OPT_ST)
91
#define OP_IM8    (1 << OPT_IM8)
92
#define OP_IM8S   (1 << OPT_IM8S)
93
#define OP_IM16   (1 << OPT_IM16)
94
#define OP_IM32   (1 << OPT_IM32)
95
#define OP_EAX    (1 << OPT_EAX)
96
#define OP_ST0    (1 << OPT_ST0)
97
#define OP_CL     (1 << OPT_CL)
98
#define OP_DX     (1 << OPT_DX)
99
#define OP_ADDR   (1 << OPT_ADDR)
100
#define OP_INDIR  (1 << OPT_INDIR)
101
 
102
#define OP_EA     0x40000000
103
#define OP_REG    (OP_REG8 | OP_REG16 | OP_REG32)
104
#define OP_IM     OP_IM32
105
    int8_t  reg; /* register, -1 if none */
106
    int8_t  reg2; /* second register, -1 if none */
107
    uint8_t shift;
108
    ExprValue e;
109
} Operand;
110
 
111
static const uint8_t reg_to_size[5] = {
112
    [OP_REG8] = 0,
113
    [OP_REG16] = 1,
114
    [OP_REG32] = 2,
115
};
116
 
117
#define WORD_PREFIX_OPCODE 0x66
118
 
119
#define NB_TEST_OPCODES 30
120
 
121
static const uint8_t test_bits[NB_TEST_OPCODES] = {
122
 0x00, /* o */
123
 0x01, /* no */
124
 0x02, /* b */
125
 0x02, /* c */
126
 0x02, /* nae */
127
 0x03, /* nb */
128
 0x03, /* nc */
129
 0x03, /* ae */
130
 0x04, /* e */
131
 0x04, /* z */
132
 0x05, /* ne */
133
 0x05, /* nz */
134
 0x06, /* be */
135
 0x06, /* na */
136
 0x07, /* nbe */
137
 0x07, /* a */
138
 0x08, /* s */
139
 0x09, /* ns */
140
 0x0a, /* p */
141
 0x0a, /* pe */
142
 0x0b, /* np */
143
 0x0b, /* po */
144
 0x0c, /* l */
145
 0x0c, /* nge */
146
 0x0d, /* nl */
147
 0x0d, /* ge */
148
 0x0e, /* le */
149
 0x0e, /* ng */
150
 0x0f, /* nle */
151
 0x0f, /* g */
152
};
153
 
154
static const ASMInstr asm_instrs[] = {
155
#define ALT(x) x
156
#define DEF_ASM_OP0(name, opcode)
157
#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 },
158
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }},
159
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }},
160
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }},
161
#include "i386-asm.h"
162
 
163
    /* last operation */
164
    { 0, },
165
};
166
 
167
static const uint16_t op0_codes[] = {
168
#define ALT(x)
169
#define DEF_ASM_OP0(x, opcode) opcode,
170
#define DEF_ASM_OP0L(name, opcode, group, instr_type)
171
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
172
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
173
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
174
#include "i386-asm.h"
175
};
176
 
177
static inline int get_reg_shift(TCCState *s1)
178
{
179
    int shift, v;
180
 
181
    v = asm_int_expr(s1);
182
    switch(v) {
183
    case 1:
184
        shift = 0;
185
        break;
186
    case 2:
187
        shift = 1;
188
        break;
189
    case 4:
190
        shift = 2;
191
        break;
192
    case 8:
193
        shift = 3;
194
        break;
195
    default:
196
        expect("1, 2, 4 or 8 constant");
197
        shift = 0;
198
        break;
199
    }
200
    return shift;
201
}
202
 
203
static int asm_parse_reg(void)
204
{
205
    int reg;
206
    if (tok != '%')
207
        goto error_32;
208
    next();
209
    if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
210
        reg = tok - TOK_ASM_eax;
211
        next();
212
        return reg;
213
    } else {
214
    error_32:
215
        expect("32 bit register");
216
        return 0;
217
    }
218
}
219
 
220
static void parse_operand(TCCState *s1, Operand *op)
221
{
222
    ExprValue e;
223
    int reg, indir;
224
    const char *p;
225
 
226
    indir = 0;
227
    if (tok == '*') {
228
        next();
229
        indir = OP_INDIR;
230
    }
231
 
232
    if (tok == '%') {
233
        next();
234
        if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) {
235
            reg = tok - TOK_ASM_al;
236
            op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */
237
            op->reg = reg & 7;
238
            if ((op->type & OP_REG) && op->reg == TREG_EAX)
239
                op->type |= OP_EAX;
240
            else if (op->type == OP_REG8 && op->reg == TREG_ECX)
241
                op->type |= OP_CL;
242
            else if (op->type == OP_REG16 && op->reg == TREG_EDX)
243
                op->type |= OP_DX;
244
        } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) {
245
            op->type = OP_DB;
246
            op->reg = tok - TOK_ASM_dr0;
247
        } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) {
248
            op->type = OP_SEG;
249
            op->reg = tok - TOK_ASM_es;
250
        } else if (tok == TOK_ASM_st) {
251
            op->type = OP_ST;
252
            op->reg = 0;
253
            next();
254
            if (tok == '(') {
255
                next();
256
                if (tok != TOK_PPNUM)
257
                    goto reg_error;
258
                p = tokc.cstr->data;
259
                reg = p[0] - '0';
260
                if ((unsigned)reg >= 8 || p[1] != '\0')
261
                    goto reg_error;
262
                op->reg = reg;
263
                next();
264
                skip(')');
265
            }
266
            if (op->reg == 0)
267
                op->type |= OP_ST0;
268
            goto no_skip;
269
        } else {
270
        reg_error:
271
            error("unknown register");
272
        }
273
        next();
274
    no_skip: ;
275
    } else if (tok == '$') {
276
        /* constant value */
277
        next();
278
        asm_expr(s1, &e);
279
        op->type = OP_IM32;
280
        op->e.v = e.v;
281
        op->e.sym = e.sym;
282
        if (!op->e.sym) {
283
            if (op->e.v == (uint8_t)op->e.v)
284
                op->type |= OP_IM8;
285
            if (op->e.v == (int8_t)op->e.v)
286
                op->type |= OP_IM8S;
287
            if (op->e.v == (uint16_t)op->e.v)
288
                op->type |= OP_IM16;
289
        }
290
    } else {
291
        /* address(reg,reg2,shift) with all variants */
292
        op->type = OP_EA;
293
        op->reg = -1;
294
        op->reg2 = -1;
295
        op->shift = 0;
296
        if (tok != '(') {
297
            asm_expr(s1, &e);
298
            op->e.v = e.v;
299
            op->e.sym = e.sym;
300
        } else {
301
            op->e.v = 0;
302
            op->e.sym = NULL;
303
        }
304
        if (tok == '(') {
305
            next();
306
            if (tok != ',') {
307
                op->reg = asm_parse_reg();
308
            }
309
            if (tok == ',') {
310
                next();
311
                if (tok != ',') {
312
                    op->reg2 = asm_parse_reg();
313
                }
314
                skip(',');
315
                op->shift = get_reg_shift(s1);
316
            }
317
            skip(')');
318
        }
319
        if (op->reg == -1 && op->reg2 == -1)
320
            op->type |= OP_ADDR;
321
    }
322
    op->type |= indir;
323
}
324
 
325
/* XXX: unify with C code output ? */
326
static void gen_expr32(ExprValue *pe)
327
{
328
    if (pe->sym)
329
        greloc(cur_text_section, pe->sym, ind, R_386_32);
330
    gen_le32(pe->v);
331
}
332
 
333
/* XXX: unify with C code output ? */
334
static void gen_disp32(ExprValue *pe)
335
{
336
    Sym *sym;
337
    sym = pe->sym;
338
    if (sym) {
339
        if (sym->r == cur_text_section->sh_num) {
340
            /* same section: we can output an absolute value. Note
341
               that the TCC compiler behaves differently here because
342
               it always outputs a relocation to ease (future) code
343
               elimination in the linker */
344
            gen_le32(pe->v + (long)sym->next - ind - 4);
345
        } else {
346
            greloc(cur_text_section, sym, ind, R_386_PC32);
347
            gen_le32(pe->v - 4);
348
        }
349
    } else {
350
        /* put an empty PC32 relocation */
351
        put_elf_reloc(symtab_section, cur_text_section,
352
                      ind, R_386_PC32, 0);
353
        gen_le32(pe->v - 4);
354
    }
355
}
356
 
357
 
358
static void gen_le16(int v)
359
{
360
    g(v);
361
    g(v >> 8);
362
}
363
 
364
/* generate the modrm operand */
365
static inline void asm_modrm(int reg, Operand *op)
366
{
367
    int mod, reg1, reg2, sib_reg1;
368
 
369
    if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
370
        g(0xc0 + (reg << 3) + op->reg);
371
    } else if (op->reg == -1 && op->reg2 == -1) {
372
        /* displacement only */
373
        g(0x05 + (reg << 3));
374
        gen_expr32(&op->e);
375
    } else {
376
        sib_reg1 = op->reg;
377
        /* fist compute displacement encoding */
378
        if (sib_reg1 == -1) {
379
            sib_reg1 = 5;
380
            mod = 0x00;
381
        } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
382
            mod = 0x00;
383
        } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
384
            mod = 0x40;
385
        } else {
386
            mod = 0x80;
387
        }
388
        /* compute if sib byte needed */
389
        reg1 = op->reg;
390
        if (op->reg2 != -1)
391
            reg1 = 4;
392
        g(mod + (reg << 3) + reg1);
393
        if (reg1 == 4) {
394
            /* add sib byte */
395
            reg2 = op->reg2;
396
            if (reg2 == -1)
397
                reg2 = 4; /* indicate no index */
398
            g((op->shift << 6) + (reg2 << 3) + sib_reg1);
399
        }
400
 
401
        /* add offset */
402
        if (mod == 0x40) {
403
            g(op->e.v);
404
        } else if (mod == 0x80 || op->reg == -1) {
405
            gen_expr32(&op->e);
406
        }
407
    }
408
}
409
 
410
static void asm_opcode(TCCState *s1, int opcode)
411
{
412
    const ASMInstr *pa;
413
    int i, modrm_index, reg, v, op1, is_short_jmp;
414
    int nb_ops, s, ss;
415
    Operand ops[MAX_OPERANDS], *pop;
416
    int op_type[3]; /* decoded op type */
417
 
418
    /* get operands */
419
    pop = ops;
420
    nb_ops = 0;
421
    for(;;) {
422
        if (tok == ';' || tok == TOK_LINEFEED)
423
            break;
424
        if (nb_ops >= MAX_OPERANDS) {
425
            error("incorrect number of operands");
426
        }
427
        parse_operand(s1, pop);
428
        pop++;
429
        nb_ops++;
430
        if (tok != ',')
431
            break;
432
        next();
433
    }
434
 
435
    is_short_jmp = 0;
436
    s = 0; /* avoid warning */
437
 
438
    /* optimize matching by using a lookup table (no hashing is needed
439
       !) */
440
    for(pa = asm_instrs; pa->sym != 0; pa++) {
441
        s = 0;
442
        if (pa->instr_type & OPC_FARITH) {
443
            v = opcode - pa->sym;
444
            if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
445
                continue;
446
        } else if (pa->instr_type & OPC_ARITH) {
447
            if (!(opcode >= pa->sym && opcode < pa->sym + 8 * 4))
448
                continue;
449
            goto compute_size;
450
        } else if (pa->instr_type & OPC_SHIFT) {
451
            if (!(opcode >= pa->sym && opcode < pa->sym + 7 * 4))
452
                continue;
453
            goto compute_size;
454
        } else if (pa->instr_type & OPC_TEST) {
455
            if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
456
                continue;
457
        } else if (pa->instr_type & OPC_B) {
458
            if (!(opcode >= pa->sym && opcode <= pa->sym + 3))
459
                continue;
460
        compute_size:
461
            s = (opcode - pa->sym) & 3;
462
        } else if (pa->instr_type & OPC_WL) {
463
            if (!(opcode >= pa->sym && opcode <= pa->sym + 2))
464
                continue;
465
            s = opcode - pa->sym + 1;
466
        } else {
467
            if (pa->sym != opcode)
468
                continue;
469
        }
470
        if (pa->nb_ops != nb_ops)
471
            continue;
472
        /* now decode and check each operand */
473
        for(i = 0; i < nb_ops; i++) {
474
            int op1, op2;
475
            op1 = pa->op_type[i];
476
            op2 = op1 & 0x1f;
477
            switch(op2) {
478
            case OPT_IM:
479
                v = OP_IM8 | OP_IM16 | OP_IM32;
480
                break;
481
            case OPT_REG:
482
                v = OP_REG8 | OP_REG16 | OP_REG32;
483
                break;
484
            case OPT_REGW:
485
                v = OP_REG16 | OP_REG32;
486
                break;
487
            case OPT_IMW:
488
                v = OP_IM16 | OP_IM32;
489
                break;
490
            default:
491
                v = 1 << op2;
492
                break;
493
            }
494
            if (op1 & OPT_EA)
495
                v |= OP_EA;
496
            op_type[i] = v;
497
            if ((ops[i].type & v) == 0)
498
                goto next;
499
        }
500
        /* all is matching ! */
501
        break;
502
    next: ;
503
    }
504
    if (pa->sym == 0) {
505
        if (opcode >= TOK_ASM_pusha && opcode <= TOK_ASM_emms) {
506
            int b;
507
            b = op0_codes[opcode - TOK_ASM_pusha];
508
            if (b & 0xff00)
509
                g(b >> 8);
510
            g(b);
511
            return;
512
        } else {
513
            error("unknown opcode '%s'",
514
                  get_tok_str(opcode, NULL));
515
        }
516
    }
517
    /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
518
    if (s == 3) {
519
        for(i = 0; s == 3 && i < nb_ops; i++) {
520
            if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX)))
521
                s = reg_to_size[ops[i].type & OP_REG];
522
        }
523
        if (s == 3) {
524
            if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
525
                (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32)))
526
                s = 2;
527
            else
528
                error("cannot infer opcode suffix");
529
        }
530
    }
531
 
532
    /* generate data16 prefix if needed */
533
    ss = s;
534
    if (s == 1 || (pa->instr_type & OPC_D16))
535
        g(WORD_PREFIX_OPCODE);
536
    else if (s == 2)
537
        s = 1;
538
    /* now generates the operation */
539
    if (pa->instr_type & OPC_FWAIT)
540
        g(0x9b);
541
 
542
    v = pa->opcode;
543
    if (v == 0x69 || v == 0x69) {
544
        /* kludge for imul $im, %reg */
545
        nb_ops = 3;
546
        ops[2] = ops[1];
547
    } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) {
548
        v--; /* int $3 case */
549
        nb_ops = 0;
550
    } else if ((v == 0x06 || v == 0x07)) {
551
        if (ops[0].reg >= 4) {
552
            /* push/pop %fs or %gs */
553
            v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3);
554
        } else {
555
            v += ops[0].reg << 3;
556
        }
557
        nb_ops = 0;
558
    } else if (v <= 0x05) {
559
        /* arith case */
560
        v += ((opcode - TOK_ASM_addb) >> 2) << 3;
561
    } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) {
562
        /* fpu arith case */
563
        v += ((opcode - pa->sym) / 6) << 3;
564
    }
565
    if (pa->instr_type & OPC_REG) {
566
        for(i = 0; i < nb_ops; i++) {
567
            if (op_type[i] & (OP_REG | OP_ST)) {
568
                v += ops[i].reg;
569
                break;
570
            }
571
        }
572
        /* mov $im, %reg case */
573
        if (pa->opcode == 0xb0 && s >= 1)
574
            v += 7;
575
    }
576
    if (pa->instr_type & OPC_B)
577
        v += s;
578
    if (pa->instr_type & OPC_TEST)
579
        v += test_bits[opcode - pa->sym];
580
    if (pa->instr_type & OPC_SHORTJMP) {
581
        Sym *sym;
582
        int jmp_disp;
583
 
584
        /* see if we can really generate the jump with a byte offset */
585
        sym = ops[0].e.sym;
586
        if (!sym)
587
            goto no_short_jump;
588
        if (sym->r != cur_text_section->sh_num)
589
            goto no_short_jump;
590
        jmp_disp = ops[0].e.v + (long)sym->next - ind - 2;
591
        if (jmp_disp == (int8_t)jmp_disp) {
592
            /* OK to generate jump */
593
            is_short_jmp = 1;
594
            ops[0].e.v = jmp_disp;
595
        } else {
596
        no_short_jump:
597
            if (pa->instr_type & OPC_JMP) {
598
                /* long jump will be allowed. need to modify the
599
                   opcode slightly */
600
                if (v == 0xeb)
601
                    v = 0xe9;
602
                else
603
                    v += 0x0f10;
604
            } else {
605
                error("invalid displacement");
606
            }
607
        }
608
    }
609
    op1 = v >> 8;
610
    if (op1)
611
        g(op1);
612
    g(v);
613
 
614
    /* search which operand will used for modrm */
615
    modrm_index = 0;
616
    if (pa->instr_type & OPC_SHIFT) {
617
        reg = (opcode - pa->sym) >> 2;
618
        if (reg == 6)
619
            reg = 7;
620
    } else if (pa->instr_type & OPC_ARITH) {
621
        reg = (opcode - pa->sym) >> 2;
622
    } else if (pa->instr_type & OPC_FARITH) {
623
        reg = (opcode - pa->sym) / 6;
624
    } else {
625
        reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
626
    }
627
    if (pa->instr_type & OPC_MODRM) {
628
        /* first look for an ea operand */
629
        for(i = 0;i < nb_ops; i++) {
630
            if (op_type[i] & OP_EA)
631
                goto modrm_found;
632
        }
633
        /* then if not found, a register or indirection (shift instructions) */
634
        for(i = 0;i < nb_ops; i++) {
635
            if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR))
636
                goto modrm_found;
637
        }
638
#ifdef ASM_DEBUG
639
        error("bad op table");
640
#endif
641
    modrm_found:
642
        modrm_index = i;
643
        /* if a register is used in another operand then it is
644
           used instead of group */
645
        for(i = 0;i < nb_ops; i++) {
646
            v = op_type[i];
647
            if (i != modrm_index &&
648
                (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) {
649
                reg = ops[i].reg;
650
                break;
651
            }
652
        }
653
 
654
        asm_modrm(reg, &ops[modrm_index]);
655
    }
656
 
657
    /* emit constants */
658
    if (pa->opcode == 0x9a || pa->opcode == 0xea) {
659
        /* ljmp or lcall kludge */
660
        gen_expr32(&ops[1].e);
661
        if (ops[0].e.sym)
662
            error("cannot relocate");
663
        gen_le16(ops[0].e.v);
664
    } else {
665
        for(i = 0;i < nb_ops; i++) {
666
            v = op_type[i];
667
            if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM8S | OP_ADDR)) {
668
                /* if multiple sizes are given it means we must look
669
                   at the op size */
670
                if (v == (OP_IM8 | OP_IM16 | OP_IM32) ||
671
                    v == (OP_IM16 | OP_IM32)) {
672
                    if (ss == 0)
673
                        v = OP_IM8;
674
                    else if (ss == 1)
675
                        v = OP_IM16;
676
                    else
677
                        v = OP_IM32;
678
                }
679
                if (v & (OP_IM8 | OP_IM8S)) {
680
                    if (ops[i].e.sym)
681
                        goto error_relocate;
682
                    g(ops[i].e.v);
683
                } else if (v & OP_IM16) {
684
                    if (ops[i].e.sym) {
685
                    error_relocate:
686
                        error("cannot relocate");
687
                    }
688
                    gen_le16(ops[i].e.v);
689
                } else {
690
                    if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
691
                        if (is_short_jmp)
692
                            g(ops[i].e.v);
693
                        else
694
                            gen_disp32(&ops[i].e);
695
                    } else {
696
                        gen_expr32(&ops[i].e);
697
                    }
698
                }
699
            }
700
        }
701
    }
702
}
703
 
704
#define NB_SAVED_REGS 3
705
#define NB_ASM_REGS 8
706
 
707
/* return the constraint priority (we allocate first the lowest
708
   numbered constraints) */
709
static inline int constraint_priority(const char *str)
710
{
711
    int priority, c, pr;
712
 
713
    /* we take the lowest priority */
714
    priority = 0;
715
    for(;;) {
716
        c = *str;
717
        if (c == '\0')
718
            break;
719
        str++;
720
        switch(c) {
721
        case 'A':
722
            pr = 0;
723
            break;
724
        case 'a':
725
        case 'b':
726
        case 'c':
727
        case 'd':
728
        case 'S':
729
        case 'D':
730
            pr = 1;
731
            break;
732
        case 'q':
733
            pr = 2;
734
            break;
735
        case 'r':
736
            pr = 3;
737
            break;
738
        case 'N':
739
        case 'M':
740
        case 'I':
741
        case 'i':
742
        case 'm':
743
        case 'g':
744
            pr = 4;
745
            break;
746
        default:
747
            error("unknown constraint '%c'", c);
748
            pr = 0;
749
        }
750
        if (pr > priority)
751
            priority = pr;
752
    }
753
    return priority;
754
}
755
 
756
static const char *skip_constraint_modifiers(const char *p)
757
{
758
    while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
759
        p++;
760
    return p;
761
}
762
 
763
#define REG_OUT_MASK 0x01
764
#define REG_IN_MASK  0x02
765
 
766
#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
767
 
768
static void asm_compute_constraints(ASMOperand *operands,
769
                                    int nb_operands, int nb_outputs,
770
                                    const uint8_t *clobber_regs,
771
                                    int *pout_reg)
772
{
773
    ASMOperand *op;
774
    int sorted_op[MAX_ASM_OPERANDS];
775
    int i, j, k, p1, p2, tmp, reg, c, reg_mask;
776
    const char *str;
777
    uint8_t regs_allocated[NB_ASM_REGS];
778
 
779
    /* init fields */
780
    for(i=0;i
781
        op = &operands[i];
782
        op->input_index = -1;
783
        op->ref_index = -1;
784
        op->reg = -1;
785
        op->is_memory = 0;
786
        op->is_rw = 0;
787
    }
788
    /* compute constraint priority and evaluate references to output
789
       constraints if input constraints */
790
    for(i=0;i
791
        op = &operands[i];
792
        str = op->constraint;
793
        str = skip_constraint_modifiers(str);
794
        if (isnum(*str) || *str == '[') {
795
            /* this is a reference to another constraint */
796
            k = find_constraint(operands, nb_operands, str, NULL);
797
            if ((unsigned)k >= i || i < nb_outputs)
798
                error("invalid reference in constraint %d ('%s')",
799
                      i, str);
800
            op->ref_index = k;
801
            if (operands[k].input_index >= 0)
802
                error("cannot reference twice the same operand");
803
            operands[k].input_index = i;
804
            op->priority = 5;
805
        } else {
806
            op->priority = constraint_priority(str);
807
        }
808
    }
809
 
810
    /* sort operands according to their priority */
811
    for(i=0;i
812
        sorted_op[i] = i;
813
    for(i=0;i
814
        for(j=i+1;j
815
            p1 = operands[sorted_op[i]].priority;
816
            p2 = operands[sorted_op[j]].priority;
817
            if (p2 < p1) {
818
                tmp = sorted_op[i];
819
                sorted_op[i] = sorted_op[j];
820
                sorted_op[j] = tmp;
821
            }
822
        }
823
    }
824
 
825
    for(i = 0;i < NB_ASM_REGS; i++) {
826
        if (clobber_regs[i])
827
            regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
828
        else
829
            regs_allocated[i] = 0;
830
    }
831
    /* esp cannot be used */
832
    regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK;
833
    /* ebp cannot be used yet */
834
    regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK;
835
 
836
    /* allocate registers and generate corresponding asm moves */
837
    for(i=0;i
838
        j = sorted_op[i];
839
        op = &operands[j];
840
        str = op->constraint;
841
        /* no need to allocate references */
842
        if (op->ref_index >= 0)
843
            continue;
844
        /* select if register is used for output, input or both */
845
        if (op->input_index >= 0) {
846
            reg_mask = REG_IN_MASK | REG_OUT_MASK;
847
        } else if (j < nb_outputs) {
848
            reg_mask = REG_OUT_MASK;
849
        } else {
850
            reg_mask = REG_IN_MASK;
851
        }
852
    try_next:
853
        c = *str++;
854
        switch(c) {
855
        case '=':
856
            goto try_next;
857
        case '+':
858
            op->is_rw = 1;
859
            /* FALL THRU */
860
        case '&':
861
            if (j >= nb_outputs)
862
                error("'%c' modifier can only be applied to outputs", c);
863
            reg_mask = REG_IN_MASK | REG_OUT_MASK;
864
            goto try_next;
865
        case 'A':
866
            /* allocate both eax and edx */
867
            if (is_reg_allocated(TREG_EAX) ||
868
                is_reg_allocated(TREG_EDX))
869
                goto try_next;
870
            op->is_llong = 1;
871
            op->reg = TREG_EAX;
872
            regs_allocated[TREG_EAX] |= reg_mask;
873
            regs_allocated[TREG_EDX] |= reg_mask;
874
            break;
875
        case 'a':
876
            reg = TREG_EAX;
877
            goto alloc_reg;
878
        case 'b':
879
            reg = 3;
880
            goto alloc_reg;
881
        case 'c':
882
            reg = TREG_ECX;
883
            goto alloc_reg;
884
        case 'd':
885
            reg = TREG_EDX;
886
            goto alloc_reg;
887
        case 'S':
888
            reg = 6;
889
            goto alloc_reg;
890
        case 'D':
891
            reg = 7;
892
        alloc_reg:
893
            if (is_reg_allocated(reg))
894
                goto try_next;
895
            goto reg_found;
896
        case 'q':
897
            /* eax, ebx, ecx or edx */
898
            for(reg = 0; reg < 4; reg++) {
899
                if (!is_reg_allocated(reg))
900
                    goto reg_found;
901
            }
902
            goto try_next;
903
        case 'r':
904
            /* any general register */
905
            for(reg = 0; reg < 8; reg++) {
906
                if (!is_reg_allocated(reg))
907
                    goto reg_found;
908
            }
909
            goto try_next;
910
        reg_found:
911
            /* now we can reload in the register */
912
            op->is_llong = 0;
913
            op->reg = reg;
914
            regs_allocated[reg] |= reg_mask;
915
            break;
916
        case 'i':
917
            if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
918
                goto try_next;
919
            break;
920
        case 'I':
921
        case 'N':
922
        case 'M':
923
            if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST))
924
                goto try_next;
925
            break;
926
        case 'm':
927
        case 'g':
928
            /* nothing special to do because the operand is already in
929
               memory, except if the pointer itself is stored in a
930
               memory variable (VT_LLOCAL case) */
931
            /* XXX: fix constant case */
932
            /* if it is a reference to a memory zone, it must lie
933
               in a register, so we reserve the register in the
934
               input registers and a load will be generated
935
               later */
936
            if (j < nb_outputs || c == 'm') {
937
                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
938
                    /* any general register */
939
                    for(reg = 0; reg < 8; reg++) {
940
                        if (!(regs_allocated[reg] & REG_IN_MASK))
941
                            goto reg_found1;
942
                    }
943
                    goto try_next;
944
                reg_found1:
945
                    /* now we can reload in the register */
946
                    regs_allocated[reg] |= REG_IN_MASK;
947
                    op->reg = reg;
948
                    op->is_memory = 1;
949
                }
950
            }
951
            break;
952
        default:
953
            error("asm constraint %d ('%s') could not be satisfied",
954
                  j, op->constraint);
955
            break;
956
        }
957
        /* if a reference is present for that operand, we assign it too */
958
        if (op->input_index >= 0) {
959
            operands[op->input_index].reg = op->reg;
960
            operands[op->input_index].is_llong = op->is_llong;
961
        }
962
    }
963
 
964
    /* compute out_reg. It is used to store outputs registers to memory
965
       locations references by pointers (VT_LLOCAL case) */
966
    *pout_reg = -1;
967
    for(i=0;i
968
        op = &operands[i];
969
        if (op->reg >= 0 &&
970
            (op->vt->r & VT_VALMASK) == VT_LLOCAL  &&
971
            !op->is_memory) {
972
            for(reg = 0; reg < 8; reg++) {
973
                if (!(regs_allocated[reg] & REG_OUT_MASK))
974
                    goto reg_found2;
975
            }
976
            error("could not find free output register for reloading");
977
        reg_found2:
978
            *pout_reg = reg;
979
            break;
980
        }
981
    }
982
 
983
    /* print sorted constraints */
984
#ifdef ASM_DEBUG
985
    for(i=0;i
986
        j = sorted_op[i];
987
        op = &operands[j];
988
        printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
989
               j,
990
               op->id ? get_tok_str(op->id, NULL) : "",
991
               op->constraint,
992
               op->vt->r,
993
               op->reg);
994
    }
995
    if (*pout_reg >= 0)
996
        printf("out_reg=%d\n", *pout_reg);
997
#endif
998
}
999
 
1000
static void subst_asm_operand(CString *add_str,
1001
                              SValue *sv, int modifier)
1002
{
1003
    int r, reg, size, val;
1004
    char buf[64];
1005
 
1006
    r = sv->r;
1007
    if ((r & VT_VALMASK) == VT_CONST) {
1008
        if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n')
1009
            cstr_ccat(add_str, '$');
1010
        if (r & VT_SYM) {
1011
            cstr_cat(add_str, get_tok_str(sv->sym->v, NULL));
1012
            if (sv->c.i != 0) {
1013
                cstr_ccat(add_str, '+');
1014
            } else {
1015
                return;
1016
            }
1017
        }
1018
        val = sv->c.i;
1019
        if (modifier == 'n')
1020
            val = -val;
1021
        snprintf(buf, sizeof(buf), "%d", sv->c.i);
1022
        cstr_cat(add_str, buf);
1023
    } else if ((r & VT_VALMASK) == VT_LOCAL) {
1024
        snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i);
1025
        cstr_cat(add_str, buf);
1026
    } else if (r & VT_LVAL) {
1027
        reg = r & VT_VALMASK;
1028
        if (reg >= VT_CONST)
1029
            error("internal compiler error");
1030
        snprintf(buf, sizeof(buf), "(%%%s)",
1031
                 get_tok_str(TOK_ASM_eax + reg, NULL));
1032
        cstr_cat(add_str, buf);
1033
    } else {
1034
        /* register case */
1035
        reg = r & VT_VALMASK;
1036
        if (reg >= VT_CONST)
1037
            error("internal compiler error");
1038
 
1039
        /* choose register operand size */
1040
        if ((sv->type.t & VT_BTYPE) == VT_BYTE)
1041
            size = 1;
1042
        else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
1043
            size = 2;
1044
        else
1045
            size = 4;
1046
        if (size == 1 && reg >= 4)
1047
            size = 4;
1048
 
1049
        if (modifier == 'b') {
1050
            if (reg >= 4)
1051
                error("cannot use byte register");
1052
            size = 1;
1053
        } else if (modifier == 'h') {
1054
            if (reg >= 4)
1055
                error("cannot use byte register");
1056
            size = -1;
1057
        } else if (modifier == 'w') {
1058
            size = 2;
1059
        }
1060
 
1061
        switch(size) {
1062
        case -1:
1063
            reg = TOK_ASM_ah + reg;
1064
            break;
1065
        case 1:
1066
            reg = TOK_ASM_al + reg;
1067
            break;
1068
        case 2:
1069
            reg = TOK_ASM_ax + reg;
1070
            break;
1071
        default:
1072
            reg = TOK_ASM_eax + reg;
1073
            break;
1074
        }
1075
        snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL));
1076
        cstr_cat(add_str, buf);
1077
    }
1078
}
1079
 
1080
/* generate prolog and epilog code for asm statment */
1081
static void asm_gen_code(ASMOperand *operands, int nb_operands,
1082
                         int nb_outputs, int is_output,
1083
                         uint8_t *clobber_regs,
1084
                         int out_reg)
1085
{
1086
    uint8_t regs_allocated[NB_ASM_REGS];
1087
    ASMOperand *op;
1088
    int i, reg;
1089
    static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 };
1090
 
1091
    /* mark all used registers */
1092
    memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
1093
    for(i = 0; i < nb_operands;i++) {
1094
        op = &operands[i];
1095
        if (op->reg >= 0)
1096
            regs_allocated[op->reg] = 1;
1097
    }
1098
    if (!is_output) {
1099
        /* generate reg save code */
1100
        for(i = 0; i < NB_SAVED_REGS; i++) {
1101
            reg = reg_saved[i];
1102
            if (regs_allocated[reg])
1103
                g(0x50 + reg);
1104
        }
1105
 
1106
        /* generate load code */
1107
        for(i = 0; i < nb_operands; i++) {
1108
            op = &operands[i];
1109
            if (op->reg >= 0) {
1110
                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
1111
                    op->is_memory) {
1112
                    /* memory reference case (for both input and
1113
                       output cases) */
1114
                    SValue sv;
1115
                    sv = *op->vt;
1116
                    sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
1117
                    load(op->reg, &sv);
1118
                } else if (i >= nb_outputs || op->is_rw) {
1119
                    /* load value in register */
1120
                    load(op->reg, op->vt);
1121
                    if (op->is_llong) {
1122
                        SValue sv;
1123
                        sv = *op->vt;
1124
                        sv.c.ul += 4;
1125
                        load(TREG_EDX, &sv);
1126
                    }
1127
                }
1128
            }
1129
        }
1130
    } else {
1131
        /* generate save code */
1132
        for(i = 0 ; i < nb_outputs; i++) {
1133
            op = &operands[i];
1134
            if (op->reg >= 0) {
1135
                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
1136
                    if (!op->is_memory) {
1137
                        SValue sv;
1138
                        sv = *op->vt;
1139
                        sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
1140
                        load(out_reg, &sv);
1141
 
1142
                        sv.r = (sv.r & ~VT_VALMASK) | out_reg;
1143
                        store(op->reg, &sv);
1144
                    }
1145
                } else {
1146
                    store(op->reg, op->vt);
1147
                    if (op->is_llong) {
1148
                        SValue sv;
1149
                        sv = *op->vt;
1150
                        sv.c.ul += 4;
1151
                        store(TREG_EDX, &sv);
1152
                    }
1153
                }
1154
            }
1155
        }
1156
        /* generate reg restore code */
1157
        for(i = NB_SAVED_REGS - 1; i >= 0; i--) {
1158
            reg = reg_saved[i];
1159
            if (regs_allocated[reg])
1160
                g(0x58 + reg);
1161
        }
1162
    }
1163
}
1164
 
1165
static void asm_clobber(uint8_t *clobber_regs, const char *str)
1166
{
1167
    int reg;
1168
    TokenSym *ts;
1169
 
1170
    if (!strcmp(str, "memory") ||
1171
        !strcmp(str, "cc"))
1172
        return;
1173
    ts = tok_alloc(str, strlen(str));
1174
    reg = ts->tok;
1175
    if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
1176
        reg -= TOK_ASM_eax;
1177
    } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {
1178
        reg -= TOK_ASM_ax;
1179
    } else {
1180
        error("invalid clobber register '%s'", str);
1181
    }
1182
    clobber_regs[reg] = 1;
1183
}