diff options
Diffstat (limited to 'src/x86')
| -rw-r--r-- | src/x86/i386-asm.c | 1502 | ||||
| -rw-r--r-- | src/x86/i386-asm.h | 474 | ||||
| -rw-r--r-- | src/x86/i386-gen.c | 1144 | ||||
| -rw-r--r-- | src/x86/i386-tok.h | 244 | ||||
| -rw-r--r-- | src/x86/x86_64-asm.h | 449 | ||||
| -rw-r--r-- | src/x86/x86_64-gen.c | 2359 |
6 files changed, 0 insertions, 6172 deletions
diff --git a/src/x86/i386-asm.c b/src/x86/i386-asm.c deleted file mode 100644 index adc40b2..0000000 --- a/src/x86/i386-asm.c +++ /dev/null @@ -1,1502 +0,0 @@ -/* - * i386 specific functions for TCC assembler - * - * Copyright (c) 2001, 2002 Fabrice Bellard - * Copyright (c) 2009 Frédéric Feret (x86_64 support) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "../tcc.h" - -/* #define NB_ASM_REGS 8 */ -#define MAX_OPERANDS 3 -#define NB_SAVED_REGS 3 - -#define TOK_ASM_first TOK_ASM_clc -#define TOK_ASM_last TOK_ASM_emms -#define TOK_ASM_alllast TOK_ASM_pxor - -#define OPC_JMP 0x01 /* jmp operand */ -#define OPC_B 0x02 /* only used with OPC_WL */ -#define OPC_WL 0x04 /* accepts w, l or no suffix */ -#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */ -#define OPC_REG 0x08 /* register is added to opcode */ -#define OPC_MODRM 0x10 /* modrm encoding */ -#define OPC_FWAIT 0x20 /* add fwait opcode */ -#define OPC_TEST 0x40 /* test opcodes */ -#define OPC_SHIFT 0x80 /* shift opcodes */ -#define OPC_D16 0x0100 /* generate data16 prefix */ -#define OPC_ARITH 0x0200 /* arithmetic opcodes */ -#define OPC_SHORTJMP 0x0400 /* short jmp operand */ -#define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */ -#ifdef TCC_TARGET_X86_64 -# define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */ -# define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */ -# define OPC_WLX OPC_WLQ -#else -# define OPC_WLX OPC_WL -#endif - -#define OPC_GROUP_SHIFT 13 - -/* in order to compress the operand type, we use specific operands and - we or only with EA */ -enum { - OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_REG16, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_REG32, /* warning: value is hardcoded from TOK_ASM_xxx */ -#ifdef TCC_TARGET_X86_64 - OPT_REG64, /* warning: value is hardcoded from TOK_ASM_xxx */ -#endif - OPT_MMX, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_SSE, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_CR, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_TR, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_SEG, - OPT_ST, - OPT_IM8, - OPT_IM8S, - OPT_IM16, - OPT_IM32, -#ifdef TCC_TARGET_X86_64 - OPT_IM64, -#endif - OPT_EAX, /* %al, %ax, %eax or %rax register */ - OPT_ST0, /* %st(0) register */ - OPT_CL, /* %cl register */ - OPT_DX, /* %dx register */ - OPT_ADDR, /* OP_EA with only offset */ - OPT_INDIR, /* *(expr) */ - /* composite types */ - OPT_COMPOSITE_FIRST, - OPT_IM, /* IM8 | IM16 | IM32 | IM64 */ - OPT_REG, /* REG8 | REG16 | REG32 | REG64 */ - OPT_REGW, /* REG16 | REG32 | REG64 */ - OPT_IMW, /* IM16 | IM32 | IM64 */ -#ifdef TCC_TARGET_X86_64 - OPT_IMNO64, /* IM16 | IM32 */ -#endif - /* can be ored with any OPT_xxx */ - OPT_EA = 0x80 -}; - -#define OP_REG8 (1 << OPT_REG8) -#define OP_REG16 (1 << OPT_REG16) -#define OP_REG32 (1 << OPT_REG32) -#define OP_MMX (1 << OPT_MMX) -#define OP_SSE (1 << OPT_SSE) -#define OP_CR (1 << OPT_CR) -#define OP_TR (1 << OPT_TR) -#define OP_DB (1 << OPT_DB) -#define OP_SEG (1 << OPT_SEG) -#define OP_ST (1 << OPT_ST) -#define OP_IM8 (1 << OPT_IM8) -#define OP_IM8S (1 << OPT_IM8S) -#define OP_IM16 (1 << OPT_IM16) -#define OP_IM32 (1 << OPT_IM32) -#define OP_EAX (1 << OPT_EAX) -#define OP_ST0 (1 << OPT_ST0) -#define OP_CL (1 << OPT_CL) -#define OP_DX (1 << OPT_DX) -#define OP_ADDR (1 << OPT_ADDR) -#define OP_INDIR (1 << OPT_INDIR) -#ifdef TCC_TARGET_X86_64 -# define OP_REG64 (1 << OPT_REG64) -# define OP_IM64 (1 << OPT_IM64) -#else -# define OP_REG64 0 -# define OP_IM64 0 -#endif - -#define OP_EA 0x40000000 -#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64) - -#ifdef TCC_TARGET_X86_64 -# define OP_IM OP_IM64 -# define TREG_XAX TREG_RAX -# define TREG_XCX TREG_RCX -# define TREG_XDX TREG_RDX -#else -# define OP_IM OP_IM32 -# define TREG_XAX TREG_EAX -# define TREG_XCX TREG_ECX -# define TREG_XDX TREG_EDX -#endif - -typedef struct ASMInstr { - uint16_t sym; - uint16_t opcode; - uint16_t instr_type; - uint8_t nb_ops; - uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */ -} ASMInstr; - -typedef struct Operand { - uint32_t type; - int8_t reg; /* register, -1 if none */ - int8_t reg2; /* second register, -1 if none */ - uint8_t shift; - ExprValue e; -} Operand; - -static const uint8_t reg_to_size[9] = { -/* - [OP_REG8] = 0, - [OP_REG16] = 1, - [OP_REG32] = 2, -#ifdef TCC_TARGET_X86_64 - [OP_REG64] = 3, -#endif -*/ - 0, 0, 1, 0, 2, 0, 0, 0, 3 -}; - -#define NB_TEST_OPCODES 30 - -static const uint8_t test_bits[NB_TEST_OPCODES] = { - 0x00, /* o */ - 0x01, /* no */ - 0x02, /* b */ - 0x02, /* c */ - 0x02, /* nae */ - 0x03, /* nb */ - 0x03, /* nc */ - 0x03, /* ae */ - 0x04, /* e */ - 0x04, /* z */ - 0x05, /* ne */ - 0x05, /* nz */ - 0x06, /* be */ - 0x06, /* na */ - 0x07, /* nbe */ - 0x07, /* a */ - 0x08, /* s */ - 0x09, /* ns */ - 0x0a, /* p */ - 0x0a, /* pe */ - 0x0b, /* np */ - 0x0b, /* po */ - 0x0c, /* l */ - 0x0c, /* nge */ - 0x0d, /* nl */ - 0x0d, /* ge */ - 0x0e, /* le */ - 0x0e, /* ng */ - 0x0f, /* nle */ - 0x0f, /* g */ -}; - -static const uint8_t segment_prefixes[] = { - 0x26, /* es */ - 0x2e, /* cs */ - 0x36, /* ss */ - 0x3e, /* ds */ - 0x64, /* fs */ - 0x65 /* gs */ -}; - -static const ASMInstr asm_instrs[] = { -#define ALT(x) x -#define DEF_ASM_OP0(name, opcode) -#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 }, -#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }}, -#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }}, -#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 }}, -#ifdef TCC_TARGET_X86_64 -# include "x86_64-asm.h" -#else -# include "i386-asm.h" -#endif - /* last operation */ - { 0, }, -}; - -static const uint16_t op0_codes[] = { -#define ALT(x) -#define DEF_ASM_OP0(x, opcode) opcode, -#define DEF_ASM_OP0L(name, opcode, group, instr_type) -#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) -#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) -#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) -#ifdef TCC_TARGET_X86_64 -# include "x86_64-asm.h" -#else -# include "i386-asm.h" -#endif -}; - -static inline int get_reg_shift(TCCState *s1) -{ - int shift, v; -#ifdef I386_ASM_16 - if (s1->seg_size == 16) - tcc_error("invalid effective address"); -#endif - v = asm_int_expr(s1); - switch(v) { - case 1: - shift = 0; - break; - case 2: - shift = 1; - break; - case 4: - shift = 2; - break; - case 8: - shift = 3; - break; - default: - expect("1, 2, 4 or 8 constant"); - shift = 0; - break; - } - return shift; -} - -static int asm_parse_reg(void) -{ - int reg = 0; - if (tok != '%') - goto error_32; - next(); - if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { - reg = tok - TOK_ASM_eax; -#ifdef TCC_TARGET_X86_64 - } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { - reg = tok - TOK_ASM_rax; -#endif -#ifdef I386_ASM_16 - } else if (tok >= TOK_ASM_ax && tok <= TOK_ASM_di) { - reg = tok - TOK_ASM_ax; -#endif - } else { - error_32: - expect("register"); - } - next(); - return reg; -} - -static void parse_operand(TCCState *s1, Operand *op) -{ - ExprValue e; - int reg, indir; - const char *p; - - indir = 0; - if (tok == '*') { - next(); - indir = OP_INDIR; - } - - if (tok == '%') { - next(); - if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) { - reg = tok - TOK_ASM_al; - op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */ - op->reg = reg & 7; - if ((op->type & OP_REG) && op->reg == TREG_XAX) - op->type |= OP_EAX; - else if (op->type == OP_REG8 && op->reg == TREG_XCX) - op->type |= OP_CL; - else if (op->type == OP_REG16 && op->reg == TREG_XDX) - op->type |= OP_DX; - } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) { - op->type = OP_DB; - op->reg = tok - TOK_ASM_dr0; - } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) { - op->type = OP_SEG; - op->reg = tok - TOK_ASM_es; - } else if (tok == TOK_ASM_st) { - op->type = OP_ST; - op->reg = 0; - next(); - if (tok == '(') { - next(); - if (tok != TOK_PPNUM) - goto reg_error; - p = tokc.cstr->data; - reg = p[0] - '0'; - if ((unsigned)reg >= 8 || p[1] != '\0') - goto reg_error; - op->reg = reg; - next(); - skip(')'); - } - if (op->reg == 0) - op->type |= OP_ST0; - goto no_skip; - } else { - reg_error: - tcc_error("unknown register"); - } - next(); - no_skip: ; - } else if (tok == '$') { - /* constant value */ - next(); - asm_expr(s1, &e); - op->type = OP_IM; - op->e.v = e.v; - op->e.sym = e.sym; - if (!op->e.sym) { - if (op->e.v == (uint8_t)op->e.v) - op->type |= OP_IM8; - if (op->e.v == (int8_t)op->e.v) - op->type |= OP_IM8S; - if (op->e.v == (uint16_t)op->e.v) - op->type |= OP_IM16; -#ifdef TCC_TARGET_X86_64 - if (op->e.v == (uint32_t)op->e.v) - op->type |= OP_IM32; -#endif - } - } else { - /* address(reg,reg2,shift) with all variants */ - op->type = OP_EA; - op->reg = -1; - op->reg2 = -1; - op->shift = 0; - if (tok != '(') { - asm_expr(s1, &e); - op->e.v = e.v; - op->e.sym = e.sym; - } else { - next(); - if (tok == '%') { - unget_tok('('); - op->e.v = 0; - op->e.sym = NULL; - } else { - /* bracketed offset expression */ - asm_expr(s1, &e); - if (tok != ')') - expect(")"); - next(); - op->e.v = e.v; - op->e.sym = e.sym; - } - } - if (tok == '(') { - next(); - if (tok != ',') { - op->reg = asm_parse_reg(); - } - if (tok == ',') { - next(); - if (tok != ',') { - op->reg2 = asm_parse_reg(); - } - if (tok == ',') { - next(); - op->shift = get_reg_shift(s1); - } - } - skip(')'); - } - if (op->reg == -1 && op->reg2 == -1) - op->type |= OP_ADDR; - } - op->type |= indir; -} - -/* XXX: unify with C code output ? */ -ST_FUNC void gen_expr32(ExprValue *pe) -{ - gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); -} - -#ifdef TCC_TARGET_X86_64 -static void gen_expr64(ExprValue *pe) -{ - gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v); -} -#endif - -/* XXX: unify with C code output ? */ -static void gen_disp32(ExprValue *pe) -{ - Sym *sym = pe->sym; - if (sym && sym->r == cur_text_section->sh_num) { - /* same section: we can output an absolute value. Note - that the TCC compiler behaves differently here because - it always outputs a relocation to ease (future) code - elimination in the linker */ - gen_le32(pe->v + sym->jnext - ind - 4); - } else { - if (sym && sym->type.t == VT_VOID) { - sym->type.t = VT_FUNC; - sym->type.ref = NULL; - } - gen_addrpc32(VT_SYM, sym, pe->v); - } -} - -#ifdef I386_ASM_16 -static void gen_expr16(ExprValue *pe) -{ - if (pe->sym) - greloc(cur_text_section, pe->sym, ind, R_386_16); - gen_le16(pe->v); -} -static void gen_disp16(ExprValue *pe) -{ - Sym *sym; - sym = pe->sym; - if (sym) { - if (sym->r == cur_text_section->sh_num) { - /* same section: we can output an absolute value. Note - that the TCC compiler behaves differently here because - it always outputs a relocation to ease (future) code - elimination in the linker */ - gen_le16(pe->v + sym->jnext - ind - 2); - } else { - greloc(cur_text_section, sym, ind, R_386_PC16); - gen_le16(pe->v - 2); - } - } else { - /* put an empty PC32 relocation */ - put_elf_reloc(symtab_section, cur_text_section, - ind, R_386_PC16, 0); - gen_le16(pe->v - 2); - } -} -#endif - -/* generate the modrm operand */ -static inline void asm_modrm(int reg, Operand *op) -{ - int mod, reg1, reg2, sib_reg1; - - if (op->type & (OP_REG | OP_MMX | OP_SSE)) { - g(0xc0 + (reg << 3) + op->reg); - } else if (op->reg == -1 && op->reg2 == -1) { - /* displacement only */ -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 16) { - g(0x06 + (reg << 3)); - gen_expr16(&op->e); - } else if (tcc_state->seg_size == 32) -#endif - { - g(0x05 + (reg << 3)); - gen_expr32(&op->e); - } - } else { - sib_reg1 = op->reg; - /* fist compute displacement encoding */ - if (sib_reg1 == -1) { - sib_reg1 = 5; - mod = 0x00; - } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) { - mod = 0x00; - } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) { - mod = 0x40; - } else { - mod = 0x80; - } - /* compute if sib byte needed */ - reg1 = op->reg; - if (op->reg2 != -1) - reg1 = 4; -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 32) { -#endif - g(mod + (reg << 3) + reg1); - if (reg1 == 4) { - /* add sib byte */ - reg2 = op->reg2; - if (reg2 == -1) - reg2 = 4; /* indicate no index */ - g((op->shift << 6) + (reg2 << 3) + sib_reg1); - } -#ifdef I386_ASM_16 - } else if (tcc_state->seg_size == 16) { - /* edi = 7, esi = 6 --> di = 5, si = 4 */ - if ((reg1 == 6) || (reg1 == 7)) { - reg1 -= 2; - /* ebx = 3 --> bx = 7 */ - } else if (reg1 == 3) { - reg1 = 7; - /* o32 = 5 --> o16 = 6 */ - } else if (reg1 == 5) { - reg1 = 6; - /* sib not valid in 16-bit mode */ - } else if (reg1 == 4) { - reg2 = op->reg2; - /* bp + si + offset */ - if ((sib_reg1 == 5) && (reg2 == 6)) { - reg1 = 2; - /* bp + di + offset */ - } else if ((sib_reg1 == 5) && (reg2 == 7)) { - reg1 = 3; - /* bx + si + offset */ - } else if ((sib_reg1 == 3) && (reg2 == 6)) { - reg1 = 0; - /* bx + di + offset */ - } else if ((sib_reg1 == 3) && (reg2 == 7)) { - reg1 = 1; - } else { - tcc_error("invalid effective address"); - } - if (op->e.v == 0) - mod = 0; - } else { - tcc_error("invalid register"); - } - g(mod + (reg << 3) + reg1); - } -#endif - /* add offset */ - if (mod == 0x40) { - g(op->e.v); - } else if (mod == 0x80 || op->reg == -1) { -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 16) - gen_expr16(&op->e); - else if (tcc_state->seg_size == 32) -#endif - gen_expr32(&op->e); - } - } -} - -ST_FUNC void asm_opcode(TCCState *s1, int opcode) -{ - const ASMInstr *pa; - int i, modrm_index, reg, v, op1, is_short_jmp, seg_prefix; - int nb_ops, s; - Operand ops[MAX_OPERANDS], *pop; - int op_type[3]; /* decoded op type */ -#ifdef I386_ASM_16 - static int a32 = 0, o32 = 0, addr32 = 0, data32 = 0; -#endif - - /* force synthetic ';' after prefix instruction, so we can handle */ - /* one-line things like "rep stosb" instead of only "rep\nstosb" */ - if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz) - unget_tok(';'); - - /* get operands */ - pop = ops; - nb_ops = 0; - seg_prefix = 0; - for(;;) { - if (tok == ';' || tok == TOK_LINEFEED) - break; - if (nb_ops >= MAX_OPERANDS) { - tcc_error("incorrect number of operands"); - } - parse_operand(s1, pop); - if (tok == ':') { - if (pop->type != OP_SEG || seg_prefix) - tcc_error("incorrect prefix"); - seg_prefix = segment_prefixes[pop->reg]; - next(); - parse_operand(s1, pop); -#ifndef I386_ASM_16 - if (!(pop->type & OP_EA)) { - tcc_error("segment prefix must be followed by memory reference"); - } -#endif - } - pop++; - nb_ops++; - if (tok != ',') - break; - next(); - } - - is_short_jmp = 0; - s = 0; /* avoid warning */ - - /* optimize matching by using a lookup table (no hashing is needed - !) */ - for(pa = asm_instrs; pa->sym != 0; pa++) { - s = 0; - if (pa->instr_type & OPC_FARITH) { - v = opcode - pa->sym; - if (!((unsigned)v < 8 * 6 && (v % 6) == 0)) - continue; - } else if (pa->instr_type & OPC_ARITH) { - if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX)) - continue; - s = (opcode - pa->sym) % NBWLX; - } else if (pa->instr_type & OPC_SHIFT) { - if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX)) - continue; - s = (opcode - pa->sym) % NBWLX; - } else if (pa->instr_type & OPC_TEST) { - if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES)) - continue; - } else if (pa->instr_type & OPC_B) { - if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX)) - continue; - s = opcode - pa->sym; - } else if (pa->instr_type & OPC_WLX) { - if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1)) - continue; - s = opcode - pa->sym + 1; - } else { - if (pa->sym != opcode) - continue; - } - if (pa->nb_ops != nb_ops) - continue; - /* now decode and check each operand */ - for(i = 0; i < nb_ops; i++) { - int op1, op2; - op1 = pa->op_type[i]; - op2 = op1 & 0x1f; - switch(op2) { - case OPT_IM: - v = OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64; - break; - case OPT_REG: - v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64; - break; - case OPT_REGW: - v = OP_REG16 | OP_REG32 | OP_REG64; - break; - case OPT_IMW: - v = OP_IM16 | OP_IM32 | OP_IM64; - break; -#ifdef TCC_TARGET_X86_64 - case OPT_IMNO64: - v = OP_IM16 | OP_IM32; - break; -#endif - default: - v = 1 << op2; - break; - } - if (op1 & OPT_EA) - v |= OP_EA; - op_type[i] = v; - if ((ops[i].type & v) == 0) - goto next; - } - /* all is matching ! */ - break; - next: ; - } - if (pa->sym == 0) { - if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) { - int b; - b = op0_codes[opcode - TOK_ASM_first]; -#ifdef I386_ASM_16 - if (opcode == TOK_ASM_o32) { - if (s1->seg_size == 32) - tcc_error("incorrect prefix"); - else - o32 = data32 = 1; - } else if (opcode == TOK_ASM_a32) { - if (s1->seg_size == 32) - tcc_error("incorrect prefix"); - else - a32 = addr32 = 1; - } -#endif - if (b & 0xff00) - g(b >> 8); - g(b); - return; - } else if (opcode <= TOK_ASM_alllast) { - tcc_error("bad operand with opcode '%s'", - get_tok_str(opcode, NULL)); - } else { - tcc_error("unknown opcode '%s'", - get_tok_str(opcode, NULL)); - } - } - /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */ - if (s == NBWLX-1) { - for(i = 0; s == NBWLX-1 && i < nb_ops; i++) { - if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX))) - s = reg_to_size[ops[i].type & OP_REG]; - } - if (s == NBWLX-1) { - if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && - (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32 | OP_IM64))) - s = 2; - else - tcc_error("cannot infer opcode suffix"); - } - } - -#ifdef I386_ASM_16 - for(i = 0; i < nb_ops; i++) { - if (ops[i].type & OP_REG32) { - if (s1->seg_size == 16) - o32 = 1; - } else if (!(ops[i].type & OP_REG32)) { - if (s1->seg_size == 32) - o32 = 1; - } - } - - - if (s == 1 || (pa->instr_type & OPC_D16)) { - if (s1->seg_size == 32) - o32 = 1; - } else if (s == 2) { - if (s1->seg_size == 16) { - if (!(pa->instr_type & OPC_D16)) - o32 = 1; - } - } - - /* generate a16/a32 prefix if needed */ - if ((a32 == 1) && (addr32 == 0)) - g(0x67); - /* generate o16/o32 prefix if needed */ - if ((o32 == 1) && (data32 == 0)) - g(0x66); - - addr32 = data32 = 0; -#else - /* generate data16 prefix if needed */ - if (s == 1 || (pa->instr_type & OPC_D16)) - g(0x66); -#ifdef TCC_TARGET_X86_64 - else if (s == 3) { - /* generate REX prefix */ - if ((opcode != TOK_ASM_push && opcode != TOK_ASM_pop) - || !(ops[0].type & OP_REG64)) - g(0x48); - } -#endif -#endif - - /* now generates the operation */ - if (pa->instr_type & OPC_FWAIT) - g(0x9b); - if (seg_prefix) - g(seg_prefix); - - v = pa->opcode; - if ((v == 0x69 || v == 0x6b) && nb_ops == 2) { - /* kludge for imul $im, %reg */ - nb_ops = 3; - ops[2] = ops[1]; - op_type[2] = op_type[1]; - } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) { - v--; /* int $3 case */ - nb_ops = 0; - } else if ((v == 0x06 || v == 0x07)) { - if (ops[0].reg >= 4) { - /* push/pop %fs or %gs */ - v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3); - } else { - v += ops[0].reg << 3; - } - nb_ops = 0; - } else if (v <= 0x05) { - /* arith case */ - v += ((opcode - TOK_ASM_addb) / NBWLX) << 3; - } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) { - /* fpu arith case */ - v += ((opcode - pa->sym) / 6) << 3; - } - if (pa->instr_type & OPC_REG) { - for(i = 0; i < nb_ops; i++) { - if (op_type[i] & (OP_REG | OP_ST)) { - v += ops[i].reg; - break; - } - } - /* mov $im, %reg case */ - if (pa->opcode == 0xb0 && s >= 1) - v += 7; - } - if (pa->instr_type & OPC_B) - v += s >= 1; - if (pa->instr_type & OPC_TEST) - v += test_bits[opcode - pa->sym]; - if (pa->instr_type & OPC_SHORTJMP) { - Sym *sym; - int jmp_disp; - - /* see if we can really generate the jump with a byte offset */ - sym = ops[0].e.sym; - if (!sym) - goto no_short_jump; - if (sym->r != cur_text_section->sh_num) - goto no_short_jump; - jmp_disp = ops[0].e.v + sym->jnext - ind - 2; - if (jmp_disp == (int8_t)jmp_disp) { - /* OK to generate jump */ - is_short_jmp = 1; - ops[0].e.v = jmp_disp; - } else { - no_short_jump: - if (pa->instr_type & OPC_JMP) { - /* long jump will be allowed. need to modify the - opcode slightly */ - if (v == 0xeb) - v = 0xe9; - else - v += 0x0f10; - } else { - tcc_error("invalid displacement"); - } - } - } - op1 = v >> 8; - if (op1) - g(op1); - g(v); - - /* search which operand will used for modrm */ - modrm_index = 0; - if (pa->instr_type & OPC_SHIFT) { - reg = (opcode - pa->sym) / NBWLX; - if (reg == 6) - reg = 7; - } else if (pa->instr_type & OPC_ARITH) { - reg = (opcode - pa->sym) / NBWLX; - } else if (pa->instr_type & OPC_FARITH) { - reg = (opcode - pa->sym) / 6; - } else { - reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7; - } - if (pa->instr_type & OPC_MODRM) { - /* first look for an ea operand */ - for(i = 0;i < nb_ops; i++) { - if (op_type[i] & OP_EA) - goto modrm_found; - } - /* then if not found, a register or indirection (shift instructions) */ - for(i = 0;i < nb_ops; i++) { - if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR)) - goto modrm_found; - } -#ifdef ASM_DEBUG - tcc_error("bad op table"); -#endif - modrm_found: - modrm_index = i; - /* if a register is used in another operand then it is - used instead of group */ - for(i = 0;i < nb_ops; i++) { - v = op_type[i]; - if (i != modrm_index && - (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { - reg = ops[i].reg; - break; - } - } - - asm_modrm(reg, &ops[modrm_index]); - } - - /* emit constants */ -#ifndef TCC_TARGET_X86_64 - if (pa->opcode == 0x9a || pa->opcode == 0xea) { - /* ljmp or lcall kludge */ -#ifdef I386_ASM_16 - if (s1->seg_size == 16 && o32 == 0) - gen_expr16(&ops[1].e); - else -#endif - gen_expr32(&ops[1].e); - if (ops[0].e.sym) - tcc_error("cannot relocate"); - gen_le16(ops[0].e.v); - return; - } -#endif - for(i = 0;i < nb_ops; i++) { - v = op_type[i]; - if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) { - /* if multiple sizes are given it means we must look - at the op size */ - if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) { - if (s == 0) - v = OP_IM8; - else if (s == 1) - v = OP_IM16; - else if (s == 2 || (v & OP_IM64) == 0) - v = OP_IM32; - else - v = OP_IM64; - } - if (v & (OP_IM8 | OP_IM8S)) { - if (ops[i].e.sym) - goto error_relocate; - g(ops[i].e.v); - } else if (v & OP_IM16) { -#ifdef I386_ASM_16 - if (s1->seg_size == 16) - gen_expr16(&ops[i].e); - else -#endif - if (ops[i].e.sym) - error_relocate: - tcc_error("cannot relocate"); - else - gen_le16(ops[i].e.v); - } else { - if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { - if (is_short_jmp) - g(ops[i].e.v); -#ifdef I386_ASM_16 - else if (s1->seg_size == 16) - gen_disp16(&ops[i].e); -#endif - else - gen_disp32(&ops[i].e); - } else { -#ifdef I386_ASM_16 - if (s1->seg_size == 16 && !((o32 == 1) && (v & OP_IM32))) - gen_expr16(&ops[i].e); - else -#endif -#ifdef TCC_TARGET_X86_64 - if (v & OP_IM64) - gen_expr64(&ops[i].e); - else -#endif - gen_expr32(&ops[i].e); - } - } -#ifdef I386_ASM_16 - } else if (v & (OP_REG16 | OP_REG32)) { - if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { - /* jmp $r */ - g(0xE0 + ops[i].reg); - } -#endif -#ifdef TCC_TARGET_X86_64 - } else if (v & (OP_REG32 | OP_REG64)) { - if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { - /* jmp $r */ - g(0xE0 + ops[i].reg); - } -#endif - } - } -#ifdef I386_ASM_16 - a32 = o32 = 0; -#endif -} - -/* return the constraint priority (we allocate first the lowest - numbered constraints) */ -static inline int constraint_priority(const char *str) -{ - int priority, c, pr; - - /* we take the lowest priority */ - priority = 0; - for(;;) { - c = *str; - if (c == '\0') - break; - str++; - switch(c) { - case 'A': - pr = 0; - break; - case 'a': - case 'b': - case 'c': - case 'd': - case 'S': - case 'D': - pr = 1; - break; - case 'q': - pr = 2; - break; - case 'r': - pr = 3; - break; - case 'N': - case 'M': - case 'I': - case 'i': - case 'm': - case 'g': - pr = 4; - break; - default: - tcc_error("unknown constraint '%c'", c); - pr = 0; - } - if (pr > priority) - priority = pr; - } - return priority; -} - -static const char *skip_constraint_modifiers(const char *p) -{ - while (*p == '=' || *p == '&' || *p == '+' || *p == '%') - p++; - return p; -} - -#define REG_OUT_MASK 0x01 -#define REG_IN_MASK 0x02 - -#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) - -ST_FUNC void asm_compute_constraints(ASMOperand *operands, - int nb_operands, int nb_outputs, - const uint8_t *clobber_regs, - int *pout_reg) -{ - ASMOperand *op; - int sorted_op[MAX_ASM_OPERANDS]; - int i, j, k, p1, p2, tmp, reg, c, reg_mask; - const char *str; - uint8_t regs_allocated[NB_ASM_REGS]; - - /* init fields */ - for(i=0;i<nb_operands;i++) { - op = &operands[i]; - op->input_index = -1; - op->ref_index = -1; - op->reg = -1; - op->is_memory = 0; - op->is_rw = 0; - } - /* compute constraint priority and evaluate references to output - constraints if input constraints */ - for(i=0;i<nb_operands;i++) { - op = &operands[i]; - str = op->constraint; - str = skip_constraint_modifiers(str); - if (isnum(*str) || *str == '[') { - /* this is a reference to another constraint */ - k = find_constraint(operands, nb_operands, str, NULL); - if ((unsigned)k >= i || i < nb_outputs) - tcc_error("invalid reference in constraint %d ('%s')", - i, str); - op->ref_index = k; - if (operands[k].input_index >= 0) - tcc_error("cannot reference twice the same operand"); - operands[k].input_index = i; - op->priority = 5; - } else { - op->priority = constraint_priority(str); - } - } - - /* sort operands according to their priority */ - for(i=0;i<nb_operands;i++) - sorted_op[i] = i; - for(i=0;i<nb_operands - 1;i++) { - for(j=i+1;j<nb_operands;j++) { - p1 = operands[sorted_op[i]].priority; - p2 = operands[sorted_op[j]].priority; - if (p2 < p1) { - tmp = sorted_op[i]; - sorted_op[i] = sorted_op[j]; - sorted_op[j] = tmp; - } - } - } - - for(i = 0;i < NB_ASM_REGS; i++) { - if (clobber_regs[i]) - regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK; - else - regs_allocated[i] = 0; - } - /* esp cannot be used */ - regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK; - /* ebp cannot be used yet */ - regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK; - - /* allocate registers and generate corresponding asm moves */ - for(i=0;i<nb_operands;i++) { - j = sorted_op[i]; - op = &operands[j]; - str = op->constraint; - /* no need to allocate references */ - if (op->ref_index >= 0) - continue; - /* select if register is used for output, input or both */ - if (op->input_index >= 0) { - reg_mask = REG_IN_MASK | REG_OUT_MASK; - } else if (j < nb_outputs) { - reg_mask = REG_OUT_MASK; - } else { - reg_mask = REG_IN_MASK; - } - try_next: - c = *str++; - switch(c) { - case '=': - goto try_next; - case '+': - op->is_rw = 1; - /* FALL THRU */ - case '&': - if (j >= nb_outputs) - tcc_error("'%c' modifier can only be applied to outputs", c); - reg_mask = REG_IN_MASK | REG_OUT_MASK; - goto try_next; - case 'A': - /* allocate both eax and edx */ - if (is_reg_allocated(TREG_XAX) || - is_reg_allocated(TREG_XDX)) - goto try_next; - op->is_llong = 1; - op->reg = TREG_XAX; - regs_allocated[TREG_XAX] |= reg_mask; - regs_allocated[TREG_XDX] |= reg_mask; - break; - case 'a': - reg = TREG_XAX; - goto alloc_reg; - case 'b': - reg = 3; - goto alloc_reg; - case 'c': - reg = TREG_XCX; - goto alloc_reg; - case 'd': - reg = TREG_XDX; - goto alloc_reg; - case 'S': - reg = 6; - goto alloc_reg; - case 'D': - reg = 7; - alloc_reg: - if (is_reg_allocated(reg)) - goto try_next; - goto reg_found; - case 'q': - /* eax, ebx, ecx or edx */ - for(reg = 0; reg < 4; reg++) { - if (!is_reg_allocated(reg)) - goto reg_found; - } - goto try_next; - case 'r': - /* any general register */ - for(reg = 0; reg < 8; reg++) { - if (!is_reg_allocated(reg)) - goto reg_found; - } - goto try_next; - reg_found: - /* now we can reload in the register */ - op->is_llong = 0; - op->reg = reg; - regs_allocated[reg] |= reg_mask; - break; - case 'i': - if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) - goto try_next; - break; - case 'I': - case 'N': - case 'M': - if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) - goto try_next; - break; - case 'm': - case 'g': - /* nothing special to do because the operand is already in - memory, except if the pointer itself is stored in a - memory variable (VT_LLOCAL case) */ - /* XXX: fix constant case */ - /* if it is a reference to a memory zone, it must lie - in a register, so we reserve the register in the - input registers and a load will be generated - later */ - if (j < nb_outputs || c == 'm') { - if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { - /* any general register */ - for(reg = 0; reg < 8; reg++) { - if (!(regs_allocated[reg] & REG_IN_MASK)) - goto reg_found1; - } - goto try_next; - reg_found1: - /* now we can reload in the register */ - regs_allocated[reg] |= REG_IN_MASK; - op->reg = reg; - op->is_memory = 1; - } - } - break; - default: - tcc_error("asm constraint %d ('%s') could not be satisfied", - j, op->constraint); - break; - } - /* if a reference is present for that operand, we assign it too */ - if (op->input_index >= 0) { - operands[op->input_index].reg = op->reg; - operands[op->input_index].is_llong = op->is_llong; - } - } - - /* compute out_reg. It is used to store outputs registers to memory - locations references by pointers (VT_LLOCAL case) */ - *pout_reg = -1; - for(i=0;i<nb_operands;i++) { - op = &operands[i]; - if (op->reg >= 0 && - (op->vt->r & VT_VALMASK) == VT_LLOCAL && - !op->is_memory) { - for(reg = 0; reg < 8; reg++) { - if (!(regs_allocated[reg] & REG_OUT_MASK)) - goto reg_found2; - } - tcc_error("could not find free output register for reloading"); - reg_found2: - *pout_reg = reg; - break; - } - } - - /* print sorted constraints */ -#ifdef ASM_DEBUG - for(i=0;i<nb_operands;i++) { - j = sorted_op[i]; - op = &operands[j]; - printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n", - j, - op->id ? get_tok_str(op->id, NULL) : "", - op->constraint, - op->vt->r, - op->reg); - } - if (*pout_reg >= 0) - printf("out_reg=%d\n", *pout_reg); -#endif -} - -ST_FUNC void subst_asm_operand(CString *add_str, - SValue *sv, int modifier) -{ - int r, reg, size, val; - char buf[64]; - - r = sv->r; - if ((r & VT_VALMASK) == VT_CONST) { - if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n') - cstr_ccat(add_str, '$'); - if (r & VT_SYM) { - cstr_cat(add_str, get_tok_str(sv->sym->v, NULL)); - if (sv->c.i != 0) { - cstr_ccat(add_str, '+'); - } else { - return; - } - } - val = sv->c.i; - if (modifier == 'n') - val = -val; - snprintf(buf, sizeof(buf), "%d", sv->c.i); - cstr_cat(add_str, buf); - } else if ((r & VT_VALMASK) == VT_LOCAL) { - snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i); - cstr_cat(add_str, buf); - } else if (r & VT_LVAL) { - reg = r & VT_VALMASK; - if (reg >= VT_CONST) - tcc_error("internal compiler error"); - snprintf(buf, sizeof(buf), "(%%%s)", - get_tok_str(TOK_ASM_eax + reg, NULL)); - cstr_cat(add_str, buf); - } else { - /* register case */ - reg = r & VT_VALMASK; - if (reg >= VT_CONST) - tcc_error("internal compiler error"); - - /* choose register operand size */ - if ((sv->type.t & VT_BTYPE) == VT_BYTE) - size = 1; - else if ((sv->type.t & VT_BTYPE) == VT_SHORT) - size = 2; -#ifdef TCC_TARGET_X86_64 - else if ((sv->type.t & VT_BTYPE) == VT_LLONG) - size = 8; -#endif - else - size = 4; - if (size == 1 && reg >= 4) - size = 4; - - if (modifier == 'b') { - if (reg >= 4) - tcc_error("cannot use byte register"); - size = 1; - } else if (modifier == 'h') { - if (reg >= 4) - tcc_error("cannot use byte register"); - size = -1; - } else if (modifier == 'w') { - size = 2; -#ifdef TCC_TARGET_X86_64 - } else if (modifier == 'q') { - size = 8; -#endif - } - - switch(size) { - case -1: - reg = TOK_ASM_ah + reg; - break; - case 1: - reg = TOK_ASM_al + reg; - break; - case 2: - reg = TOK_ASM_ax + reg; - break; - default: - reg = TOK_ASM_eax + reg; - break; -#ifdef TCC_TARGET_X86_64 - case 8: - reg = TOK_ASM_rax + reg; - break; -#endif - } - snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL)); - cstr_cat(add_str, buf); - } -} - -/* generate prolog and epilog code for asm statement */ -ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, - int nb_outputs, int is_output, - uint8_t *clobber_regs, - int out_reg) -{ - uint8_t regs_allocated[NB_ASM_REGS]; - ASMOperand *op; - int i, reg; - static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 }; - - /* mark all used registers */ - memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); - for(i = 0; i < nb_operands;i++) { - op = &operands[i]; - if (op->reg >= 0) - regs_allocated[op->reg] = 1; - } - if (!is_output) { - /* generate reg save code */ - for(i = 0; i < NB_SAVED_REGS; i++) { - reg = reg_saved[i]; - if (regs_allocated[reg]) { -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 16) - g(0x66); -#endif - g(0x50 + reg); - } - } - - /* generate load code */ - for(i = 0; i < nb_operands; i++) { - op = &operands[i]; - if (op->reg >= 0) { - if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && - op->is_memory) { - /* memory reference case (for both input and - output cases) */ - SValue sv; - sv = *op->vt; - sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; - load(op->reg, &sv); - } else if (i >= nb_outputs || op->is_rw) { - /* load value in register */ - load(op->reg, op->vt); - if (op->is_llong) { - SValue sv; - sv = *op->vt; - sv.c.ul += 4; - load(TREG_XDX, &sv); - } - } - } - } - } else { - /* generate save code */ - for(i = 0 ; i < nb_outputs; i++) { - op = &operands[i]; - if (op->reg >= 0) { - if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { - if (!op->is_memory) { - SValue sv; - sv = *op->vt; - sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; - load(out_reg, &sv); - - sv.r = (sv.r & ~VT_VALMASK) | out_reg; - store(op->reg, &sv); - } - } else { - store(op->reg, op->vt); - if (op->is_llong) { - SValue sv; - sv = *op->vt; - sv.c.ul += 4; - store(TREG_XDX, &sv); - } - } - } - } - /* generate reg restore code */ - for(i = NB_SAVED_REGS - 1; i >= 0; i--) { - reg = reg_saved[i]; - if (regs_allocated[reg]) { -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 16) - g(0x66); -#endif - g(0x58 + reg); - } - } - } -} - -ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) -{ - int reg; - TokenSym *ts; - - if (!strcmp(str, "memory") || - !strcmp(str, "cc")) - return; - ts = tok_alloc(str, strlen(str)); - reg = ts->tok; - if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) { - reg -= TOK_ASM_eax; - } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) { - reg -= TOK_ASM_ax; -#ifdef TCC_TARGET_X86_64 - } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) { - reg -= TOK_ASM_rax; -#endif - } else { - tcc_error("invalid clobber register '%s'", str); - } - clobber_regs[reg] = 1; -} diff --git a/src/x86/i386-asm.h b/src/x86/i386-asm.h deleted file mode 100644 index 2e82e0d..0000000 --- a/src/x86/i386-asm.h +++ /dev/null @@ -1,474 +0,0 @@ - DEF_ASM_OP0(clc, 0xf8) /* must be first OP0 */ - DEF_ASM_OP0(cld, 0xfc) - DEF_ASM_OP0(cli, 0xfa) - DEF_ASM_OP0(clts, 0x0f06) - DEF_ASM_OP0(cmc, 0xf5) - DEF_ASM_OP0(lahf, 0x9f) - DEF_ASM_OP0(sahf, 0x9e) - DEF_ASM_OP0(pusha, 0x60) - DEF_ASM_OP0(popa, 0x61) - DEF_ASM_OP0(pushfl, 0x9c) - DEF_ASM_OP0(popfl, 0x9d) - DEF_ASM_OP0(pushf, 0x9c) - DEF_ASM_OP0(popf, 0x9d) - DEF_ASM_OP0(stc, 0xf9) - DEF_ASM_OP0(std, 0xfd) - DEF_ASM_OP0(sti, 0xfb) - DEF_ASM_OP0(aaa, 0x37) - DEF_ASM_OP0(aas, 0x3f) - DEF_ASM_OP0(daa, 0x27) - DEF_ASM_OP0(das, 0x2f) - DEF_ASM_OP0(aad, 0xd50a) - DEF_ASM_OP0(aam, 0xd40a) - DEF_ASM_OP0(cbw, 0x6698) - DEF_ASM_OP0(cwd, 0x6699) - DEF_ASM_OP0(cwde, 0x98) - DEF_ASM_OP0(cdq, 0x99) - DEF_ASM_OP0(cbtw, 0x6698) - DEF_ASM_OP0(cwtl, 0x98) - DEF_ASM_OP0(cwtd, 0x6699) - DEF_ASM_OP0(cltd, 0x99) - DEF_ASM_OP0(int3, 0xcc) - DEF_ASM_OP0(into, 0xce) - DEF_ASM_OP0(iret, 0xcf) - DEF_ASM_OP0(rsm, 0x0faa) - DEF_ASM_OP0(hlt, 0xf4) - DEF_ASM_OP0(nop, 0x90) - DEF_ASM_OP0(pause, 0xf390) - DEF_ASM_OP0(xlat, 0xd7) - - /* strings */ -ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL)) -ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL)) - -ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL)) -ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL)) - -ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL)) -ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL)) - -ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL)) -ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL)) - -ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL)) -ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL)) - -ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL)) -ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL)) - - /* bits */ - -ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW)) -ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW)) - -ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) - -ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) - -ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) - -ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) - - /* prefixes */ - DEF_ASM_OP0(wait, 0x9b) - DEF_ASM_OP0(fwait, 0x9b) -#ifdef I386_ASM_16 - DEF_ASM_OP0(a32, 0x67) - DEF_ASM_OP0(o32, 0x66) -#else - DEF_ASM_OP0(aword, 0x67) - DEF_ASM_OP0(addr16, 0x67) - ALT(DEF_ASM_OP0(word, 0x66)) - DEF_ASM_OP0(data16, 0x66) -#endif - DEF_ASM_OP0(lock, 0xf0) - DEF_ASM_OP0(rep, 0xf3) - DEF_ASM_OP0(repe, 0xf3) - DEF_ASM_OP0(repz, 0xf3) - DEF_ASM_OP0(repne, 0xf2) - DEF_ASM_OP0(repnz, 0xf2) - - DEF_ASM_OP0(invd, 0x0f08) - DEF_ASM_OP0(wbinvd, 0x0f09) - DEF_ASM_OP0(cpuid, 0x0fa2) - DEF_ASM_OP0(wrmsr, 0x0f30) - DEF_ASM_OP0(rdtsc, 0x0f31) - DEF_ASM_OP0(rdmsr, 0x0f32) - DEF_ASM_OP0(rdpmc, 0x0f33) - DEF_ASM_OP0(ud2, 0x0f0b) - - /* NOTE: we took the same order as gas opcode definition order */ -ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX)) -ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR)) -ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) -ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG)) -ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA)) - -ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG)) - -ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32)) -ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32)) -ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32)) -ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR)) -ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB)) -ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR)) - -ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16)) -ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW)) -ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32)) - -ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW)) -ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S)) -ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32)) -ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG)) - -ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW)) -ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG)) - -ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX)) -ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG)) -ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) - -ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX)) -ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8)) -ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX)) -ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX)) - -ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8)) -ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8)) -ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX)) -ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX)) - -ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG)) - -ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32)) - - /* arith */ -ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */ -ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) -ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX)) -ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG)) - -ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) -ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX)) -ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG)) - -ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW)) -ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW)) -ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) - -ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) - -ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) - -ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG)) -ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW)) -ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW)) -ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW)) -ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW)) - -ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX)) -ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX)) - - /* shifts */ -ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG)) - -ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW)) - -ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR)) -ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR)) -ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR)) -ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR)) -#ifdef I386_ASM_16 -ALT(DEF_ASM_OP1(jmp, 0xff, 0, OPC_JMP | OPC_WL, OPT_REGW)) -#endif - -ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32)) -ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA)) -ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32)) -ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA)) - -ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8)) -ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA)) -ALT(DEF_ASM_OP1(setob, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA)) - DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8) - DEF_ASM_OP0(leave, 0xc9) - DEF_ASM_OP0(ret, 0xc3) - DEF_ASM_OP0(retl,0xc3) -ALT(DEF_ASM_OP1(retl,0xc2, 0, 0, OPT_IM16)) -ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16)) - DEF_ASM_OP0(lret, 0xcb) -ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16)) - -ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR)) - DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR) - - /* float */ - /* specific fcomp handling */ -ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0)) - -ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST)) -ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0)) -ALT(DEF_ASM_OP2(fadd, 0xdcc0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST)) -ALT(DEF_ASM_OP2(fmul, 0xdcc8, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST)) -ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH)) -ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST)) -ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0)) -ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST)) -ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH)) -ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) -ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) -ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) -ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) - - DEF_ASM_OP0(fucompp, 0xdae9) - DEF_ASM_OP0(ftst, 0xd9e4) - DEF_ASM_OP0(fxam, 0xd9e5) - DEF_ASM_OP0(fld1, 0xd9e8) - DEF_ASM_OP0(fldl2t, 0xd9e9) - DEF_ASM_OP0(fldl2e, 0xd9ea) - DEF_ASM_OP0(fldpi, 0xd9eb) - DEF_ASM_OP0(fldlg2, 0xd9ec) - DEF_ASM_OP0(fldln2, 0xd9ed) - DEF_ASM_OP0(fldz, 0xd9ee) - - DEF_ASM_OP0(f2xm1, 0xd9f0) - DEF_ASM_OP0(fyl2x, 0xd9f1) - DEF_ASM_OP0(fptan, 0xd9f2) - DEF_ASM_OP0(fpatan, 0xd9f3) - DEF_ASM_OP0(fxtract, 0xd9f4) - DEF_ASM_OP0(fprem1, 0xd9f5) - DEF_ASM_OP0(fdecstp, 0xd9f6) - DEF_ASM_OP0(fincstp, 0xd9f7) - DEF_ASM_OP0(fprem, 0xd9f8) - DEF_ASM_OP0(fyl2xp1, 0xd9f9) - DEF_ASM_OP0(fsqrt, 0xd9fa) - DEF_ASM_OP0(fsincos, 0xd9fb) - DEF_ASM_OP0(frndint, 0xd9fc) - DEF_ASM_OP0(fscale, 0xd9fd) - DEF_ASM_OP0(fsin, 0xd9fe) - DEF_ASM_OP0(fcos, 0xd9ff) - DEF_ASM_OP0(fchs, 0xd9e0) - DEF_ASM_OP0(fabs, 0xd9e1) - DEF_ASM_OP0(fninit, 0xdbe3) - DEF_ASM_OP0(fnclex, 0xdbe2) - DEF_ASM_OP0(fnop, 0xd9d0) - - /* fp load */ - DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA) -ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA)) - DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA) - DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA) - - /* fp store */ - DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA) -ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA)) - DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA) - - DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA) - - /* exchange */ - DEF_ASM_OP0(fxch, 0xd9c9) -ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST)) - - /* misc FPU */ - DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST ) - DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST ) - - DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT) - DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ) - DEF_ASM_OP0(fnstsw, 0xdfe0) -ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX )) -ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA )) - DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX ) -ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT)) -ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )) - DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT) - DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA ) - DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA ) - DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST ) - DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST ) - DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA ) - - /* segments */ - DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA) - DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32) - DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG) - DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG) -ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG)) - DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG) - DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA) - DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA) - DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA) - DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA) - DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA) - -#ifdef I386_ASM_16 - /* 386 */ - DEF_ASM_OP0(loadall386, 0x0f07) -#endif - - /* 486 */ - DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 ) -ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA )) -ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA )) - DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA ) - - DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA) - DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA) - - /* pentium */ - DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA ) - - /* pentium pro */ - ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -#ifdef I386_ASM_16 -ALT(DEF_ASM_OP2(cmovno, 0x0f41, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovc, 0x0f42, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovnc, 0x0f43, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovz, 0x0f44, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovnz, 0x0f45, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovna, 0x0f46, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmova, 0x0f47, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -#endif - DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - - DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - - /* mmx */ - DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */ - - DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX ) -ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 )) - DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX )) - DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) /* must be last !OP0 */ - -#undef ALT -#undef DEF_ASM_OP0 -#undef DEF_ASM_OP0L -#undef DEF_ASM_OP1 -#undef DEF_ASM_OP2 -#undef DEF_ASM_OP3 diff --git a/src/x86/i386-gen.c b/src/x86/i386-gen.c deleted file mode 100644 index b8b12aa..0000000 --- a/src/x86/i386-gen.c +++ /dev/null @@ -1,1144 +0,0 @@ -/* - * X86 code generator for TCC - * - * Copyright (c) 2001-2004 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef TARGET_DEFS_ONLY - -/* number of available registers */ -#define NB_REGS 4 -#define NB_ASM_REGS 8 - -typedef int RegArgs; - -/* a register can belong to several classes. The classes must be - sorted from more general to more precise (see gv2() code which does - assumptions on it). */ -#define RC_INT 0x0001 /* generic integer register */ -#define RC_FLOAT 0x0002 /* generic float register */ -#define RC_EAX 0x0004 -#define RC_ST0 0x0008 -#define RC_ECX 0x0010 -#define RC_EDX 0x0020 -#define RC_IRET RC_EAX /* function return: integer register */ -#define RC_LRET RC_EDX /* function return: second integer register */ -#define RC_FRET RC_ST0 /* function return: float register */ - -/* pretty names for the registers */ -enum { - TREG_EAX = 0, - TREG_ECX, - TREG_EDX, - TREG_ST0, - TREG_ESP = 4 -}; - -/* return registers for function */ -#define REG_IRET TREG_EAX /* single word int return register */ -#define REG_LRET TREG_EDX /* second word return register (for long long) */ -#define REG_FRET TREG_ST0 /* float return register */ - -/* defined if function parameters must be evaluated in reverse order */ -#define INVERT_FUNC_PARAMS - -/* defined if structures are passed as pointers. Otherwise structures - are directly pushed on stack. */ -/* #define FUNC_STRUCT_PARAM_AS_PTR */ - -/* pointer size, in bytes */ -#define PTR_SIZE 4 - -/* long double size and alignment, in bytes */ -#define LDOUBLE_SIZE 12 -#define LDOUBLE_ALIGN 4 -/* maximum alignment (for aligned attribute support) */ -#define MAX_ALIGN 8 - - -#define psym oad - -/******************************************************/ -/* ELF defines */ - -#define EM_TCC_TARGET EM_386 - -/* relocation type for 32 bit data relocation */ -#define R_DATA_32 R_386_32 -#define R_DATA_PTR R_386_32 -#define R_JMP_SLOT R_386_JMP_SLOT -#define R_COPY R_386_COPY - -#define ELF_START_ADDR 0x08048000 -#define ELF_PAGE_SIZE 0x1000 - -/******************************************************/ -#else /* ! TARGET_DEFS_ONLY */ -/******************************************************/ -#include "../tcc.h" - -ST_DATA const int reg_classes[NB_REGS] = { - /* eax */ RC_INT | RC_EAX, - /* ecx */ RC_INT | RC_ECX, - /* edx */ RC_INT | RC_EDX, - /* st0 */ RC_FLOAT | RC_ST0, -}; - -static unsigned long func_sub_sp_offset; -static int func_ret_sub; -#ifdef CONFIG_TCC_BCHECK -static addr_t func_bound_offset; -#endif - -/* XXX: make it faster ? */ -ST_FUNC void g(int c) -{ - int ind1; - ind1 = ind + 1; - if (ind1 > cur_text_section->data_allocated) - section_realloc(cur_text_section, ind1); - cur_text_section->data[ind] = c; - ind = ind1; -} - -ST_FUNC void o(unsigned int c) -{ - while (c) { - g(c); - c = c >> 8; - } -} - -ST_FUNC void gen_le16(int v) -{ - g(v); - g(v >> 8); -} - -ST_FUNC void gen_le32(int c) -{ - g(c); - g(c >> 8); - g(c >> 16); - g(c >> 24); -} - -/* output a symbol and patch all calls to it */ -ST_FUNC void gsym_addr(int t, int a) -{ - int n, *ptr; - while (t) { - ptr = (int *)(cur_text_section->data + t); - n = *ptr; /* next value */ - *ptr = a - t - 4; - t = n; - } -} - -ST_FUNC void gsym(int t) -{ - gsym_addr(t, ind); -} - -/* psym is used to put an instruction with a data field which is a - reference to a symbol. It is in fact the same as oad ! */ -#define psym oad - -/* instruction + 4 bytes data. Return the address of the data */ -ST_FUNC int oad(int c, int s) -{ - int ind1; - - o(c); - ind1 = ind + 4; - if (ind1 > cur_text_section->data_allocated) - section_realloc(cur_text_section, ind1); - *(int *)(cur_text_section->data + ind) = s; - s = ind; - ind = ind1; - return s; -} - -/* output constant with relocation if 'r & VT_SYM' is true */ -ST_FUNC void gen_addr32(int r, Sym *sym, int c) -{ - if (r & VT_SYM) - greloc(cur_text_section, sym, ind, R_386_32); - gen_le32(c); -} - -ST_FUNC void gen_addrpc32(int r, Sym *sym, int c) -{ - if (r & VT_SYM) - greloc(cur_text_section, sym, ind, R_386_PC32); - gen_le32(c - 4); -} - -/* generate a modrm reference. 'op_reg' contains the addtionnal 3 - opcode bits */ -static void gen_modrm(int op_reg, int r, Sym *sym, int c) -{ - op_reg = op_reg << 3; - if ((r & VT_VALMASK) == VT_CONST) { - /* constant memory reference */ - o(0x05 | op_reg); - gen_addr32(r, sym, c); - } else if ((r & VT_VALMASK) == VT_LOCAL) { - /* currently, we use only ebp as base */ - if (c == (char)c) { - /* short reference */ - o(0x45 | op_reg); - g(c); - } else { - oad(0x85 | op_reg, c); - } - } else { - g(0x00 | op_reg | (r & VT_VALMASK)); - } -} - -/* load 'r' from value 'sv' */ -ST_FUNC void load(int r, SValue *sv) -{ - int v, t, ft, fc, fr; - SValue v1; - -#ifdef TCC_TARGET_PE - SValue v2; - sv = pe_getimport(sv, &v2); -#endif - - fr = sv->r; - ft = sv->type.t; - fc = sv->c.ul; - - v = fr & VT_VALMASK; - if (fr & VT_LVAL) { - if (v == VT_LLOCAL) { - v1.type.t = VT_INT; - v1.r = VT_LOCAL | VT_LVAL; - v1.c.ul = fc; - fr = r; - if (!(reg_classes[fr] & RC_INT)) - fr = get_reg(RC_INT); - load(fr, &v1); - } - if ((ft & VT_BTYPE) == VT_FLOAT) { - o(0xd9); /* flds */ - r = 0; - } else if ((ft & VT_BTYPE) == VT_DOUBLE) { - o(0xdd); /* fldl */ - r = 0; - } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { - o(0xdb); /* fldt */ - r = 5; - } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) { - o(0xbe0f); /* movsbl */ - } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { - o(0xb60f); /* movzbl */ - } else if ((ft & VT_TYPE) == VT_SHORT) { - o(0xbf0f); /* movswl */ - } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { - o(0xb70f); /* movzwl */ - } else { - o(0x8b); /* movl */ - } - gen_modrm(r, fr, sv->sym, fc); - } else { - if (v == VT_CONST) { - o(0xb8 + r); /* mov $xx, r */ - gen_addr32(fr, sv->sym, fc); - } else if (v == VT_LOCAL) { - if (fc) { - o(0x8d); /* lea xxx(%ebp), r */ - gen_modrm(r, VT_LOCAL, sv->sym, fc); - } else { - o(0x89); - o(0xe8 + r); /* mov %ebp, r */ - } - } else if (v == VT_CMP) { - oad(0xb8 + r, 0); /* mov $0, r */ - o(0x0f); /* setxx %br */ - o(fc); - o(0xc0 + r); - } else if (v == VT_JMP || v == VT_JMPI) { - t = v & 1; - oad(0xb8 + r, t); /* mov $1, r */ - o(0x05eb); /* jmp after */ - gsym(fc); - oad(0xb8 + r, t ^ 1); /* mov $0, r */ - } else if (v != r) { - o(0x89); - o(0xc0 + r + v * 8); /* mov v, r */ - } - } -} - -/* store register 'r' in lvalue 'v' */ -ST_FUNC void store(int r, SValue *v) -{ - int fr, bt, ft, fc; - -#ifdef TCC_TARGET_PE - SValue v2; - v = pe_getimport(v, &v2); -#endif - - ft = v->type.t; - fc = v->c.ul; - fr = v->r & VT_VALMASK; - bt = ft & VT_BTYPE; - /* XXX: incorrect if float reg to reg */ - if (bt == VT_FLOAT) { - o(0xd9); /* fsts */ - r = 2; - } else if (bt == VT_DOUBLE) { - o(0xdd); /* fstpl */ - r = 2; - } else if (bt == VT_LDOUBLE) { - o(0xc0d9); /* fld %st(0) */ - o(0xdb); /* fstpt */ - r = 7; - } else { - if (bt == VT_SHORT) - o(0x66); - if (bt == VT_BYTE || bt == VT_BOOL) - o(0x88); - else - o(0x89); - } - if (fr == VT_CONST || - fr == VT_LOCAL || - (v->r & VT_LVAL)) { - gen_modrm(r, v->r, v->sym, fc); - } else if (fr != r) { - o(0xc0 + fr + r * 8); /* mov r, fr */ - } -} - -static void gadd_sp(int val) -{ - if (val == (char)val) { - o(0xc483); - g(val); - } else { - oad(0xc481, val); /* add $xxx, %esp */ - } -} - -static void gen_static_call(int v) -{ - Sym *sym; - - sym = external_global_sym(v, &func_old_type, 0); - oad(0xe8, -4); - greloc(cur_text_section, sym, ind-4, R_386_PC32); -} - -/* 'is_jmp' is '1' if it is a jump */ -static void gcall_or_jmp(int is_jmp) -{ - int r; - if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { - /* constant case */ - if (vtop->r & VT_SYM) { - /* relocation case */ - greloc(cur_text_section, vtop->sym, - ind + 1, R_386_PC32); - } else { - /* put an empty PC32 relocation */ - put_elf_reloc(symtab_section, cur_text_section, - ind + 1, R_386_PC32, 0); - } - oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ - } else { - /* otherwise, indirect call */ - r = gv(RC_INT); - o(0xff); /* call/jmp *r */ - o(0xd0 + r + (is_jmp << 4)); - } -} - -static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; -static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; - -ST_FUNC int regargs_nregs(RegArgs *args) -{ - return *args; -} - -/* Return the number of registers needed to return the struct, or 0 if - returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) -{ -#ifdef TCC_TARGET_PE - int size, align; - - *ret_align = 1; // Never have to re-align return values for x86 - *regsize = 4; - size = type_size(vt, &align); - if (size > 8) { - *args = 0; - } else if (size > 4) { - ret->ref = NULL; - ret->t = VT_LLONG; - *args = 1; - } else { - ret->ref = NULL; - ret->t = VT_INT; - *args = 1; - } -#else - *ret_align = 1; // Never have to re-align return values for x86 - *args = 0; -#endif - - return *args != 0; -} - -/* Generate function call. The function address is pushed first, then - all the parameters in call order. This functions pops all the - parameters and the function address. */ -ST_FUNC void gfunc_call(int nb_args) -{ - int size, align, r, args_size, i, func_call; - Sym *func_sym; - - args_size = 0; - for(i = 0;i < nb_args; i++) { - if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { - size = type_size(&vtop->type, &align); - /* align to stack align size */ - size = (size + 3) & ~3; - /* allocate the necessary size on stack */ - oad(0xec81, size); /* sub $xxx, %esp */ - /* generate structure store */ - r = get_reg(RC_INT); - o(0x89); /* mov %esp, r */ - o(0xe0 + r); - vset(&vtop->type, r | VT_LVAL, 0); - vswap(); - vstore(); - args_size += size; - } else if (is_float(vtop->type.t)) { - gv(RC_FLOAT); /* only one float register */ - if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) - size = 4; - else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) - size = 8; - else - size = 12; - oad(0xec81, size); /* sub $xxx, %esp */ - if (size == 12) - o(0x7cdb); - else - o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */ - g(0x24); - g(0x00); - args_size += size; - } else { - /* simple type (currently always same size) */ - /* XXX: implicit cast ? */ - r = gv(RC_INT); - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { - size = 8; - o(0x50 + vtop->r2); /* push r */ - } else { - size = 4; - } - o(0x50 + r); /* push r */ - args_size += size; - } - vtop--; - } - save_regs(0); /* save used temporary registers */ - func_sym = vtop->type.ref; - func_call = func_sym->a.func_call; - /* fast call case */ - if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) || - func_call == FUNC_FASTCALLW) { - int fastcall_nb_regs; - uint8_t *fastcall_regs_ptr; - if (func_call == FUNC_FASTCALLW) { - fastcall_regs_ptr = fastcallw_regs; - fastcall_nb_regs = 2; - } else { - fastcall_regs_ptr = fastcall_regs; - fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; - } - for(i = 0;i < fastcall_nb_regs; i++) { - if (args_size <= 0) - break; - o(0x58 + fastcall_regs_ptr[i]); /* pop r */ - /* XXX: incorrect for struct/floats */ - args_size -= 4; - } - } -#ifndef TCC_TARGET_PE - else if ((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT) - args_size -= 4; -#endif - gcall_or_jmp(0); - - if (args_size && func_call != FUNC_STDCALL) - gadd_sp(args_size); - vtop--; -} - -#ifdef TCC_TARGET_PE -#define FUNC_PROLOG_SIZE 10 -#else -#define FUNC_PROLOG_SIZE 9 -#endif - -/* generate function prolog of type 't' */ -ST_FUNC void gfunc_prolog(CType *func_type) -{ - int addr, align, size, func_call, fastcall_nb_regs; - int param_index, param_addr; - uint8_t *fastcall_regs_ptr; - Sym *sym; - CType *type; - - sym = func_type->ref; - func_call = sym->a.func_call; - addr = 8; - loc = 0; - func_vc = 0; - - if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) { - fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; - fastcall_regs_ptr = fastcall_regs; - } else if (func_call == FUNC_FASTCALLW) { - fastcall_nb_regs = 2; - fastcall_regs_ptr = fastcallw_regs; - } else { - fastcall_nb_regs = 0; - fastcall_regs_ptr = NULL; - } - param_index = 0; - - ind += FUNC_PROLOG_SIZE; - func_sub_sp_offset = ind; - /* if the function returns a structure, then add an - implicit pointer parameter */ - func_vt = sym->type; - func_var = (sym->c == FUNC_ELLIPSIS); -#ifdef TCC_TARGET_PE - size = type_size(&func_vt,&align); - if (((func_vt.t & VT_BTYPE) == VT_STRUCT) && (size > 8)) { -#else - if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { -#endif - /* XXX: fastcall case ? */ - func_vc = addr; - addr += 4; - param_index++; - } - /* define parameters */ - while ((sym = sym->next) != NULL) { - type = &sym->type; - size = type_size(type, &align); - size = (size + 3) & ~3; -#ifdef FUNC_STRUCT_PARAM_AS_PTR - /* structs are passed as pointer */ - if ((type->t & VT_BTYPE) == VT_STRUCT) { - size = 4; - } -#endif - if (param_index < fastcall_nb_regs) { - /* save FASTCALL register */ - loc -= 4; - o(0x89); /* movl */ - gen_modrm(fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc); - param_addr = loc; - } else { - param_addr = addr; - addr += size; - } - sym_push(sym->v & ~SYM_FIELD, type, - VT_LOCAL | lvalue_type(type->t), param_addr); - param_index++; - } - func_ret_sub = 0; - /* pascal type call ? */ - if (func_call == FUNC_STDCALL) - func_ret_sub = addr - 8; -#ifndef TCC_TARGET_PE - else if (func_vc) - func_ret_sub = 4; -#endif - -#ifdef CONFIG_TCC_BCHECK - /* leave some room for bound checking code */ - if (tcc_state->do_bounds_check) { - oad(0xb8, 0); /* lbound section pointer */ - oad(0xb8, 0); /* call to function */ - func_bound_offset = lbounds_section->data_offset; - } -#endif -} - -/* generate function epilog */ -ST_FUNC void gfunc_epilog(void) -{ - addr_t v, saved_ind; - -#ifdef CONFIG_TCC_BCHECK - if (tcc_state->do_bounds_check - && func_bound_offset != lbounds_section->data_offset) { - addr_t saved_ind; - addr_t *bounds_ptr; - Sym *sym_data; - /* add end of table info */ - bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); - *bounds_ptr = 0; - /* generate bound local allocation */ - saved_ind = ind; - ind = func_sub_sp_offset; - sym_data = get_sym_ref(&char_pointer_type, lbounds_section, - func_bound_offset, lbounds_section->data_offset); - greloc(cur_text_section, sym_data, - ind + 1, R_386_32); - oad(0xb8, 0); /* mov %eax, xxx */ - gen_static_call(TOK___bound_local_new); - - ind = saved_ind; - /* generate bound check local freeing */ - o(0x5250); /* save returned value, if any */ - greloc(cur_text_section, sym_data, - ind + 1, R_386_32); - oad(0xb8, 0); /* mov %eax, xxx */ - gen_static_call(TOK___bound_local_delete); - - o(0x585a); /* restore returned value, if any */ - } -#endif - o(0xc9); /* leave */ - if (func_ret_sub == 0) { - o(0xc3); /* ret */ - } else { - o(0xc2); /* ret n */ - g(func_ret_sub); - g(func_ret_sub >> 8); - } - /* align local size to word & save local variables */ - - v = (-loc + 3) & -4; - saved_ind = ind; - ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; -#ifdef TCC_TARGET_PE - if (v >= 4096) { - oad(0xb8, v); /* mov stacksize, %eax */ - gen_static_call(TOK___chkstk); /* call __chkstk, (does the stackframe too) */ - } else -#endif - { - o(0xe58955); /* push %ebp, mov %esp, %ebp */ - o(0xec81); /* sub esp, stacksize */ - gen_le32(v); -#if FUNC_PROLOG_SIZE == 10 - o(0x90); /* adjust to FUNC_PROLOG_SIZE */ -#endif - } - ind = saved_ind; -} - -/* generate a jump to a label */ -ST_FUNC int gjmp(int t) -{ - return psym(0xe9, t); -} - -/* generate a jump to a fixed address */ -ST_FUNC void gjmp_addr(int a) -{ - int r; - r = a - ind - 2; - if (r == (char)r) { - g(0xeb); - g(r); - } else { - oad(0xe9, a - ind - 5); - } -} - -/* generate a test. set 'inv' to invert test. Stack entry is popped */ -ST_FUNC int gtst(int inv, int t) -{ - int v, *p; - - v = vtop->r & VT_VALMASK; - if (v == VT_CMP) { - /* fast case : can jump directly since flags are set */ - g(0x0f); - t = psym((vtop->c.i - 16) ^ inv, t); - } else if (v == VT_JMP || v == VT_JMPI) { - /* && or || optimization */ - if ((v & 1) == inv) { - /* insert vtop->c jump list in t */ - p = &vtop->c.i; - while (*p != 0) - p = (int *)(cur_text_section->data + *p); - *p = t; - t = vtop->c.i; - } else { - t = gjmp(t); - gsym(vtop->c.i); - } - } - vtop--; - return t; -} - -/* generate an integer binary operation */ -ST_FUNC void gen_opi(int op) -{ - int r, fr, opc, c; - - switch(op) { - case '+': - case TOK_ADDC1: /* add with carry generation */ - opc = 0; - gen_op8: - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant case */ - vswap(); - r = gv(RC_INT); - vswap(); - c = vtop->c.i; - if (c == (char)c) { - /* generate inc and dec for smaller code */ - if (c==1 && opc==0) { - o (0x40 | r); // inc - } else if (c==1 && opc==5) { - o (0x48 | r); // dec - } else { - o(0x83); - o(0xc0 | (opc << 3) | r); - g(c); - } - } else { - o(0x81); - oad(0xc0 | (opc << 3) | r, c); - } - } else { - gv2(RC_INT, RC_INT); - r = vtop[-1].r; - fr = vtop[0].r; - o((opc << 3) | 0x01); - o(0xc0 + r + fr * 8); - } - vtop--; - if (op >= TOK_ULT && op <= TOK_GT) { - vtop->r = VT_CMP; - vtop->c.i = op; - } - break; - case '-': - case TOK_SUBC1: /* sub with carry generation */ - opc = 5; - goto gen_op8; - case TOK_ADDC2: /* add with carry use */ - opc = 2; - goto gen_op8; - case TOK_SUBC2: /* sub with carry use */ - opc = 3; - goto gen_op8; - case '&': - opc = 4; - goto gen_op8; - case '^': - opc = 6; - goto gen_op8; - case '|': - opc = 1; - goto gen_op8; - case '*': - gv2(RC_INT, RC_INT); - r = vtop[-1].r; - fr = vtop[0].r; - vtop--; - o(0xaf0f); /* imul fr, r */ - o(0xc0 + fr + r * 8); - break; - case TOK_SHL: - opc = 4; - goto gen_shift; - case TOK_SHR: - opc = 5; - goto gen_shift; - case TOK_SAR: - opc = 7; - gen_shift: - opc = 0xc0 | (opc << 3); - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant case */ - vswap(); - r = gv(RC_INT); - vswap(); - c = vtop->c.i & 0x1f; - o(0xc1); /* shl/shr/sar $xxx, r */ - o(opc | r); - g(c); - } else { - /* we generate the shift in ecx */ - gv2(RC_INT, RC_ECX); - r = vtop[-1].r; - o(0xd3); /* shl/shr/sar %cl, r */ - o(opc | r); - } - vtop--; - break; - case '/': - case TOK_UDIV: - case TOK_PDIV: - case '%': - case TOK_UMOD: - case TOK_UMULL: - /* first operand must be in eax */ - /* XXX: need better constraint for second operand */ - gv2(RC_EAX, RC_ECX); - r = vtop[-1].r; - fr = vtop[0].r; - vtop--; - save_reg(TREG_EDX); - if (op == TOK_UMULL) { - o(0xf7); /* mul fr */ - o(0xe0 + fr); - vtop->r2 = TREG_EDX; - r = TREG_EAX; - } else { - if (op == TOK_UDIV || op == TOK_UMOD) { - o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ - o(0xf0 + fr); - } else { - o(0xf799); /* cltd, idiv fr, %eax */ - o(0xf8 + fr); - } - if (op == '%' || op == TOK_UMOD) - r = TREG_EDX; - else - r = TREG_EAX; - } - vtop->r = r; - break; - default: - opc = 7; - goto gen_op8; - } -} - -/* generate a floating point operation 'v = t1 op t2' instruction. The - two operands are guaranted to have the same floating point type */ -/* XXX: need to use ST1 too */ -ST_FUNC void gen_opf(int op) -{ - int a, ft, fc, swapped, r; - - /* convert constants to memory references */ - if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { - vswap(); - gv(RC_FLOAT); - vswap(); - } - if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) - gv(RC_FLOAT); - - /* must put at least one value in the floating point register */ - if ((vtop[-1].r & VT_LVAL) && - (vtop[0].r & VT_LVAL)) { - vswap(); - gv(RC_FLOAT); - vswap(); - } - swapped = 0; - /* swap the stack if needed so that t1 is the register and t2 is - the memory reference */ - if (vtop[-1].r & VT_LVAL) { - vswap(); - swapped = 1; - } - if (op >= TOK_ULT && op <= TOK_GT) { - /* load on stack second operand */ - load(TREG_ST0, vtop); - save_reg(TREG_EAX); /* eax is used by FP comparison code */ - if (op == TOK_GE || op == TOK_GT) - swapped = !swapped; - else if (op == TOK_EQ || op == TOK_NE) - swapped = 0; - if (swapped) - o(0xc9d9); /* fxch %st(1) */ - if (op == TOK_EQ || op == TOK_NE) - o(0xe9da); /* fucompp */ - else - o(0xd9de); /* fcompp */ - o(0xe0df); /* fnstsw %ax */ - if (op == TOK_EQ) { - o(0x45e480); /* and $0x45, %ah */ - o(0x40fC80); /* cmp $0x40, %ah */ - } else if (op == TOK_NE) { - o(0x45e480); /* and $0x45, %ah */ - o(0x40f480); /* xor $0x40, %ah */ - op = TOK_NE; - } else if (op == TOK_GE || op == TOK_LE) { - o(0x05c4f6); /* test $0x05, %ah */ - op = TOK_EQ; - } else { - o(0x45c4f6); /* test $0x45, %ah */ - op = TOK_EQ; - } - vtop--; - vtop->r = VT_CMP; - vtop->c.i = op; - } else { - /* no memory reference possible for long double operations */ - if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { - load(TREG_ST0, vtop); - swapped = !swapped; - } - - switch(op) { - default: - case '+': - a = 0; - break; - case '-': - a = 4; - if (swapped) - a++; - break; - case '*': - a = 1; - break; - case '/': - a = 6; - if (swapped) - a++; - break; - } - ft = vtop->type.t; - fc = vtop->c.ul; - if ((ft & VT_BTYPE) == VT_LDOUBLE) { - o(0xde); /* fxxxp %st, %st(1) */ - o(0xc1 + (a << 3)); - } else { - /* if saved lvalue, then we must reload it */ - r = vtop->r; - if ((r & VT_VALMASK) == VT_LLOCAL) { - SValue v1; - r = get_reg(RC_INT); - v1.type.t = VT_INT; - v1.r = VT_LOCAL | VT_LVAL; - v1.c.ul = fc; - load(r, &v1); - fc = 0; - } - - if ((ft & VT_BTYPE) == VT_DOUBLE) - o(0xdc); - else - o(0xd8); - gen_modrm(a, r, vtop->sym, fc); - } - vtop--; - } -} - -/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' - and 'long long' cases. */ -ST_FUNC void gen_cvt_itof(int t) -{ - save_reg(TREG_ST0); - gv(RC_INT); - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { - /* signed long long to float/double/long double (unsigned case - is handled generically) */ - o(0x50 + vtop->r2); /* push r2 */ - o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ - o(0x242cdf); /* fildll (%esp) */ - o(0x08c483); /* add $8, %esp */ - } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == - (VT_INT | VT_UNSIGNED)) { - /* unsigned int to float/double/long double */ - o(0x6a); /* push $0 */ - g(0x00); - o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ - o(0x242cdf); /* fildll (%esp) */ - o(0x08c483); /* add $8, %esp */ - } else { - /* int to float/double/long double */ - o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ - o(0x2404db); /* fildl (%esp) */ - o(0x04c483); /* add $4, %esp */ - } - vtop->r = TREG_ST0; -} - -/* convert fp to int 't' type */ -ST_FUNC void gen_cvt_ftoi(int t) -{ - #ifndef COMMIT_4ad186c5ef61_IS_FIXED - /* a good version but it takes a more time to execute */ - gv(RC_FLOAT); - save_reg(TREG_EAX); - save_reg(TREG_EDX); - gen_static_call(TOK___tcc_cvt_ftol); - vtop->r = TREG_EAX; /* mark reg as used */ - if (t == VT_LLONG) - vtop->r2 = TREG_EDX; - #else - /* a new version with a bug: t2a = 44100312 */ - /* - #include<stdio.h> - int main() { - int t1 = 176401255; - float f = 0.25f; - int t2a = (int)(t1 * f); // must be 44100313 - int t2b = (int)(t1 * (float)0.25f); - printf("t2a=%d t2b=%d \n",t2a,t2b); - return 0; - } - */ - int bt = vtop->type.t & VT_BTYPE; - if (bt == VT_FLOAT) - vpush_global_sym(&func_old_type, TOK___fixsfdi); - else if (bt == VT_LDOUBLE) - vpush_global_sym(&func_old_type, TOK___fixxfdi); - else - vpush_global_sym(&func_old_type, TOK___fixdfdi); - vswap(); - gfunc_call(1); - vpushi(0); - vtop->r = REG_IRET; - vtop->r2 = REG_LRET; - #endif -} - -/* convert from one floating point type to another */ -ST_FUNC void gen_cvt_ftof(int t) -{ - /* all we have to do on i386 is to put the float in a register */ - gv(RC_FLOAT); -} - -/* computed goto support */ -ST_FUNC void ggoto(void) -{ - gcall_or_jmp(1); - vtop--; -} - -/* bound check support functions */ -#ifdef CONFIG_TCC_BCHECK - -/* generate a bounded pointer addition */ -ST_FUNC void gen_bounded_ptr_add(void) -{ - /* prepare fast i386 function call (args in eax and edx) */ - gv2(RC_EAX, RC_EDX); - /* save all temporary registers */ - vtop -= 2; - save_regs(0); - /* do a fast function call */ - gen_static_call(TOK___bound_ptr_add); - /* returned pointer is in eax */ - vtop++; - vtop->r = TREG_EAX | VT_BOUNDED; - /* address of bounding function call point */ - vtop->c.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel)); -} - -/* patch pointer addition in vtop so that pointer dereferencing is - also tested */ -ST_FUNC void gen_bounded_ptr_deref(void) -{ - addr_t func; - addr_t size, align; - Elf32_Rel *rel; - Sym *sym; - - size = 0; - /* XXX: put that code in generic part of tcc */ - if (!is_float(vtop->type.t)) { - if (vtop->r & VT_LVAL_BYTE) - size = 1; - else if (vtop->r & VT_LVAL_SHORT) - size = 2; - } - if (!size) - size = type_size(&vtop->type, &align); - switch(size) { - case 1: func = TOK___bound_ptr_indir1; break; - case 2: func = TOK___bound_ptr_indir2; break; - case 4: func = TOK___bound_ptr_indir4; break; - case 8: func = TOK___bound_ptr_indir8; break; - case 12: func = TOK___bound_ptr_indir12; break; - case 16: func = TOK___bound_ptr_indir16; break; - default: - tcc_error("unhandled size when dereferencing bounded pointer"); - func = 0; - break; - } - - /* patch relocation */ - /* XXX: find a better solution ? */ - rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.ul); - sym = external_global_sym(func, &func_old_type, 0); - if (!sym->c) - put_extern_sym(sym, NULL, 0, 0); - rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info)); -} -#endif - -/* Save the stack pointer onto the stack */ -ST_FUNC void gen_vla_sp_save(int addr) { - /* mov %esp,addr(%ebp)*/ - o(0x89); - gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr); -} - -/* Restore the SP from a location on the stack */ -ST_FUNC void gen_vla_sp_restore(int addr) { - o(0x8b); - gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr); -} - -/* Subtract from the stack pointer, and push the resulting value onto the stack */ -ST_FUNC void gen_vla_alloc(CType *type, int align) { -#ifdef TCC_TARGET_PE - /* alloca does more than just adjust %rsp on Windows */ - vpush_global_sym(&func_old_type, TOK_alloca); - vswap(); /* Move alloca ref past allocation size */ - gfunc_call(1); -#else - int r; - r = gv(RC_INT); /* allocation size */ - /* sub r,%rsp */ - o(0x2b); - o(0xe0 | r); - /* We align to 16 bytes rather than align */ - /* and ~15, %esp */ - o(0xf0e483); - vpop(); -#endif -} - -/* end of X86 code generator */ -/*************************************************************/ -#endif -/*************************************************************/ diff --git a/src/x86/i386-tok.h b/src/x86/i386-tok.h deleted file mode 100644 index b0ca3ed..0000000 --- a/src/x86/i386-tok.h +++ /dev/null @@ -1,244 +0,0 @@ -/* ------------------------------------------------------------------ */ -/* WARNING: relative order of tokens is important. */ - -/* register */ - DEF_ASM(al) - DEF_ASM(cl) - DEF_ASM(dl) - DEF_ASM(bl) - DEF_ASM(ah) - DEF_ASM(ch) - DEF_ASM(dh) - DEF_ASM(bh) - DEF_ASM(ax) - DEF_ASM(cx) - DEF_ASM(dx) - DEF_ASM(bx) - DEF_ASM(sp) - DEF_ASM(bp) - DEF_ASM(si) - DEF_ASM(di) - DEF_ASM(eax) - DEF_ASM(ecx) - DEF_ASM(edx) - DEF_ASM(ebx) - DEF_ASM(esp) - DEF_ASM(ebp) - DEF_ASM(esi) - DEF_ASM(edi) -#ifdef TCC_TARGET_X86_64 - DEF_ASM(rax) - DEF_ASM(rcx) - DEF_ASM(rdx) - DEF_ASM(rbx) - DEF_ASM(rsp) - DEF_ASM(rbp) - DEF_ASM(rsi) - DEF_ASM(rdi) -#endif - DEF_ASM(mm0) - DEF_ASM(mm1) - DEF_ASM(mm2) - DEF_ASM(mm3) - DEF_ASM(mm4) - DEF_ASM(mm5) - DEF_ASM(mm6) - DEF_ASM(mm7) - DEF_ASM(xmm0) - DEF_ASM(xmm1) - DEF_ASM(xmm2) - DEF_ASM(xmm3) - DEF_ASM(xmm4) - DEF_ASM(xmm5) - DEF_ASM(xmm6) - DEF_ASM(xmm7) - DEF_ASM(cr0) - DEF_ASM(cr1) - DEF_ASM(cr2) - DEF_ASM(cr3) - DEF_ASM(cr4) - DEF_ASM(cr5) - DEF_ASM(cr6) - DEF_ASM(cr7) - DEF_ASM(tr0) - DEF_ASM(tr1) - DEF_ASM(tr2) - DEF_ASM(tr3) - DEF_ASM(tr4) - DEF_ASM(tr5) - DEF_ASM(tr6) - DEF_ASM(tr7) - DEF_ASM(db0) - DEF_ASM(db1) - DEF_ASM(db2) - DEF_ASM(db3) - DEF_ASM(db4) - DEF_ASM(db5) - DEF_ASM(db6) - DEF_ASM(db7) - DEF_ASM(dr0) - DEF_ASM(dr1) - DEF_ASM(dr2) - DEF_ASM(dr3) - DEF_ASM(dr4) - DEF_ASM(dr5) - DEF_ASM(dr6) - DEF_ASM(dr7) - DEF_ASM(es) - DEF_ASM(cs) - DEF_ASM(ss) - DEF_ASM(ds) - DEF_ASM(fs) - DEF_ASM(gs) - DEF_ASM(st) - - /* generic two operands */ - DEF_BWLX(mov) - - DEF_BWLX(add) - DEF_BWLX(or) - DEF_BWLX(adc) - DEF_BWLX(sbb) - DEF_BWLX(and) - DEF_BWLX(sub) - DEF_BWLX(xor) - DEF_BWLX(cmp) - - /* unary ops */ - DEF_BWLX(inc) - DEF_BWLX(dec) - DEF_BWLX(not) - DEF_BWLX(neg) - DEF_BWLX(mul) - DEF_BWLX(imul) - DEF_BWLX(div) - DEF_BWLX(idiv) - - DEF_BWLX(xchg) - DEF_BWLX(test) - - /* shifts */ - DEF_BWLX(rol) - DEF_BWLX(ror) - DEF_BWLX(rcl) - DEF_BWLX(rcr) - DEF_BWLX(shl) - DEF_BWLX(shr) - DEF_BWLX(sar) - - DEF_ASM(shldw) - DEF_ASM(shldl) - DEF_ASM(shld) - DEF_ASM(shrdw) - DEF_ASM(shrdl) - DEF_ASM(shrd) - - DEF_ASM(pushw) - DEF_ASM(pushl) -#ifdef TCC_TARGET_X86_64 - DEF_ASM(pushq) -#endif - DEF_ASM(push) - - DEF_ASM(popw) - DEF_ASM(popl) -#ifdef TCC_TARGET_X86_64 - DEF_ASM(popq) -#endif - DEF_ASM(pop) - - DEF_BWL(in) - DEF_BWL(out) - - DEF_WL(movzb) - DEF_ASM(movzwl) - DEF_ASM(movsbw) - DEF_ASM(movsbl) - DEF_ASM(movswl) -#ifdef TCC_TARGET_X86_64 - DEF_ASM(movslq) -#endif - - DEF_WLX(lea) - - DEF_ASM(les) - DEF_ASM(lds) - DEF_ASM(lss) - DEF_ASM(lfs) - DEF_ASM(lgs) - - DEF_ASM(call) - DEF_ASM(jmp) - DEF_ASM(lcall) - DEF_ASM(ljmp) - - DEF_ASMTEST(j,) - - DEF_ASMTEST(set,) - DEF_ASMTEST(set,b) - DEF_ASMTEST(cmov,) - - DEF_WLX(bsf) - DEF_WLX(bsr) - DEF_WLX(bt) - DEF_WLX(bts) - DEF_WLX(btr) - DEF_WLX(btc) - - DEF_WLX(lsl) - - /* generic FP ops */ - DEF_FP(add) - DEF_FP(mul) - - DEF_ASM(fcom) - DEF_ASM(fcom_1) /* non existent op, just to have a regular table */ - DEF_FP1(com) - - DEF_FP(comp) - DEF_FP(sub) - DEF_FP(subr) - DEF_FP(div) - DEF_FP(divr) - - DEF_BWLX(xadd) - DEF_BWLX(cmpxchg) - - /* string ops */ - DEF_BWLX(cmps) - DEF_BWLX(scmp) - DEF_BWL(ins) - DEF_BWL(outs) - DEF_BWLX(lods) - DEF_BWLX(slod) - DEF_BWLX(movs) - DEF_BWLX(smov) - DEF_BWLX(scas) - DEF_BWLX(ssca) - DEF_BWLX(stos) - DEF_BWLX(ssto) - - /* generic asm ops */ -#define ALT(x) -#define DEF_ASM_OP0(name, opcode) DEF_ASM(name) -#define DEF_ASM_OP0L(name, opcode, group, instr_type) -#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) -#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) -#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) -#ifdef TCC_TARGET_X86_64 -# include "x86_64-asm.h" -#else -# include "i386-asm.h" -#endif - -#define ALT(x) -#define DEF_ASM_OP0(name, opcode) -#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name) -#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name) -#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name) -#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name) -#ifdef TCC_TARGET_X86_64 -# include "x86_64-asm.h" -#else -# include "i386-asm.h" -#endif diff --git a/src/x86/x86_64-asm.h b/src/x86/x86_64-asm.h deleted file mode 100644 index 2f80e4b..0000000 --- a/src/x86/x86_64-asm.h +++ /dev/null @@ -1,449 +0,0 @@ - DEF_ASM_OP0(clc, 0xf8) /* must be first OP0 */ - DEF_ASM_OP0(cld, 0xfc) - DEF_ASM_OP0(cli, 0xfa) - DEF_ASM_OP0(clts, 0x0f06) - DEF_ASM_OP0(cmc, 0xf5) - DEF_ASM_OP0(lahf, 0x9f) - DEF_ASM_OP0(sahf, 0x9e) - DEF_ASM_OP0(pushfl, 0x9c) - DEF_ASM_OP0(popfl, 0x9d) - DEF_ASM_OP0(pushf, 0x9c) - DEF_ASM_OP0(popf, 0x9d) - DEF_ASM_OP0(stc, 0xf9) - DEF_ASM_OP0(std, 0xfd) - DEF_ASM_OP0(sti, 0xfb) - DEF_ASM_OP0(aaa, 0x37) - DEF_ASM_OP0(aas, 0x3f) - DEF_ASM_OP0(daa, 0x27) - DEF_ASM_OP0(das, 0x2f) - DEF_ASM_OP0(aad, 0xd50a) - DEF_ASM_OP0(aam, 0xd40a) - DEF_ASM_OP0(cbw, 0x6698) - DEF_ASM_OP0(cwd, 0x6699) - DEF_ASM_OP0(cwde, 0x98) - DEF_ASM_OP0(cdq, 0x99) - DEF_ASM_OP0(cbtw, 0x6698) - DEF_ASM_OP0(cwtl, 0x98) - DEF_ASM_OP0(cwtd, 0x6699) - DEF_ASM_OP0(cltd, 0x99) - DEF_ASM_OP0(cqto, 0x4899) - DEF_ASM_OP0(int3, 0xcc) - DEF_ASM_OP0(into, 0xce) - DEF_ASM_OP0(iret, 0xcf) - DEF_ASM_OP0(rsm, 0x0faa) - DEF_ASM_OP0(hlt, 0xf4) - DEF_ASM_OP0(wait, 0x9b) - DEF_ASM_OP0(nop, 0x90) - DEF_ASM_OP0(xlat, 0xd7) - - /* strings */ -ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWLQ)) -ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWLQ)) - -ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWLQ)) -ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWLQ)) - -ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWLQ)) -ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWLQ)) - -ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWLQ)) -ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWLQ)) - -ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWLQ)) -ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWLQ)) - -ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWLQ)) -ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWLQ)) - - /* bits */ - -ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA, OPT_REGW)) -ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA, OPT_REGW)) - -ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA)) - -ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA)) - -ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA)) - -ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA)) - - /* prefixes */ - DEF_ASM_OP0(lock, 0xf0) - DEF_ASM_OP0(rep, 0xf3) - DEF_ASM_OP0(repe, 0xf3) - DEF_ASM_OP0(repz, 0xf3) - DEF_ASM_OP0(repne, 0xf2) - DEF_ASM_OP0(repnz, 0xf2) - - DEF_ASM_OP0(invd, 0x0f08) - DEF_ASM_OP0(wbinvd, 0x0f09) - DEF_ASM_OP0(cpuid, 0x0fa2) - DEF_ASM_OP0(wrmsr, 0x0f30) - DEF_ASM_OP0(rdtsc, 0x0f31) - DEF_ASM_OP0(rdmsr, 0x0f32) - DEF_ASM_OP0(rdpmc, 0x0f33) - DEF_ASM_OP0(ud2, 0x0f0b) - - /* NOTE: we took the same order as gas opcode definition order */ -ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWLQ, OPT_ADDR, OPT_EAX)) -ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWLQ, OPT_EAX, OPT_ADDR)) -ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG)) -ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWLQ, OPT_IM, OPT_REG)) -ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWLQ, OPT_IM, OPT_REG | OPT_EA)) - -ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WLQ, OPT_SEG, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WLQ, OPT_EA | OPT_REG, OPT_SEG)) - -ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WLQ, OPT_CR, OPT_REG64)) -ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WLQ, OPT_DB, OPT_REG64)) -ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WLQ, OPT_TR, OPT_REG64)) -ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_CR)) -ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_DB)) -ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_TR)) - -ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16)) -ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(movslq, 0x4863, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG)) -ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW)) -ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32)) - -ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WLQ, OPT_REG64)) -ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WLQ, OPT_REG64 | OPT_EA)) -ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WLQ, OPT_IM32)) -ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WLQ, OPT_SEG)) - DEF_ASM_OP1(pushb, 0x6a, 0, OPC_B, OPT_IM8S) - -ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WLQ, OPT_REGW)) -ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA)) -ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WLQ, OPT_SEG)) - -ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLQ, OPT_REG, OPT_EAX)) -ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLQ, OPT_EAX, OPT_REG)) -ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG)) - -ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX)) -ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8)) -ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX)) -ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX)) - -ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8)) -ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8)) -ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX)) -ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX)) - -ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WLQ, OPT_EA, OPT_REG)) - -ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32)) - - /* arith */ -ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */ -ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG)) -ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWLQ, OPT_IMNO64, OPT_EAX)) -ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_IMNO64, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_EA | OPT_REG)) - -ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG)) -ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWLQ, OPT_IMNO64, OPT_EAX)) -ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWLQ, OPT_IMNO64, OPT_EA | OPT_REG)) - -ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WLQ, OPT_REGW)) -ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WLQ, OPT_REGW)) -ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) - -ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) - -ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) - -ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WLQ, OPT_REG | OPT_EA, OPT_REG)) -ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW)) -ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_REGW)) -ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WLQ, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW)) -ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WLQ, OPT_IMW, OPT_REGW)) - -ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA, OPT_EAX)) -ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) -ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA, OPT_EAX)) - - /* shifts */ -ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG)) -ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_EA | OPT_REG)) - -ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLQ, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLQ, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) -ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_EA | OPT_REGW)) - -ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR)) -ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR)) -ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR)) -ALT(DEF_ASM_OP1(jmp, 0xff, 0, OPC_JMP | OPC_WL, OPT_REGW)) -ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR)) - -ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA)) -ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA)) - -ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8)) -ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA)) -ALT(DEF_ASM_OP1(setob, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA)) - DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8) - DEF_ASM_OP0(leave, 0xc9) - DEF_ASM_OP0(ret, 0xc3) -ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16)) - DEF_ASM_OP0(lret, 0xcb) -ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16)) - -ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR)) - DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR) - DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR) - - /* float */ - /* specific fcomp handling */ -ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0)) - -ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST)) -ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0)) -ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH)) -ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST)) -ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0)) -ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST)) -ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH)) -ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) -ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) -ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) -ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) - - DEF_ASM_OP0(fucompp, 0xdae9) - DEF_ASM_OP0(ftst, 0xd9e4) - DEF_ASM_OP0(fxam, 0xd9e5) - DEF_ASM_OP0(fld1, 0xd9e8) - DEF_ASM_OP0(fldl2t, 0xd9e9) - DEF_ASM_OP0(fldl2e, 0xd9ea) - DEF_ASM_OP0(fldpi, 0xd9eb) - DEF_ASM_OP0(fldlg2, 0xd9ec) - DEF_ASM_OP0(fldln2, 0xd9ed) - DEF_ASM_OP0(fldz, 0xd9ee) - - DEF_ASM_OP0(f2xm1, 0xd9f0) - DEF_ASM_OP0(fyl2x, 0xd9f1) - DEF_ASM_OP0(fptan, 0xd9f2) - DEF_ASM_OP0(fpatan, 0xd9f3) - DEF_ASM_OP0(fxtract, 0xd9f4) - DEF_ASM_OP0(fprem1, 0xd9f5) - DEF_ASM_OP0(fdecstp, 0xd9f6) - DEF_ASM_OP0(fincstp, 0xd9f7) - DEF_ASM_OP0(fprem, 0xd9f8) - DEF_ASM_OP0(fyl2xp1, 0xd9f9) - DEF_ASM_OP0(fsqrt, 0xd9fa) - DEF_ASM_OP0(fsincos, 0xd9fb) - DEF_ASM_OP0(frndint, 0xd9fc) - DEF_ASM_OP0(fscale, 0xd9fd) - DEF_ASM_OP0(fsin, 0xd9fe) - DEF_ASM_OP0(fcos, 0xd9ff) - DEF_ASM_OP0(fchs, 0xd9e0) - DEF_ASM_OP0(fabs, 0xd9e1) - DEF_ASM_OP0(fninit, 0xdbe3) - DEF_ASM_OP0(fnclex, 0xdbe2) - DEF_ASM_OP0(fnop, 0xd9d0) - DEF_ASM_OP0(fwait, 0x9b) - - /* fp load */ - DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA) -ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA)) - DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA) - DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA) - - /* fp store */ - DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA) -ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA)) - DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA) - - DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST) - DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA) - - /* exchange */ - DEF_ASM_OP0(fxch, 0xd9c9) -ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST)) - - /* misc FPU */ - DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST ) - DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST ) - - DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT) - DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ) - DEF_ASM_OP0(fnstsw, 0xdfe0) -ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX )) -ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA )) - DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX ) -ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT)) -ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )) - DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT) - DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA ) - DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA ) - DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST ) - DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST ) - DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA ) - DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA ) - - /* segments */ - DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA) - DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32) - DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG) - DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG) -ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG)) - DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG) - DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA) - DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA) - DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA) - DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA) - DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA) - DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA) - - /* 486 */ - DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 ) -ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA )) -ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA )) - DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA ) - - DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA) - DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA) - - /* pentium */ - DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA ) - - /* pentium pro */ -ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovno, 0x0f41, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovc, 0x0f42, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovnc, 0x0f43, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovz, 0x0f44, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovnz, 0x0f45, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmovna, 0x0f46, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) -ALT(DEF_ASM_OP2(cmova, 0x0f47, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) - - DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - - DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 ) - DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 ) - - /* mmx */ - DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */ - DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX ) -ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 )) -ALT(DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )) -ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX )) - DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) -ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) - DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) - DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) /* must be last !OP0 */ - -#undef ALT -#undef DEF_ASM_OP0 -#undef DEF_ASM_OP0L -#undef DEF_ASM_OP1 -#undef DEF_ASM_OP2 -#undef DEF_ASM_OP3 diff --git a/src/x86/x86_64-gen.c b/src/x86/x86_64-gen.c deleted file mode 100644 index ab2eb6a..0000000 --- a/src/x86/x86_64-gen.c +++ /dev/null @@ -1,2359 +0,0 @@ -/* - * x86-64 code generator for TCC - * - * Copyright (c) 2008 Shinichiro Hamaji - * - * Based on i386-gen.c by Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef TARGET_DEFS_ONLY - -/* number of available registers */ -#define NB_REGS 25 -#define NB_ASM_REGS 8 -#define REG_ARGS_MAX 2 /* at most 2 registers used for each argument */ - -#ifdef TCC_TARGET_PE -typedef int RegArgs; -#else -/* This struct stores the struct offsets at which %rax, %rdx, %xmm0, and - * %xmm1 are to be stored. - * - * struct { long long l; double x; }: ireg = { 0, -1 } freg = { 8, -1 } - * struct { double x; long long l; }: ireg = { 8, -1 } freg = { 0, -1 } - * struct { long long l; long long l2; }: ireg = { 0, 8 } freg = { -1, -1 } - * struct { double x; double x2; }: ireg = { -1, -1 } freg = { 0, 8 } - */ -typedef struct { - int ireg[REG_ARGS_MAX]; - int freg[REG_ARGS_MAX]; -} RegArgs; -#endif - -/* a register can belong to several classes. The classes must be - sorted from more general to more precise (see gv2() code which does - assumptions on it). */ -#define RC_INT 0x0001 /* generic integer register */ -#define RC_FLOAT 0x0002 /* generic float register */ -#define RC_RAX 0x0004 -#define RC_RCX 0x0008 -#define RC_RDX 0x0010 -#define RC_ST0 0x0080 /* only for long double */ -#define RC_R8 0x0100 -#define RC_R9 0x0200 -#define RC_R10 0x0400 -#define RC_R11 0x0800 -#define RC_XMM0 0x1000 -#define RC_XMM1 0x2000 -#define RC_XMM2 0x4000 -#define RC_XMM3 0x8000 -#define RC_XMM4 0x10000 -#define RC_XMM5 0x20000 -#define RC_XMM6 0x40000 -#define RC_XMM7 0x80000 -#define RC_IRET RC_RAX /* function return: integer register */ -#define RC_LRET RC_RDX /* function return: second integer register */ -#define RC_FRET RC_XMM0 /* function return: float register */ -#define RC_QRET RC_XMM1 /* function return: second float register */ - -/* pretty names for the registers */ -enum { - TREG_RAX = 0, - TREG_RCX = 1, - TREG_RDX = 2, - TREG_RSP = 4, - TREG_RSI = 6, - TREG_RDI = 7, - - TREG_R8 = 8, - TREG_R9 = 9, - TREG_R10 = 10, - TREG_R11 = 11, - - TREG_XMM0 = 16, - TREG_XMM1 = 17, - TREG_XMM2 = 18, - TREG_XMM3 = 19, - TREG_XMM4 = 20, - TREG_XMM5 = 21, - TREG_XMM6 = 22, - TREG_XMM7 = 23, - - TREG_ST0 = 24, - - TREG_MEM = 0x20, -}; - -#define REX_BASE(reg) (((reg) >> 3) & 1) -#define REG_VALUE(reg) ((reg) & 7) - -/* return registers for function */ -#define REG_IRET TREG_RAX /* single word int return register */ -#define REG_LRET TREG_RDX /* second word return register (for long long) */ -#define REG_FRET TREG_XMM0 /* float return register */ -#define REG_QRET TREG_XMM1 /* second float return register */ - -/* defined if function parameters must be evaluated in reverse order */ -#define INVERT_FUNC_PARAMS - -/* pointer size, in bytes */ -#define PTR_SIZE 8 - -/* long double size and alignment, in bytes */ -#define LDOUBLE_SIZE 16 -#define LDOUBLE_ALIGN 16 -/* maximum alignment (for aligned attribute support) */ -#define MAX_ALIGN 16 - -/******************************************************/ -/* ELF defines */ - -#define EM_TCC_TARGET EM_X86_64 - -/* relocation type for 32 bit data relocation */ -#define R_DATA_32 R_X86_64_32 -#define R_DATA_PTR R_X86_64_64 -#define R_JMP_SLOT R_X86_64_JUMP_SLOT -#define R_COPY R_X86_64_COPY - -#define ELF_START_ADDR 0x400000 -#define ELF_PAGE_SIZE 0x200000 - -/******************************************************/ -#else /* ! TARGET_DEFS_ONLY */ -/******************************************************/ -#include "../tcc.h" -#include <assert.h> - -ST_DATA const int reg_classes[NB_REGS] = { - /* eax */ RC_INT | RC_RAX, - /* ecx */ RC_INT | RC_RCX, - /* edx */ RC_INT | RC_RDX, - 0, - 0, - 0, - 0, - 0, - RC_R8, - RC_R9, - RC_R10, - RC_R11, - 0, - 0, - 0, - 0, - /* xmm0 */ RC_FLOAT | RC_XMM0, - /* xmm1 */ RC_FLOAT | RC_XMM1, - /* xmm2 */ RC_FLOAT | RC_XMM2, - /* xmm3 */ RC_FLOAT | RC_XMM3, - /* xmm4 */ RC_FLOAT | RC_XMM4, - /* xmm5 */ RC_FLOAT | RC_XMM5, - /* xmm6 an xmm7 are included so gv() can be used on them, - but they are not tagged with RC_FLOAT because they are - callee saved on Windows */ - RC_XMM6, - RC_XMM7, - /* st0 */ RC_ST0 -}; - -static unsigned long func_sub_sp_offset; -static int func_ret_sub; - -/* XXX: make it faster ? */ -void g(int c) -{ - int ind1; - ind1 = ind + 1; - if (ind1 > cur_text_section->data_allocated) - section_realloc(cur_text_section, ind1); - cur_text_section->data[ind] = c; - ind = ind1; -} - -void o(unsigned int c) -{ - while (c) { - g(c); - c = c >> 8; - } -} - -void gen_le16(int v) -{ - g(v); - g(v >> 8); -} - -void gen_le32(int c) -{ - g(c); - g(c >> 8); - g(c >> 16); - g(c >> 24); -} - -void gen_le64(int64_t c) -{ - g(c); - g(c >> 8); - g(c >> 16); - g(c >> 24); - g(c >> 32); - g(c >> 40); - g(c >> 48); - g(c >> 56); -} - -void orex(int ll, int r, int r2, int b) -{ - if ((r & VT_VALMASK) >= VT_CONST) - r = 0; - if ((r2 & VT_VALMASK) >= VT_CONST) - r2 = 0; - if (ll || REX_BASE(r) || REX_BASE(r2)) - o(0x40 | REX_BASE(r) | (REX_BASE(r2) << 2) | (ll << 3)); - o(b); -} - -/* output a symbol and patch all calls to it */ -void gsym_addr(int t, int a) -{ - int n, *ptr; - while (t) { - ptr = (int *)(cur_text_section->data + t); - n = *ptr; /* next value */ - *ptr = a - t - 4; - t = n; - } -} - -void gsym(int t) -{ - gsym_addr(t, ind); -} - -/* psym is used to put an instruction with a data field which is a - reference to a symbol. It is in fact the same as oad ! */ -#define psym oad - -static int is64_type(int t) -{ - return ((t & VT_BTYPE) == VT_PTR || - (t & VT_BTYPE) == VT_FUNC || - (t & VT_BTYPE) == VT_LLONG); -} - -/* instruction + 4 bytes data. Return the address of the data */ -ST_FUNC int oad(int c, int s) -{ - int ind1; - - o(c); - ind1 = ind + 4; - if (ind1 > cur_text_section->data_allocated) - section_realloc(cur_text_section, ind1); - *(int *)(cur_text_section->data + ind) = s; - s = ind; - ind = ind1; - return s; -} - -ST_FUNC void gen_addr32(int r, Sym *sym, int c) -{ - if (r & VT_SYM) - greloc(cur_text_section, sym, ind, R_X86_64_32); - gen_le32(c); -} - -/* output constant with relocation if 'r & VT_SYM' is true */ -ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c) -{ - if (r & VT_SYM) - greloc(cur_text_section, sym, ind, R_X86_64_64); - gen_le64(c); -} - -/* output constant with relocation if 'r & VT_SYM' is true */ -ST_FUNC void gen_addrpc32(int r, Sym *sym, int c) -{ - if (r & VT_SYM) - greloc(cur_text_section, sym, ind, R_X86_64_PC32); - gen_le32(c-4); -} - -/* output got address with relocation */ -static void gen_gotpcrel(int r, Sym *sym, int c) -{ -#ifndef TCC_TARGET_PE - Section *sr; - ElfW(Rela) *rel; - greloc(cur_text_section, sym, ind, R_X86_64_GOTPCREL); - sr = cur_text_section->reloc; - rel = (ElfW(Rela) *)(sr->data + sr->data_offset - sizeof(ElfW(Rela))); - rel->r_addend = -4; -#else - tcc_error("internal error: no GOT on PE: %s %x %x | %02x %02x %02x\n", - get_tok_str(sym->v, NULL), c, r, - cur_text_section->data[ind-3], - cur_text_section->data[ind-2], - cur_text_section->data[ind-1] - ); - greloc(cur_text_section, sym, ind, R_X86_64_PC32); -#endif - gen_le32(0); - if (c) { - /* we use add c, %xxx for displacement */ - orex(1, r, 0, 0x81); - o(0xc0 + REG_VALUE(r)); - gen_le32(c); - } -} - -static void gen_modrm_impl(int op_reg, int r, Sym *sym, int c, int is_got) -{ - op_reg = REG_VALUE(op_reg) << 3; - if ((r & VT_VALMASK) == VT_CONST) { - /* constant memory reference */ - o(0x05 | op_reg); - if (is_got) { - gen_gotpcrel(r, sym, c); - } else { - gen_addrpc32(r, sym, c); - } - } else if ((r & VT_VALMASK) == VT_LOCAL) { - /* currently, we use only ebp as base */ - if (c == (char)c) { - /* short reference */ - o(0x45 | op_reg); - g(c); - } else { - oad(0x85 | op_reg, c); - } - } else if ((r & VT_VALMASK) >= TREG_MEM) { - if (c) { - g(0x80 | op_reg | REG_VALUE(r)); - gen_le32(c); - } else { - g(0x00 | op_reg | REG_VALUE(r)); - } - } else { - g(0x00 | op_reg | REG_VALUE(r)); - } -} - -/* generate a modrm reference. 'op_reg' contains the addtionnal 3 - opcode bits */ -static void gen_modrm(int op_reg, int r, Sym *sym, int c) -{ - gen_modrm_impl(op_reg, r, sym, c, 0); -} - -/* generate a modrm reference. 'op_reg' contains the addtionnal 3 - opcode bits */ -static void gen_modrm64(int opcode, int op_reg, int r, Sym *sym, int c) -{ - int is_got; - is_got = (op_reg & TREG_MEM) && !(sym->type.t & VT_STATIC); - orex(1, r, op_reg, opcode); - gen_modrm_impl(op_reg, r, sym, c, is_got); -} - - -/* load 'r' from value 'sv' */ -void load(int r, SValue *sv) -{ - int v, t, ft, fc, fr; - SValue v1; - -#ifdef TCC_TARGET_PE - SValue v2; - sv = pe_getimport(sv, &v2); -#endif - - fr = sv->r; - ft = sv->type.t & ~VT_DEFSIGN; - fc = sv->c.ul; - -#ifndef TCC_TARGET_PE - /* we use indirect access via got */ - if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) && - (fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) { - /* use the result register as a temporal register */ - int tr = r | TREG_MEM; - if (is_float(ft)) { - /* we cannot use float registers as a temporal register */ - tr = get_reg(RC_INT) | TREG_MEM; - } - gen_modrm64(0x8b, tr, fr, sv->sym, 0); - - /* load from the temporal register */ - fr = tr | VT_LVAL; - } -#endif - - v = fr & VT_VALMASK; - if (fr & VT_LVAL) { - int b, ll; - if (v == VT_LLOCAL) { - v1.type.t = VT_PTR; - v1.r = VT_LOCAL | VT_LVAL; - v1.c.ul = fc; - fr = r; - if (!(reg_classes[fr] & (RC_INT|RC_R11))) - fr = get_reg(RC_INT); - load(fr, &v1); - } - ll = 0; - if ((ft & VT_BTYPE) == VT_FLOAT) { - b = 0x6e0f66; - r = REG_VALUE(r); /* movd */ - } else if ((ft & VT_BTYPE) == VT_DOUBLE) { - b = 0x7e0ff3; /* movq */ - r = REG_VALUE(r); - } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { - b = 0xdb, r = 5; /* fldt */ - } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) { - b = 0xbe0f; /* movsbl */ - } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { - b = 0xb60f; /* movzbl */ - } else if ((ft & VT_TYPE) == VT_SHORT) { - b = 0xbf0f; /* movswl */ - } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { - b = 0xb70f; /* movzwl */ - } else { - assert(((ft & VT_BTYPE) == VT_INT) || ((ft & VT_BTYPE) == VT_LLONG) - || ((ft & VT_BTYPE) == VT_PTR) || ((ft & VT_BTYPE) == VT_ENUM) - || ((ft & VT_BTYPE) == VT_FUNC)); - ll = is64_type(ft); - b = 0x8b; - } - if (ll) { - gen_modrm64(b, r, fr, sv->sym, fc); - } else { - orex(ll, fr, r, b); - gen_modrm(r, fr, sv->sym, fc); - } - } else { - if (v == VT_CONST) { - if (fr & VT_SYM) { -#ifdef TCC_TARGET_PE - orex(1,0,r,0x8d); - o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */ - gen_addrpc32(fr, sv->sym, fc); -#else - if (sv->sym->type.t & VT_STATIC) { - orex(1,0,r,0x8d); - o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */ - gen_addrpc32(fr, sv->sym, fc); - } else { - orex(1,0,r,0x8b); - o(0x05 + REG_VALUE(r) * 8); /* mov xx(%rip), r */ - gen_gotpcrel(r, sv->sym, fc); - } -#endif - } else if (is64_type(ft)) { - orex(1,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */ - gen_le64(sv->c.ull); - } else { - orex(0,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */ - gen_le32(fc); - } - } else if (v == VT_LOCAL) { - orex(1,0,r,0x8d); /* lea xxx(%ebp), r */ - gen_modrm(r, VT_LOCAL, sv->sym, fc); - } else if (v == VT_CMP) { - orex(0,r,0,0); - if ((fc & ~0x100) != TOK_NE) - oad(0xb8 + REG_VALUE(r), 0); /* mov $0, r */ - else - oad(0xb8 + REG_VALUE(r), 1); /* mov $1, r */ - if (fc & 0x100) { - /* This was a float compare. If the parity bit is - * set the result was unordered, meaning false for everything - * except TOK_NE, and true for TOK_NE. */ - fc &= ~0x100; - o(0x037a + (REX_BASE(r) << 8)); - } - orex(0,r,0, 0x0f); /* setxx %br */ - o(fc); - o(0xc0 + REG_VALUE(r)); - } else if (v == VT_JMP || v == VT_JMPI) { - t = v & 1; - orex(0,r,0,0); - oad(0xb8 + REG_VALUE(r), t); /* mov $1, r */ - o(0x05eb + (REX_BASE(r) << 8)); /* jmp after */ - gsym(fc); - orex(0,r,0,0); - oad(0xb8 + REG_VALUE(r), t ^ 1); /* mov $0, r */ - } else if (v != r) { - if ((r >= TREG_XMM0) && (r <= TREG_XMM7)) { - if (v == TREG_ST0) { - /* gen_cvt_ftof(VT_DOUBLE); */ - o(0xf0245cdd); /* fstpl -0x10(%rsp) */ - /* movsd -0x10(%rsp),%xmmN */ - o(0x100ff2); - o(0x44 + REG_VALUE(r)*8); /* %xmmN */ - o(0xf024); - } else { - assert((v >= TREG_XMM0) && (v <= TREG_XMM7)); - if ((ft & VT_BTYPE) == VT_FLOAT) { - o(0x100ff3); - } else { - assert((ft & VT_BTYPE) == VT_DOUBLE); - o(0x100ff2); - } - o(0xc0 + REG_VALUE(v) + REG_VALUE(r)*8); - } - } else if (r == TREG_ST0) { - assert((v >= TREG_XMM0) && (v <= TREG_XMM7)); - /* gen_cvt_ftof(VT_LDOUBLE); */ - /* movsd %xmmN,-0x10(%rsp) */ - o(0x110ff2); - o(0x44 + REG_VALUE(r)*8); /* %xmmN */ - o(0xf024); - o(0xf02444dd); /* fldl -0x10(%rsp) */ - } else { - orex(1,r,v, 0x89); - o(0xc0 + REG_VALUE(r) + REG_VALUE(v) * 8); /* mov v, r */ - } - } - } -} - -/* store register 'r' in lvalue 'v' */ -void store(int r, SValue *v) -{ - int fr, bt, ft, fc; - int op64 = 0; - /* store the REX prefix in this variable when PIC is enabled */ - int pic = 0; - -#ifdef TCC_TARGET_PE - SValue v2; - v = pe_getimport(v, &v2); -#endif - - ft = v->type.t; - fc = v->c.ul; - fr = v->r & VT_VALMASK; - bt = ft & VT_BTYPE; - -#ifndef TCC_TARGET_PE - /* we need to access the variable via got */ - if (fr == VT_CONST && (v->r & VT_SYM)) { - /* mov xx(%rip), %r11 */ - o(0x1d8b4c); - gen_gotpcrel(TREG_R11, v->sym, v->c.ul); - pic = is64_type(bt) ? 0x49 : 0x41; - } -#endif - - /* XXX: incorrect if float reg to reg */ - if (bt == VT_FLOAT) { - o(0x66); - o(pic); - o(0x7e0f); /* movd */ - r = REG_VALUE(r); - } else if (bt == VT_DOUBLE) { - o(0x66); - o(pic); - o(0xd60f); /* movq */ - r = REG_VALUE(r); - } else if (bt == VT_LDOUBLE) { - o(0xc0d9); /* fld %st(0) */ - o(pic); - o(0xdb); /* fstpt */ - r = 7; - } else { - if (bt == VT_SHORT) - o(0x66); - o(pic); - if (bt == VT_BYTE || bt == VT_BOOL) - orex(0, 0, r, 0x88); - else if (is64_type(bt)) - op64 = 0x89; - else - orex(0, 0, r, 0x89); - } - if (pic) { - /* xxx r, (%r11) where xxx is mov, movq, fld, or etc */ - if (op64) - o(op64); - o(3 + (r << 3)); - } else if (op64) { - if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { - gen_modrm64(op64, r, v->r, v->sym, fc); - } else if (fr != r) { - /* XXX: don't we really come here? */ - abort(); - o(0xc0 + fr + r * 8); /* mov r, fr */ - } - } else { - if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { - gen_modrm(r, v->r, v->sym, fc); - } else if (fr != r) { - /* XXX: don't we really come here? */ - abort(); - o(0xc0 + fr + r * 8); /* mov r, fr */ - } - } -} - -/* 'is_jmp' is '1' if it is a jump */ -static void gcall_or_jmp(int is_jmp) -{ - int r; - if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && - ((vtop->r & VT_SYM) || (vtop->c.ll-4) == (int)(vtop->c.ll-4))) { - /* constant case */ - if (vtop->r & VT_SYM) { - /* relocation case */ -#ifdef TCC_TARGET_PE - greloc(cur_text_section, vtop->sym, ind + 1, R_X86_64_PC32); -#else - greloc(cur_text_section, vtop->sym, ind + 1, R_X86_64_PLT32); -#endif - } else { - /* put an empty PC32 relocation */ - put_elf_reloc(symtab_section, cur_text_section, - ind + 1, R_X86_64_PC32, 0); - } - oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ - } else { - /* otherwise, indirect call */ - r = TREG_R11; - load(r, vtop); - o(0x41); /* REX */ - o(0xff); /* call/jmp *r */ - o(0xd0 + REG_VALUE(r) + (is_jmp << 4)); - } -} - -#if defined(CONFIG_TCC_BCHECK) -#ifndef TCC_TARGET_PE -static addr_t func_bound_offset; -static unsigned long func_bound_ind; -#endif - -static void gen_static_call(int v) -{ - Sym *sym = external_global_sym(v, &func_old_type, 0); - oad(0xe8, -4); - greloc(cur_text_section, sym, ind-4, R_X86_64_PC32); -} - -/* generate a bounded pointer addition */ -ST_FUNC void gen_bounded_ptr_add(void) -{ - /* save all temporary registers */ - save_regs(0); - - /* prepare fast x86_64 function call */ - gv(RC_RAX); - o(0xc68948); // mov %rax,%rsi ## second arg in %rsi, this must be size - vtop--; - - gv(RC_RAX); - o(0xc78948); // mov %rax,%rdi ## first arg in %rdi, this must be ptr - vtop--; - - /* do a fast function call */ - gen_static_call(TOK___bound_ptr_add); - - /* returned pointer is in rax */ - vtop++; - vtop->r = TREG_RAX | VT_BOUNDED; - - - /* relocation offset of the bounding function call point */ - vtop->c.ull = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela))); -} - -/* patch pointer addition in vtop so that pointer dereferencing is - also tested */ -ST_FUNC void gen_bounded_ptr_deref(void) -{ - addr_t func; - int size, align; - ElfW(Rela) *rel; - Sym *sym; - - size = 0; - /* XXX: put that code in generic part of tcc */ - if (!is_float(vtop->type.t)) { - if (vtop->r & VT_LVAL_BYTE) - size = 1; - else if (vtop->r & VT_LVAL_SHORT) - size = 2; - } - if (!size) - size = type_size(&vtop->type, &align); - switch(size) { - case 1: func = TOK___bound_ptr_indir1; break; - case 2: func = TOK___bound_ptr_indir2; break; - case 4: func = TOK___bound_ptr_indir4; break; - case 8: func = TOK___bound_ptr_indir8; break; - case 12: func = TOK___bound_ptr_indir12; break; - case 16: func = TOK___bound_ptr_indir16; break; - default: - tcc_error("unhandled size when dereferencing bounded pointer"); - func = 0; - break; - } - - sym = external_global_sym(func, &func_old_type, 0); - if (!sym->c) - put_extern_sym(sym, NULL, 0, 0); - - /* patch relocation */ - /* XXX: find a better solution ? */ - - rel = (ElfW(Rela) *)(cur_text_section->reloc->data + vtop->c.ull); - rel->r_info = ELF64_R_INFO(sym->c, ELF64_R_TYPE(rel->r_info)); -} -#endif - -#ifdef TCC_TARGET_PE - -#define REGN 4 -static const uint8_t arg_regs[REGN] = { - TREG_RCX, TREG_RDX, TREG_R8, TREG_R9 -}; - -/* Prepare arguments in R10 and R11 rather than RCX and RDX - because gv() will not ever use these */ -static int arg_prepare_reg(int idx) { - if (idx == 0 || idx == 1) - /* idx=0: r10, idx=1: r11 */ - return idx + 10; - else - return arg_regs[idx]; -} - -static int func_scratch; - -/* Generate function call. The function address is pushed first, then - all the parameters in call order. This functions pops all the - parameters and the function address. */ - -void gen_offs_sp(int b, int r, int d) -{ - orex(1,0,r & 0x100 ? 0 : r, b); - if (d == (char)d) { - o(0x2444 | (REG_VALUE(r) << 3)); - g(d); - } else { - o(0x2484 | (REG_VALUE(r) << 3)); - gen_le32(d); - } -} - -ST_FUNC int regargs_nregs(RegArgs *args) -{ - return *args; -} - -/* Return the number of registers needed to return the struct, or 0 if - returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) -{ - int size, align; - *regsize = 8; - *ret_align = 1; // Never have to re-align return values for x86-64 - size = type_size(vt, &align); - ret->ref = NULL; - if (size > 8) { - *args = 0; - } else if (size > 4) { - ret->t = VT_LLONG; - *args = 1; - } else if (size > 2) { - ret->t = VT_INT; - *args = 1; - } else if (size > 1) { - ret->t = VT_SHORT; - *args = 1; - } else { - ret->t = VT_BYTE; - *args = 1; - } - - return *args != 0; -} - -static int is_sse_float(int t) { - int bt; - bt = t & VT_BTYPE; - return bt == VT_DOUBLE || bt == VT_FLOAT; -} - -int gfunc_arg_size(CType *type) { - int align; - if (type->t & (VT_ARRAY|VT_BITFIELD)) - return 8; - return type_size(type, &align); -} - -void gfunc_call(int nb_args) -{ - int size, r, args_size, i, d, bt, struct_size; - int arg; - - args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE; - arg = nb_args; - - /* for struct arguments, we need to call memcpy and the function - call breaks register passing arguments we are preparing. - So, we process arguments which will be passed by stack first. */ - struct_size = args_size; - for(i = 0; i < nb_args; i++) { - SValue *sv; - - --arg; - sv = &vtop[-i]; - bt = (sv->type.t & VT_BTYPE); - size = gfunc_arg_size(&sv->type); - - if (size <= 8) - continue; /* arguments smaller than 8 bytes passed in registers or on stack */ - - if (bt == VT_STRUCT) { - /* align to stack align size */ - size = (size + 15) & ~15; - /* generate structure store */ - r = get_reg(RC_INT); - gen_offs_sp(0x8d, r, struct_size); - struct_size += size; - - /* generate memcpy call */ - vset(&sv->type, r | VT_LVAL, 0); - vpushv(sv); - vstore(); - --vtop; - } else if (bt == VT_LDOUBLE) { - gv(RC_ST0); - gen_offs_sp(0xdb, 0x107, struct_size); - struct_size += 16; - } - } - - if (func_scratch < struct_size) - func_scratch = struct_size; - - arg = nb_args; - struct_size = args_size; - - for(i = 0; i < nb_args; i++) { - --arg; - bt = (vtop->type.t & VT_BTYPE); - - size = gfunc_arg_size(&vtop->type); - if (size > 8) { - /* align to stack align size */ - size = (size + 15) & ~15; - if (arg >= REGN) { - d = get_reg(RC_INT); - gen_offs_sp(0x8d, d, struct_size); - gen_offs_sp(0x89, d, arg*8); - } else { - d = arg_prepare_reg(arg); - gen_offs_sp(0x8d, d, struct_size); - } - struct_size += size; - } else { - if (is_sse_float(vtop->type.t)) { - gv(RC_XMM0); /* only use one float register */ - if (arg >= REGN) { - /* movq %xmm0, j*8(%rsp) */ - gen_offs_sp(0xd60f66, 0x100, arg*8); - } else { - /* movaps %xmm0, %xmmN */ - o(0x280f); - o(0xc0 + (arg << 3)); - d = arg_prepare_reg(arg); - /* mov %xmm0, %rxx */ - o(0x66); - orex(1,d,0, 0x7e0f); - o(0xc0 + REG_VALUE(d)); - } - } else { - if (bt == VT_STRUCT) { - vtop->type.ref = NULL; - vtop->type.t = size > 4 ? VT_LLONG : size > 2 ? VT_INT - : size > 1 ? VT_SHORT : VT_BYTE; - } - - r = gv(RC_INT); - if (arg >= REGN) { - gen_offs_sp(0x89, r, arg*8); - } else { - d = arg_prepare_reg(arg); - orex(1,d,r,0x89); /* mov */ - o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); - } - } - } - vtop--; - } - save_regs(0); - - /* Copy R10 and R11 into RCX and RDX, respectively */ - if (nb_args > 0) { - o(0xd1894c); /* mov %r10, %rcx */ - if (nb_args > 1) { - o(0xda894c); /* mov %r11, %rdx */ - } - } - - gcall_or_jmp(0); - vtop--; -} - - -#define FUNC_PROLOG_SIZE 11 - -/* generate function prolog of type 't' */ -void gfunc_prolog(CType *func_type) -{ - int addr, reg_param_index, bt, size; - Sym *sym; - CType *type; - - func_ret_sub = 0; - func_scratch = 0; - loc = 0; - - addr = PTR_SIZE * 2; - ind += FUNC_PROLOG_SIZE; - func_sub_sp_offset = ind; - reg_param_index = 0; - - sym = func_type->ref; - - /* if the function returns a structure, then add an - implicit pointer parameter */ - func_vt = sym->type; - func_var = (sym->c == FUNC_ELLIPSIS); - size = gfunc_arg_size(&func_vt); - if (size > 8) { - gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); - func_vc = addr; - reg_param_index++; - addr += 8; - } - - /* define parameters */ - while ((sym = sym->next) != NULL) { - type = &sym->type; - bt = type->t & VT_BTYPE; - size = gfunc_arg_size(type); - if (size > 8) { - if (reg_param_index < REGN) { - gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); - } - sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL | VT_REF, addr); - } else { - if (reg_param_index < REGN) { - /* save arguments passed by register */ - if ((bt == VT_FLOAT) || (bt == VT_DOUBLE)) { - o(0xd60f66); /* movq */ - gen_modrm(reg_param_index, VT_LOCAL, NULL, addr); - } else { - gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); - } - } - sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr); - } - addr += 8; - reg_param_index++; - } - - while (reg_param_index < REGN) { - if (func_type->ref->c == FUNC_ELLIPSIS) { - gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); - addr += 8; - } - reg_param_index++; - } -} - -/* generate function epilog */ -void gfunc_epilog(void) -{ - int v, saved_ind; - - o(0xc9); /* leave */ - if (func_ret_sub == 0) { - o(0xc3); /* ret */ - } else { - o(0xc2); /* ret n */ - g(func_ret_sub); - g(func_ret_sub >> 8); - } - - saved_ind = ind; - ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; - /* align local size to word & save local variables */ - v = (func_scratch + -loc + 15) & -16; - - if (v >= 4096) { - Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0); - oad(0xb8, v); /* mov stacksize, %eax */ - oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */ - greloc(cur_text_section, sym, ind-4, R_X86_64_PC32); - o(0x90); /* fill for FUNC_PROLOG_SIZE = 11 bytes */ - } else { - o(0xe5894855); /* push %rbp, mov %rsp, %rbp */ - o(0xec8148); /* sub rsp, stacksize */ - gen_le32(v); - } - - cur_text_section->data_offset = saved_ind; - pe_add_unwind_data(ind, saved_ind, v); - ind = cur_text_section->data_offset; -} - -#else - -static void gadd_sp(int val) -{ - if (val == (char)val) { - o(0xc48348); - g(val); - } else { - oad(0xc48148, val); /* add $xxx, %rsp */ - } -} - -typedef enum X86_64_Mode { - x86_64_mode_none, - x86_64_mode_memory, - x86_64_mode_integer, - x86_64_mode_sse, - x86_64_mode_x87 -} X86_64_Mode; - -static X86_64_Mode classify_x86_64_merge(X86_64_Mode a, X86_64_Mode b) -{ - if (a == b) - return a; - else if (a == x86_64_mode_none) - return b; - else if (b == x86_64_mode_none) - return a; - else if ((a == x86_64_mode_memory) || (b == x86_64_mode_memory)) - return x86_64_mode_memory; - else if ((a == x86_64_mode_integer) || (b == x86_64_mode_integer)) - return x86_64_mode_integer; - else if ((a == x86_64_mode_x87) || (b == x86_64_mode_x87)) - return x86_64_mode_memory; - else - return x86_64_mode_sse; -} - -/* classify the x86 eightbytes from byte index start to byte index - * end, at offset offset from the root struct */ -static X86_64_Mode classify_x86_64_inner(CType *ty, int offset, int start, int end) -{ - X86_64_Mode mode; - Sym *f; - - switch (ty->t & VT_BTYPE) { - case VT_VOID: return x86_64_mode_none; - - case VT_INT: - case VT_BYTE: - case VT_SHORT: - case VT_LLONG: - case VT_BOOL: - case VT_PTR: - case VT_FUNC: - case VT_ENUM: return x86_64_mode_integer; - - case VT_FLOAT: - case VT_DOUBLE: return x86_64_mode_sse; - - case VT_LDOUBLE: return x86_64_mode_x87; - - case VT_STRUCT: - f = ty->ref; - - mode = x86_64_mode_none; - while ((f = f->next) != NULL) { - if (f->c + offset >= start && f->c + offset < end) - mode = classify_x86_64_merge(mode, classify_x86_64_inner(&f->type, f->c + offset, start, end)); - } - - return mode; - } - - assert(0); -} - -static X86_64_Mode classify_x86_64_arg_eightbyte(CType *ty, int offset) -{ - X86_64_Mode mode; - - assert((ty->t & VT_BTYPE) == VT_STRUCT); - - mode = classify_x86_64_inner(ty, 0, offset, offset + 8); - - return mode; -} - -static void regargs_init(RegArgs *args) -{ - int i; - for(i=0; i<REG_ARGS_MAX; i++) { - args->ireg[i] = -1; - args->freg[i] = -1; - } -} - -static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *palign, RegArgs *args) -{ - X86_64_Mode mode = x86_64_mode_none; - int size, align, ret_t = 0; - int ireg = 0, freg = 0; - - if (args) - regargs_init(args); - - if (ty->t & (VT_BITFIELD|VT_ARRAY)) { - *psize = 8; - *palign = 8; - if (args) - args->ireg[ireg++] = 0; - ret_t = ty->t; - mode = x86_64_mode_integer; - } else { - size = type_size(ty, &align); - *psize = (size + 7) & ~7; - *palign = (align + 7) & ~7; - - if (size > 16) { - mode = x86_64_mode_memory; - } else { - int start; - - for(start=0; start < size; start += 8) { - if ((ty->t & VT_BTYPE) == VT_STRUCT) { - mode = classify_x86_64_arg_eightbyte(ty, start); - } else { - mode = classify_x86_64_inner(ty, 0, 0, size); - } - - if (mode == x86_64_mode_integer) { - if (args) - args->ireg[ireg++] = start; - ret_t = (size > 4) ? VT_LLONG : VT_INT; - } else if (mode == x86_64_mode_sse) { - if (args) - args->freg[freg++] = start; - ret_t = (size > 4) ? VT_DOUBLE : VT_FLOAT; - } else { - ret_t = VT_LDOUBLE; - } - } - } - } - - if (ret) { - ret->ref = NULL; - ret->t = ret_t; - } - - return mode; -} - -ST_FUNC int classify_x86_64_va_arg(CType *ty) -{ - /* This definition must be synced with stdarg.h */ - enum __va_arg_type { - __va_gen_reg, __va_float_reg, __va_stack - }; - int size, align; - X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, NULL); - switch (mode) { - default: return __va_stack; - case x86_64_mode_integer: return __va_gen_reg; - case x86_64_mode_sse: return __va_float_reg; - } -} - -static int regargs_iregs(RegArgs *args) -{ - int i; - int ret = 0; - for(i=0; i<REG_ARGS_MAX; i++) { - if(args->ireg[i] != -1) - ret++; - } - - return ret; -} - -static int regargs_fregs(RegArgs *args) -{ - int i; - int ret = 0; - for(i=0; i<REG_ARGS_MAX; i++) { - if(args->freg[i] != -1) - ret++; - } - - return ret; -} - -/* Count the total number of registers used by args */ -ST_FUNC int regargs_nregs(RegArgs *args) -{ - int i; - int ret = 0; - for(i=0; i<REG_ARGS_MAX; i++) { - if(args->ireg[i] != -1) - ret++; - - if(args->freg[i] != -1) - ret++; - } - - return ret; -} - -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) -{ - int size, align; - X86_64_Mode mode; - *ret_align = 1; // Never have to re-align return values for x86-64 - *regsize = 8; - - mode = classify_x86_64_arg(vt, ret, &size, &align, args); - - return mode != x86_64_mode_memory && - mode != x86_64_mode_none; -} - -#define REGN 6 -static const uint8_t arg_regs[REGN] = { - TREG_RDI, TREG_RSI, TREG_RDX, TREG_RCX, TREG_R8, TREG_R9 -}; - -static int arg_prepare_reg(int idx) { - if (idx == 2 || idx == 3) - /* idx=2: r10, idx=3: r11 */ - return idx + 8; - else - return arg_regs[idx]; -} - -/* Generate function call. The function address is pushed first, then - all the parameters in call order. This functions pops all the - parameters and the function address. */ -void gfunc_call(int nb_args) -{ - X86_64_Mode mode; - CType type; - int size, align, r, args_size, stack_adjust, run_start, run_end, i; - int nb_reg_args = 0; - int nb_sse_args = 0; - int sse_reg = 0, gen_reg = 0; - RegArgs *reg_args = alloca(nb_args * sizeof *reg_args); - - /* calculate the number of integer/float register arguments */ - for(i = nb_args - 1; i >= 0; i--) { - int fregs, iregs; - mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_args[i]); - fregs = regargs_fregs(®_args[i]); - iregs = regargs_iregs(®_args[i]); - - nb_sse_args += fregs; - nb_reg_args += iregs; - - if (sse_reg + fregs > 8 || gen_reg + iregs > REGN) { - regargs_init(®_args[i]); - } else { - sse_reg += fregs; - gen_reg += iregs; - } - } - - /* arguments are collected in runs. Each run is a collection of 8-byte aligned arguments - and ended by a 16-byte aligned argument. This is because, from the point of view of - the callee, argument alignment is computed from the bottom up. */ - /* for struct arguments, we need to call memcpy and the function - call breaks register passing arguments we are preparing. - So, we process arguments which will be passed by stack first. */ - gen_reg = nb_reg_args; - sse_reg = nb_sse_args; - run_start = 0; - args_size = 0; - while (run_start != nb_args) { - int run_gen_reg = gen_reg, run_sse_reg = sse_reg; - - run_end = nb_args; - stack_adjust = 0; - for(i = run_start; (i < nb_args) && (run_end == nb_args); i++) { - int stack = regargs_nregs(®_args[i]) == 0; - classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, NULL); - - if (stack) { - if (align == 16) - run_end = i; - else - stack_adjust += size; - } - } - - gen_reg = run_gen_reg; - sse_reg = run_sse_reg; - - /* adjust stack to align SSE boundary */ - if (stack_adjust &= 15) { - /* fetch cpu flag before the following sub will change the value */ - if (vtop >= vstack && (vtop->r & VT_VALMASK) == VT_CMP) - gv(RC_INT); - - stack_adjust = 16 - stack_adjust; - o(0x48); - oad(0xec81, stack_adjust); /* sub $xxx, %rsp */ - args_size += stack_adjust; - } - - for(i = run_start; i < run_end;) { - int arg_stored = regargs_nregs(®_args[i]) == 0; - SValue tmp; - RegArgs args; - - if (!arg_stored) { - ++i; - continue; - } - - /* Swap argument to top, it will possibly be changed here, - and might use more temps. At the end of the loop we keep - in on the stack and swap it back to its original position - if it is a register. */ - tmp = vtop[0]; - vtop[0] = vtop[-i]; - vtop[-i] = tmp; - - classify_x86_64_arg(&vtop->type, NULL, &size, &align, &args); - - switch (vtop->type.t & VT_BTYPE) { - case VT_STRUCT: - /* allocate the necessary size on stack */ - o(0x48); - oad(0xec81, size); /* sub $xxx, %rsp */ - /* generate structure store */ - r = get_reg(RC_INT); - orex(1, r, 0, 0x89); /* mov %rsp, r */ - o(0xe0 + REG_VALUE(r)); - vset(&vtop->type, r | VT_LVAL, 0); - vswap(); - vstore(); - args_size += size; - break; - - case VT_LDOUBLE: - assert(0); - break; - - case VT_FLOAT: - case VT_DOUBLE: - r = gv(RC_FLOAT); - o(0x50); /* push $rax */ - /* movq %xmmN, (%rsp) */ - o(0xd60f66); - o(0x04 + REG_VALUE(r)*8); - o(0x24); - args_size += size; - break; - - default: - /* simple type */ - /* XXX: implicit cast ? */ - --gen_reg; - r = gv(RC_INT); - orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */ - args_size += size; - break; - } - - /* And swap the argument back to its original position. */ - tmp = vtop[0]; - vtop[0] = vtop[-i]; - vtop[-i] = tmp; - - vrotb(i+1); - assert((vtop->type.t == tmp.type.t) && (vtop->r == tmp.r)); - vpop(); - memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args); - --nb_args; - --run_end; - } - - /* handle 16 byte aligned arguments at end of run */ - run_start = i = run_end; - while (i < nb_args) { - /* Rotate argument to top since it will always be popped */ - mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, NULL); - if (align != 16) - break; - - vrotb(i+1); - - if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { - gv(RC_ST0); - oad(0xec8148, size); /* sub $xxx, %rsp */ - o(0x7cdb); /* fstpt 0(%rsp) */ - g(0x24); - g(0x00); - args_size += size; - } else { - assert(mode == x86_64_mode_memory); - - /* allocate the necessary size on stack */ - o(0x48); - oad(0xec81, size); /* sub $xxx, %rsp */ - /* generate structure store */ - r = get_reg(RC_INT); - orex(1, r, 0, 0x89); /* mov %rsp, r */ - o(0xe0 + REG_VALUE(r)); - vset(&vtop->type, r | VT_LVAL, 0); - vswap(); - vstore(); - args_size += size; - } - - vpop(); - memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args); - --nb_args; - } - } - - /* XXX This should be superfluous. */ - save_regs(0); /* save used temporary registers */ - - /* recalculate the number of register arguments there actually - * are. This is slow but more obviously correct than using the - * old counts. */ - gen_reg = 0; - sse_reg = 0; - for(i = 0; i < nb_args; i++) { - gen_reg += regargs_iregs(®_args[i]); - sse_reg += regargs_fregs(®_args[i]); - } - - /* then, we prepare register passing arguments. - Note that we cannot set RDX and RCX in this loop because gv() - may break these temporary registers. Let's use R10 and R11 - instead of them */ - assert(gen_reg <= REGN); - assert(sse_reg <= 8); - for(i = 0; i < nb_args; i++) { - RegArgs args; - - args = reg_args[i]; - - /* Alter stack entry type so that gv() knows how to treat it */ - if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { - int k; - - for(k=REG_ARGS_MAX-1; k>=0; k--) { - if (args.freg[k] == -1) - continue; - - sse_reg--; - assert(sse_reg >= 0); - - vdup(); - vtop->type.t = VT_DOUBLE; - vtop->c.ull += args.freg[k]; - gv(RC_XMM0 << sse_reg); - vpop(); - } - for(k=REG_ARGS_MAX-1; k>=0; k--) { - int d; - if (args.ireg[k] == -1) - continue; - - gen_reg--; - - vdup(); - vtop->type.t = VT_LLONG; - vtop->c.ull += args.ireg[k]; - r = gv(RC_INT); - d = arg_prepare_reg(gen_reg); - orex(1,d,r,0x89); /* mov */ - o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); - vpop(); - } - } else { - /* XXX is it really necessary to set vtop->type? */ - classify_x86_64_arg(&vtop->type, &type, &size, &align, NULL); - vtop->type = type; - if (args.freg[0] != -1) { - --sse_reg; - /* Load directly to register */ - gv(RC_XMM0 << sse_reg); - } else if (args.ireg[0] != -1) { - int d; - /* simple type */ - /* XXX: implicit cast ? */ - gen_reg--; - r = gv(RC_INT); - d = arg_prepare_reg(gen_reg); - orex(1,d,r,0x89); /* mov */ - o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); - } else { - assert(0); - } - } - vtop--; - } - assert(gen_reg == 0); - assert(sse_reg == 0); - - /* We shouldn't have many operands on the stack anymore, but the - call address itself is still there, and it might be in %eax - (or edx/ecx) currently, which the below writes would clobber. - So evict all remaining operands here. */ - save_regs(0); - - /* Copy R10 and R11 into RDX and RCX, respectively */ - if (nb_reg_args > 2) { - o(0xd2894c); /* mov %r10, %rdx */ - if (nb_reg_args > 3) { - o(0xd9894c); /* mov %r11, %rcx */ - } - } - - oad(0xb8, nb_sse_args < 8 ? nb_sse_args : 8); /* mov nb_sse_args, %eax */ - gcall_or_jmp(0); - if (args_size) - gadd_sp(args_size); - vtop--; -} - - -#define FUNC_PROLOG_SIZE 11 - -static void push_arg_reg(int i) { - loc -= 8; - gen_modrm64(0x89, arg_regs[i], VT_LOCAL, NULL, loc); -} - -/* generate function prolog of type 't' */ -void gfunc_prolog(CType *func_type) -{ - X86_64_Mode mode; - int i, addr, align, size; - int param_addr = 0, reg_param_index, sse_param_index; - Sym *sym; - CType *type; - - sym = func_type->ref; - addr = PTR_SIZE * 2; - loc = 0; - ind += FUNC_PROLOG_SIZE; - func_sub_sp_offset = ind; - func_ret_sub = 0; - - if (func_type->ref->c == FUNC_ELLIPSIS) { - int seen_reg_num, seen_sse_num, seen_stack_size; - seen_reg_num = seen_sse_num = 0; - /* frame pointer and return address */ - seen_stack_size = PTR_SIZE * 2; - /* count the number of seen parameters */ - sym = func_type->ref; - while ((sym = sym->next) != NULL) { - RegArgs args; - - type = &sym->type; - mode = classify_x86_64_arg(type, NULL, &size, &align, &args); - - switch (mode) { - default: - stack_arg: - seen_stack_size = ((seen_stack_size + align - 1) & -align) + size; - break; - - case x86_64_mode_integer: - case x86_64_mode_sse: { - int stack = 0; - - seen_sse_num += regargs_fregs(&args); - seen_reg_num += regargs_iregs(&args); - - if (seen_reg_num > 8) { - seen_reg_num = 8; - stack = 1; - } - if (seen_sse_num > 8) { - seen_sse_num = 8; - stack = 1; - } - - if (stack) - goto stack_arg; - break; - } - } - } - - loc -= 16; - /* movl $0x????????, -0x10(%rbp) */ - o(0xf045c7); - gen_le32(seen_reg_num * 8); - /* movl $0x????????, -0xc(%rbp) */ - o(0xf445c7); - gen_le32(seen_sse_num * 16 + 48); - /* movl $0x????????, -0x8(%rbp) */ - o(0xf845c7); - gen_le32(seen_stack_size); - - /* save all register passing arguments */ - for (i = 0; i < 8; i++) { - loc -= 16; - o(0xd60f66); /* movq */ - gen_modrm(7 - i, VT_LOCAL, NULL, loc); - /* movq $0, loc+8(%rbp) */ - o(0x85c748); - gen_le32(loc + 8); - gen_le32(0); - } - for (i = 0; i < REGN; i++) { - push_arg_reg(REGN-1-i); - } - } - - sym = func_type->ref; - reg_param_index = 0; - sse_param_index = 0; - - /* if the function returns a structure, then add an - implicit pointer parameter */ - func_vt = sym->type; - mode = classify_x86_64_arg(&func_vt, NULL, &size, &align, NULL); - if (mode == x86_64_mode_memory) { - push_arg_reg(reg_param_index); - func_vc = loc; - reg_param_index++; - } - /* define parameters */ - while ((sym = sym->next) != NULL) { - RegArgs args; - int reg_count_integer = 0; - int reg_count_sse = 0; - int arg_stored = 1; - - type = &sym->type; - mode = classify_x86_64_arg(type, NULL, &size, &align, &args); - reg_count_integer = regargs_iregs(&args); - reg_count_sse = regargs_fregs(&args); - - switch (mode) { - case x86_64_mode_integer: - case x86_64_mode_sse: - if (reg_count_integer || reg_count_sse) { - if ((reg_count_sse == 0 || sse_param_index + reg_count_sse <= 8) && - (reg_count_integer == 0 || reg_param_index + reg_count_integer <= REGN)) { - /* argument fits into registers */ - arg_stored = 0; - } - } - - if (!arg_stored) { - /* save arguments passed by register */ - loc -= (reg_count_sse + reg_count_integer) * 8; - param_addr = loc; - for (i = 0; i < reg_count_sse; ++i) { - o(0xd60f66); /* movq */ - gen_modrm(sse_param_index, VT_LOCAL, NULL, param_addr + args.freg[i]); - ++sse_param_index; - } - for (i = 0; i < reg_count_integer; ++i) { - gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, param_addr + args.ireg[i]); - ++reg_param_index; - } - } else { - addr = (addr + align - 1) & -align; - param_addr = addr; - addr += size; - } - break; - - case x86_64_mode_memory: - case x86_64_mode_x87: - addr = (addr + align - 1) & -align; - param_addr = addr; - addr += size; - break; - default: break; /* nothing to be done for x86_64_mode_none */ - } - sym_push(sym->v & ~SYM_FIELD, type, - VT_LOCAL | VT_LVAL, param_addr); - } - -#ifdef CONFIG_TCC_BCHECK - /* leave some room for bound checking code */ - if (tcc_state->do_bounds_check) { - func_bound_offset = lbounds_section->data_offset; - func_bound_ind = ind; - oad(0xb8, 0); /* lbound section pointer */ - o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */ - oad(0xb8, 0); /* call to function */ - } -#endif -} - -/* generate function epilog */ -void gfunc_epilog(void) -{ - int v, saved_ind; - -#ifdef CONFIG_TCC_BCHECK - if (tcc_state->do_bounds_check - && func_bound_offset != lbounds_section->data_offset) - { - addr_t saved_ind; - addr_t *bounds_ptr; - Sym *sym_data; - - /* add end of table info */ - bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); - *bounds_ptr = 0; - - /* generate bound local allocation */ - sym_data = get_sym_ref(&char_pointer_type, lbounds_section, - func_bound_offset, lbounds_section->data_offset); - saved_ind = ind; - ind = func_bound_ind; - greloc(cur_text_section, sym_data, ind + 1, R_386_32); - ind = ind + 5 + 3; - gen_static_call(TOK___bound_local_new); - ind = saved_ind; - - /* generate bound check local freeing */ - o(0x5250); /* save returned value, if any */ - greloc(cur_text_section, sym_data, ind + 1, R_386_32); - oad(0xb8, 0); /* mov xxx, %rax */ - o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */ - gen_static_call(TOK___bound_local_delete); - o(0x585a); /* restore returned value, if any */ - } -#endif - o(0xc9); /* leave */ - if (func_ret_sub == 0) { - o(0xc3); /* ret */ - } else { - o(0xc2); /* ret n */ - g(func_ret_sub); - g(func_ret_sub >> 8); - } - /* align local size to word & save local variables */ - v = (-loc + 15) & -16; - saved_ind = ind; - ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; - o(0xe5894855); /* push %rbp, mov %rsp, %rbp */ - o(0xec8148); /* sub rsp, stacksize */ - gen_le32(v); - ind = saved_ind; -} - -#endif /* not PE */ - -/* generate a jump to a label */ -int gjmp(int t) -{ - return psym(0xe9, t); -} - -/* generate a jump to a fixed address */ -void gjmp_addr(int a) -{ - int r; - r = a - ind - 2; - if (r == (char)r) { - g(0xeb); - g(r); - } else { - oad(0xe9, a - ind - 5); - } -} - -/* generate a test. set 'inv' to invert test. Stack entry is popped */ -int gtst(int inv, int t) -{ - int v, *p; - - v = vtop->r & VT_VALMASK; - if (v == VT_CMP) { - /* fast case : can jump directly since flags are set */ - if (vtop->c.i & 0x100) - { - /* This was a float compare. If the parity flag is set - the result was unordered. For anything except != this - means false and we don't jump (anding both conditions). - For != this means true (oring both). - Take care about inverting the test. We need to jump - to our target if the result was unordered and test wasn't NE, - otherwise if unordered we don't want to jump. */ - vtop->c.i &= ~0x100; - if (!inv == (vtop->c.i != TOK_NE)) - o(0x067a); /* jp +6 */ - else { - g(0x0f); - t = psym(0x8a, t); /* jp t */ - } - } - g(0x0f); - t = psym((vtop->c.i - 16) ^ inv, t); - } else if (v == VT_JMP || v == VT_JMPI) { - /* && or || optimization */ - if ((v & 1) == inv) { - /* insert vtop->c jump list in t */ - p = &vtop->c.i; - while (*p != 0) - p = (int *)(cur_text_section->data + *p); - *p = t; - t = vtop->c.i; - } else { - t = gjmp(t); - gsym(vtop->c.i); - } - } - vtop--; - return t; -} - -/* generate an integer binary operation */ -void gen_opi(int op) -{ - int r, fr, opc, c; - int ll, uu, cc; - - ll = is64_type(vtop[-1].type.t); - uu = (vtop[-1].type.t & VT_UNSIGNED) != 0; - cc = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; - - switch(op) { - case '+': - case TOK_ADDC1: /* add with carry generation */ - opc = 0; - gen_op8: - if (cc && (!ll || (int)vtop->c.ll == vtop->c.ll)) { - /* constant case */ - vswap(); - r = gv(RC_INT); - vswap(); - c = vtop->c.i; - if (c == (char)c) { - /* XXX: generate inc and dec for smaller code ? */ - orex(ll, r, 0, 0x83); - o(0xc0 | (opc << 3) | REG_VALUE(r)); - g(c); - } else { - orex(ll, r, 0, 0x81); - oad(0xc0 | (opc << 3) | REG_VALUE(r), c); - } - } else { - gv2(RC_INT, RC_INT); - r = vtop[-1].r; - fr = vtop[0].r; - orex(ll, r, fr, (opc << 3) | 0x01); - o(0xc0 + REG_VALUE(r) + REG_VALUE(fr) * 8); - } - vtop--; - if (op >= TOK_ULT && op <= TOK_GT) { - vtop->r = VT_CMP; - vtop->c.i = op; - } - break; - case '-': - case TOK_SUBC1: /* sub with carry generation */ - opc = 5; - goto gen_op8; - case TOK_ADDC2: /* add with carry use */ - opc = 2; - goto gen_op8; - case TOK_SUBC2: /* sub with carry use */ - opc = 3; - goto gen_op8; - case '&': - opc = 4; - goto gen_op8; - case '^': - opc = 6; - goto gen_op8; - case '|': - opc = 1; - goto gen_op8; - case '*': - gv2(RC_INT, RC_INT); - r = vtop[-1].r; - fr = vtop[0].r; - orex(ll, fr, r, 0xaf0f); /* imul fr, r */ - o(0xc0 + REG_VALUE(fr) + REG_VALUE(r) * 8); - vtop--; - break; - case TOK_SHL: - opc = 4; - goto gen_shift; - case TOK_SHR: - opc = 5; - goto gen_shift; - case TOK_SAR: - opc = 7; - gen_shift: - opc = 0xc0 | (opc << 3); - if (cc) { - /* constant case */ - vswap(); - r = gv(RC_INT); - vswap(); - orex(ll, r, 0, 0xc1); /* shl/shr/sar $xxx, r */ - o(opc | REG_VALUE(r)); - g(vtop->c.i & (ll ? 63 : 31)); - } else { - /* we generate the shift in ecx */ - gv2(RC_INT, RC_RCX); - r = vtop[-1].r; - orex(ll, r, 0, 0xd3); /* shl/shr/sar %cl, r */ - o(opc | REG_VALUE(r)); - } - vtop--; - break; - case TOK_UDIV: - case TOK_UMOD: - uu = 1; - goto divmod; - case '/': - case '%': - case TOK_PDIV: - uu = 0; - divmod: - /* first operand must be in eax */ - /* XXX: need better constraint for second operand */ - gv2(RC_RAX, RC_RCX); - r = vtop[-1].r; - fr = vtop[0].r; - vtop--; - save_reg(TREG_RDX); - orex(ll, 0, 0, uu ? 0xd231 : 0x99); /* xor %edx,%edx : cqto */ - orex(ll, fr, 0, 0xf7); /* div fr, %eax */ - o((uu ? 0xf0 : 0xf8) + REG_VALUE(fr)); - if (op == '%' || op == TOK_UMOD) - r = TREG_RDX; - else - r = TREG_RAX; - vtop->r = r; - break; - default: - opc = 7; - goto gen_op8; - } -} - -void gen_opl(int op) -{ - gen_opi(op); -} - -/* generate a floating point operation 'v = t1 op t2' instruction. The - two operands are guaranted to have the same floating point type */ -/* XXX: need to use ST1 too */ -void gen_opf(int op) -{ - int a, ft, fc, swapped, r; - int float_type = - (vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_FLOAT; - - /* convert constants to memory references */ - if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { - vswap(); - gv(float_type); - vswap(); - } - if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) - gv(float_type); - - /* must put at least one value in the floating point register */ - if ((vtop[-1].r & VT_LVAL) && - (vtop[0].r & VT_LVAL)) { - vswap(); - gv(float_type); - vswap(); - } - swapped = 0; - /* swap the stack if needed so that t1 is the register and t2 is - the memory reference */ - if (vtop[-1].r & VT_LVAL) { - vswap(); - swapped = 1; - } - if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { - if (op >= TOK_ULT && op <= TOK_GT) { - /* load on stack second operand */ - load(TREG_ST0, vtop); - save_reg(TREG_RAX); /* eax is used by FP comparison code */ - if (op == TOK_GE || op == TOK_GT) - swapped = !swapped; - else if (op == TOK_EQ || op == TOK_NE) - swapped = 0; - if (swapped) - o(0xc9d9); /* fxch %st(1) */ - if (op == TOK_EQ || op == TOK_NE) - o(0xe9da); /* fucompp */ - else - o(0xd9de); /* fcompp */ - o(0xe0df); /* fnstsw %ax */ - if (op == TOK_EQ) { - o(0x45e480); /* and $0x45, %ah */ - o(0x40fC80); /* cmp $0x40, %ah */ - } else if (op == TOK_NE) { - o(0x45e480); /* and $0x45, %ah */ - o(0x40f480); /* xor $0x40, %ah */ - op = TOK_NE; - } else if (op == TOK_GE || op == TOK_LE) { - o(0x05c4f6); /* test $0x05, %ah */ - op = TOK_EQ; - } else { - o(0x45c4f6); /* test $0x45, %ah */ - op = TOK_EQ; - } - vtop--; - vtop->r = VT_CMP; - vtop->c.i = op; - } else { - /* no memory reference possible for long double operations */ - load(TREG_ST0, vtop); - swapped = !swapped; - - switch(op) { - default: - case '+': - a = 0; - break; - case '-': - a = 4; - if (swapped) - a++; - break; - case '*': - a = 1; - break; - case '/': - a = 6; - if (swapped) - a++; - break; - } - ft = vtop->type.t; - fc = vtop->c.ul; - o(0xde); /* fxxxp %st, %st(1) */ - o(0xc1 + (a << 3)); - vtop--; - } - } else { - if (op >= TOK_ULT && op <= TOK_GT) { - /* if saved lvalue, then we must reload it */ - r = vtop->r; - fc = vtop->c.ul; - if ((r & VT_VALMASK) == VT_LLOCAL) { - SValue v1; - r = get_reg(RC_INT); - v1.type.t = VT_PTR; - v1.r = VT_LOCAL | VT_LVAL; - v1.c.ul = fc; - load(r, &v1); - fc = 0; - } - - if (op == TOK_EQ || op == TOK_NE) { - swapped = 0; - } else { - if (op == TOK_LE || op == TOK_LT) - swapped = !swapped; - if (op == TOK_LE || op == TOK_GE) { - op = 0x93; /* setae */ - } else { - op = 0x97; /* seta */ - } - } - - if (swapped) { - gv(RC_FLOAT); - vswap(); - } - assert(!(vtop[-1].r & VT_LVAL)); - - if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) - o(0x66); - if (op == TOK_EQ || op == TOK_NE) - o(0x2e0f); /* ucomisd */ - else - o(0x2f0f); /* comisd */ - - if (vtop->r & VT_LVAL) { - gen_modrm(vtop[-1].r, r, vtop->sym, fc); - } else { - o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8); - } - - vtop--; - vtop->r = VT_CMP; - vtop->c.i = op | 0x100; - } else { - assert((vtop->type.t & VT_BTYPE) != VT_LDOUBLE); - switch(op) { - default: - case '+': - a = 0; - break; - case '-': - a = 4; - break; - case '*': - a = 1; - break; - case '/': - a = 6; - break; - } - ft = vtop->type.t; - fc = vtop->c.ul; - assert((ft & VT_BTYPE) != VT_LDOUBLE); - - r = vtop->r; - /* if saved lvalue, then we must reload it */ - if ((vtop->r & VT_VALMASK) == VT_LLOCAL) { - SValue v1; - r = get_reg(RC_INT); - v1.type.t = VT_PTR; - v1.r = VT_LOCAL | VT_LVAL; - v1.c.ul = fc; - load(r, &v1); - fc = 0; - } - - assert(!(vtop[-1].r & VT_LVAL)); - if (swapped) { - assert(vtop->r & VT_LVAL); - gv(RC_FLOAT); - vswap(); - } - - if ((ft & VT_BTYPE) == VT_DOUBLE) { - o(0xf2); - } else { - o(0xf3); - } - o(0x0f); - o(0x58 + a); - - if (vtop->r & VT_LVAL) { - gen_modrm(vtop[-1].r, r, vtop->sym, fc); - } else { - o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8); - } - - vtop--; - } - } -} - -/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' - and 'long long' cases. */ -void gen_cvt_itof(int t) -{ - if ((t & VT_BTYPE) == VT_LDOUBLE) { - save_reg(TREG_ST0); - gv(RC_INT); - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { - /* signed long long to float/double/long double (unsigned case - is handled generically) */ - o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ - o(0x242cdf); /* fildll (%rsp) */ - o(0x08c48348); /* add $8, %rsp */ - } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == - (VT_INT | VT_UNSIGNED)) { - /* unsigned int to float/double/long double */ - o(0x6a); /* push $0 */ - g(0x00); - o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ - o(0x242cdf); /* fildll (%rsp) */ - o(0x10c48348); /* add $16, %rsp */ - } else { - /* int to float/double/long double */ - o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ - o(0x2404db); /* fildl (%rsp) */ - o(0x08c48348); /* add $8, %rsp */ - } - vtop->r = TREG_ST0; - } else { - int r = get_reg(RC_FLOAT); - gv(RC_INT); - o(0xf2 + ((t & VT_BTYPE) == VT_FLOAT?1:0)); - if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == - (VT_INT | VT_UNSIGNED) || - (vtop->type.t & VT_BTYPE) == VT_LLONG) { - o(0x48); /* REX */ - } - o(0x2a0f); - o(0xc0 + (vtop->r & VT_VALMASK) + REG_VALUE(r)*8); /* cvtsi2sd */ - vtop->r = r; - } -} - -/* convert from one floating point type to another */ -void gen_cvt_ftof(int t) -{ - int ft, bt, tbt; - - ft = vtop->type.t; - bt = ft & VT_BTYPE; - tbt = t & VT_BTYPE; - - if (bt == VT_FLOAT) { - gv(RC_FLOAT); - if (tbt == VT_DOUBLE) { - o(0x140f); /* unpcklps */ - o(0xc0 + REG_VALUE(vtop->r)*9); - o(0x5a0f); /* cvtps2pd */ - o(0xc0 + REG_VALUE(vtop->r)*9); - } else if (tbt == VT_LDOUBLE) { - save_reg(RC_ST0); - /* movss %xmm0,-0x10(%rsp) */ - o(0x110ff3); - o(0x44 + REG_VALUE(vtop->r)*8); - o(0xf024); - o(0xf02444d9); /* flds -0x10(%rsp) */ - vtop->r = TREG_ST0; - } - } else if (bt == VT_DOUBLE) { - gv(RC_FLOAT); - if (tbt == VT_FLOAT) { - o(0x140f66); /* unpcklpd */ - o(0xc0 + REG_VALUE(vtop->r)*9); - o(0x5a0f66); /* cvtpd2ps */ - o(0xc0 + REG_VALUE(vtop->r)*9); - } else if (tbt == VT_LDOUBLE) { - save_reg(RC_ST0); - /* movsd %xmm0,-0x10(%rsp) */ - o(0x110ff2); - o(0x44 + REG_VALUE(vtop->r)*8); - o(0xf024); - o(0xf02444dd); /* fldl -0x10(%rsp) */ - vtop->r = TREG_ST0; - } - } else { - int r; - gv(RC_ST0); - r = get_reg(RC_FLOAT); - if (tbt == VT_DOUBLE) { - o(0xf0245cdd); /* fstpl -0x10(%rsp) */ - /* movsd -0x10(%rsp),%xmm0 */ - o(0x100ff2); - o(0x44 + REG_VALUE(r)*8); - o(0xf024); - vtop->r = r; - } else if (tbt == VT_FLOAT) { - o(0xf0245cd9); /* fstps -0x10(%rsp) */ - /* movss -0x10(%rsp),%xmm0 */ - o(0x100ff3); - o(0x44 + REG_VALUE(r)*8); - o(0xf024); - vtop->r = r; - } - } -} - -/* convert fp to int 't' type */ -void gen_cvt_ftoi(int t) -{ - int ft, bt, size, r; - ft = vtop->type.t; - bt = ft & VT_BTYPE; - if (bt == VT_LDOUBLE) { - gen_cvt_ftof(VT_DOUBLE); - bt = VT_DOUBLE; - } - - gv(RC_FLOAT); - if (t != VT_INT) - size = 8; - else - size = 4; - - r = get_reg(RC_INT); - if (bt == VT_FLOAT) { - o(0xf3); - } else if (bt == VT_DOUBLE) { - o(0xf2); - } else { - assert(0); - } - orex(size == 8, r, 0, 0x2c0f); /* cvttss2si or cvttsd2si */ - o(0xc0 + REG_VALUE(vtop->r) + REG_VALUE(r)*8); - vtop->r = r; -} - -/* computed goto support */ -void ggoto(void) -{ - gcall_or_jmp(1); - vtop--; -} - -/* Save the stack pointer onto the stack and return the location of its address */ -ST_FUNC void gen_vla_sp_save(int addr) { - /* mov %rsp,addr(%rbp)*/ - gen_modrm64(0x89, TREG_RSP, VT_LOCAL, NULL, addr); -} - -/* Restore the SP from a location on the stack */ -ST_FUNC void gen_vla_sp_restore(int addr) { - gen_modrm64(0x8b, TREG_RSP, VT_LOCAL, NULL, addr); -} - -/* Subtract from the stack pointer, and push the resulting value onto the stack */ -ST_FUNC void gen_vla_alloc(CType *type, int align) { -#ifdef TCC_TARGET_PE - /* alloca does more than just adjust %rsp on Windows */ - vpush_global_sym(&func_old_type, TOK_alloca); - vswap(); /* Move alloca ref past allocation size */ - gfunc_call(1); - vset(type, REG_IRET, 0); -#else - int r = gv(RC_INT); /* allocation size */ - /* sub r,%rsp */ - o(0x2b48); - o(0xe0 | REG_VALUE(r)); - /* We align to 16 bytes rather than align */ - /* and ~15, %rsp */ - o(0xf0e48348); - vpop(); -#endif -} - - -/* end of x86-64 code generator */ -/*************************************************************/ -#endif /* ! TARGET_DEFS_ONLY */ -/******************************************************/ |
