/* * This file is based off of * * x86-codegen.h: Macros for generating x86 code * * Copyright (C) 2000 Intel Corporation. All rights reserved. * Copyright (C) 2001, 2002 Ximian, Inc. * Copyright (C) 2003, 2004, 2007 Soeren Sandmann * Copyright (C) 2007 Red Hat, Inc. * * Copyright statement from ORP project: * * Copyright (C) 2000, Intel Corporation, all rights reserved. Third party * copyrights are property of their respective owners. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. Redistributions * in binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. The name of Intel * Corporation may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: * Paolo Molaro (lupus@ximian.com) * Intel Corporation (ORP Project) * Sergey Chaban (serge@wildwestsoftware.com) * Dietmar Maurer (dietmar@ximian.com) * Patrik Torstensson * Soren Sandmann */ #include #include #include #include #include #include #include #include "codex86.h" typedef enum { SHIFT_ROL = 0, SHIFT_ROR = 1, SHIFT_RCL = 2, SHIFT_RCR = 3, SHIFT_SHL = 4, SHIFT_SHR = 5, SHIFT_SAR = 7 } ShiftOpcode; typedef enum { ALU_ADD = 0, ALU_OR = 1, ALU_ADC = 2, ALU_SBB = 3, ALU_AND = 4, ALU_SUB = 5, ALU_XOR = 6, ALU_CMP = 7 } AluOpcode; typedef enum { CONDITION_EQ = 0, CONDITION_E = 0, CONDITION_Z = 0, CONDITION_NE = 1, CONDITION_NZ = 1, CONDITION_LT = 2, CONDITION_B = 2, CONDITION_C = 2, CONDITION_NAE = 2, CONDITION_LE = 3, CONDITION_BE = 3, CONDITION_NA = 3, CONDITION_GT = 4, CONDITION_A = 4, CONDITION_NBE = 4, CONDITION_GE = 5, CONDITION_AE = 5, CONDITION_NB = 5, CONDITION_NC = 5, CONDITION_LZ = 6, CONDITION_S = 6, CONDITION_GEZ = 7, CONDITION_NS = 7, CONDITION_P = 8, CONDITION_PE = 8, CONDITION_NP = 9, CONDITION_PO = 9, CONDITION_O = 10, CONDITION_NO = 11, N_CONDITIONS } ConditionCode; static const int register_map[] = { EAX, 0, EBX, 3, ECX, 1, EDX, 2, ESP, 4, EBP, 5, ESI, 6, EDI, 7, MM0, 0, MM1, 1, MM2, 2, MM3, 3, MM4, 4, MM5, 5, MM6, 6, MM7, 7, XMM0, 0, XMM1, 1, XMM2, 2, XMM3, 3, XMM4, 4, XMM5, 5, XMM6, 6, XMM7, 7, }; static int is_regno (int regno) { return regno == NO_REG || (regno >= 0 && regno <= 7); } static int reg_to_regno (reg_t reg) { int i; if (reg == NO_REG) return NO_REG; for (i = 0; i < sizeof (register_map) / sizeof (int); i += 2) { if (register_map[i] == reg) return register_map[i + 1]; } assert (0); return -1; } static int op_to_regno (op_t op) { if (op.type == REGNO) { return op.regno.regno; } else if (op.type == REG) { return reg_to_regno (op.reg.reg); } else { assert (0); return -1; } } static op_t regno (int no) { op_t r; r.type = REGNO; r.regno.regno = no; return r; } typedef struct Label Label; struct Label { const char *name; uint8_t *position; Label *next; }; struct Asm { Label *labels; Label *jumps; uint8_t *start; uint8_t *code; uint8_t *exec; }; static void allocate_mem (Asm *a) { /* See * * http://people.redhat.com/drepper/selinux-mem.html * * for information about how to appease selinux when dynamically * generating machine code. * * It's worth noting that the file we generate could * be an ELF file so that gdb and the profilers will deal * correctly with it if we don't delete the file. Maybe * determined by a runtime flag or environment variable. * * Ie., PIXMAN_GENERATE_ELF, that would cause the file generated * to (a) be an ELF file, and (b) not be deleted immediately. * Possibly an atexit() handler could be installed. * * The names in the file should probably be on the form * * pixman__generated_OVER_8888_8_8888_flags() * */ char tmpfname[] = "/home/ssp/execmemXXXXXX"; int fd = mkstemp (tmpfname); unlink (tmpfname); ftruncate (fd, 65536); a->start = a->code = mmap (NULL, 65536, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); a->exec = mmap (NULL, 65536, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0); } Asm * asm_new (void) { Asm *a = malloc (sizeof (Asm)); allocate_mem (a); a->labels = a->jumps = NULL; return a; } /* Functions */ void asm_function_preamble (Asm *a) { x86_push (a, x86_ebp()); x86_mov (a, x86_esp(), x86_ebp()); x86_push (a, x86_ebx()); x86_push (a, x86_esi()); x86_push (a, x86_edi()); } void asm_function_postamble (Asm *a) { x86_pop (a, x86_edi()); x86_pop (a, x86_esi()); x86_pop (a, x86_ebx()); x86_pop (a, x86_ebp()); x86_ret (a); } static void free_labels (Label *l) { while (l) { Label *next = l->next; free (l); l = next; } } static uint8_t is_imm8 (int32_t imm) { return imm >= -128 && imm <= 127; } static uint8_t * emit_imm32 (uint8_t *code, int32_t imm) { *code++ = (imm & 0x000000ff) >> 0; *code++ = (imm & 0x0000ff00) >> 8; *code++ = (imm & 0x00ff0000) >> 16; *code++ = (imm & 0xff000000) >> 24; return code; } static uint8_t * emit_imm8 (uint8_t *code, int32_t imm) { *code++ = imm & 0xff; return code; } /* * target is the position in the code where to jump to: * target = code; * .. output loop code... * x86_mov_reg_imm (code, X86_EAX, 0); * loop = code; * x86_loop (code, -1); * ... finish method * * patch displacement * x86_patch (loop, target); * * ins should point at the start of the instruction that encodes a target. * the instruction is inspected for validity and the correct displacement * is inserted. */ static void patch (uint8_t *code, const uint8_t *target) { uint8_t* pos = code + 1; int size = 0; int disp; switch (*code) { /* call, jump32 */ case 0xe8: case 0xe9: ++size; break; /* prefix for 32-bit disp */ case 0x0f: if (!(*pos >= 0x70 && *pos <= 0x8f)) assert (0); ++size; ++pos; break; /* loop */ case 0xe0: case 0xe1: case 0xe2: /* jump8 */ case 0xeb: /* conditional jump opcodes */ case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f: break; default: assert (0); } disp = target - pos; if (size) { emit_imm32 (pos, disp - 4); } else if (is_imm8 (disp - 1)) { emit_imm8 (pos, disp - 1); } else { assert (0); } } static void fixup_jumps (Asm *a) { Label *j, *l; for (j = a->jumps; j != NULL; j = j->next) { for (l = a->labels; l != NULL; l = l->next) { if (strcmp (j->name, l->name) == 0) { patch (j->position, l->position); break; } } if (!l) { /* Nothing was patched */ printf ("No label \"%s\"\n", j->name); assert (l); } } } uint8_t * asm_emit (Asm *a) { uint8_t *res; fixup_jumps (a); free_labels (a->labels); free_labels (a->jumps); a->labels = a->jumps = NULL; res = a->exec; munmap (a->start, 65536); allocate_mem (a); return res; } void asm_free_code (uint8_t *code) { munmap (code, 65536); } void asm_free (Asm *a) { free (a->code); free (a); } void asm_label (Asm *a, const char *name) { Label *l = malloc (sizeof (Label)); l->name = name; l->position = a->code; l->next = a->labels; a->labels = l; } /* Ops */ op_t x86_imm (uint32_t imm) { op_t r; r.type = IMM; r.immediate.value = imm; return r; } op_t x86_memindex (op_t base, int32_t disp, op_t index, uint32_t shift) { op_t r; assert (base.type == REG); assert (index.type == REG); r.type = MEM; r.mem.base_reg = base.reg.reg; r.mem.disp = disp; r.mem.index_reg = index.reg.reg; r.mem.shift = shift; return r; } static op_t make_reg (int8_t reg) { op_t r; r.type = REG; r.reg.reg = reg; return r; } op_t x86_regp (op_t reg) { return x86_membase (reg, 0); } op_t x86_address (void *address) { return x86_membase (make_reg (NO_REG), (uint32_t)address); } op_t x86_membase (op_t base, int32_t disp) { return x86_memindex (base, disp, make_reg (NO_REG), 0); } static void add_jump (Asm *a, const char *label) { Label *l = malloc (sizeof (Label)); l->name = label; l->position = a->code; l->next = a->jumps; a->jumps = l; assert (label != NULL); } static const uint8_t cc_unsigned_map [N_CONDITIONS] = { 0x74, /* eq */ 0x75, /* ne */ 0x72, /* lt */ 0x76, /* le */ 0x77, /* gt */ 0x73, /* ge */ 0x78, /* lz */ 0x79, /* gez */ 0x7a, /* p */ 0x7b, /* np */ 0x70, /* o */ 0x71, /* no */ }; static const uint8_t cc_signed_map [N_CONDITIONS] = { 0x74, /* eq */ 0x75, /* ne */ 0x7c, /* lt */ 0x7e, /* le */ 0x7f, /* gt */ 0x7d, /* ge */ 0x78, /* lz */ 0x79, /* gez */ 0x7a, /* p */ 0x7b, /* np */ 0x70, /* o */ 0x71, /* no */ }; static uint8_t * branch8 (uint8_t *code, ConditionCode cond, uint32_t imm, int is_signed) { if (is_signed) *code++ = cc_signed_map[cond]; else *code++ = cc_unsigned_map[cond]; return emit_imm8 (code, imm); } static uint8_t * branch32 (uint8_t *code, ConditionCode cond, uint32_t imm, int is_signed) { *code++ = 0x0f; if (is_signed) *code++ = cc_signed_map[cond] + 0x10; else *code++ = cc_unsigned_map[cond] + 0x10; return emit_imm32 (code, imm); } static void branch (Asm *a, ConditionCode cc, const char *label, int is_signed) { Label *l; const uint8_t *target = NULL; for (l = a->labels; l != NULL; l = l->next) { if (strcmp (l->name, label) == 0) { target = l->position; break; } } if (target) { int offset = target - a->code - 2; if (is_imm8 (offset)) { a->code = branch8 (a->code, cc, offset, is_signed); } else { offset -= 4; a->code = branch32 (a->code, cc, offset, is_signed); } } else { add_jump (a, label); /* We will patch it later */ a->code = branch32 (a->code, cc, 0x00000000, is_signed); } } void x86_jmp (Asm *a, const char *label) { add_jump (a, label); *(a->code)++ = 0xe9; a->code = emit_imm32 (a->code, 0x0000000000); } void x86_jnz (Asm *a, const char *label) { branch (a, CONDITION_NZ, label, 0); } void x86_jz (Asm *a, const char *label) { branch (a, CONDITION_Z, label, 0); } void x86_je (Asm *a, const char *label) { branch (a, CONDITION_E, label, 0); } void x86_jge (Asm *a, const char *label) { branch (a, CONDITION_GE, label, 0); } void x86_jg_s (Asm *a, const char *label) { branch (a, CONDITION_GT, label, 1); } void x86_jle_s (Asm *a, const char *label) { branch (a, CONDITION_LE, label, 1); } void x86_jne (Asm *a, const char *label) { branch (a, CONDITION_NE, label, 0); } void x86_emms (Asm *a) { *(a->code)++ = 0x0f; *(a->code)++ = 0x77; } /* Emit utilities */ static uint8_t * emit_address_byte (uint8_t *code, int m, int o, int r) { *code++ = ((m & 0x03) << 6) | ((o & 0x07) << 3) | ((r & 0x07)); return code; } static uint8_t * emit_address (uint8_t *code, int regno, uint32_t address) { code = emit_address_byte (code, 0, regno, 5); code = emit_imm32 (code, address); return code; } static uint8_t * emit_membase (uint8_t *code, int regno, int base_regno, uint32_t disp) { int esp_regno = reg_to_regno (ESP); int ebp_regno = reg_to_regno (EBP); assert (is_regno (regno)); assert (is_regno (base_regno)); if (base_regno == NO_REG) { code = emit_address (code, regno, disp); } else if (base_regno == esp_regno) { if (disp == 0) { code = emit_address_byte (code, 0, regno, esp_regno); code = emit_address_byte (code, 0, esp_regno, esp_regno); } else if (is_imm8 (disp)) { code = emit_address_byte (code, 1, regno, esp_regno); code = emit_address_byte (code, 0, esp_regno, esp_regno); code = emit_imm8 (code, disp); } else { code = emit_address_byte (code, 2, regno, esp_regno); code = emit_address_byte (code, 0, esp_regno, esp_regno); code = emit_imm32 (code, disp); } } else if (disp == 0 && base_regno != ebp_regno) { code = emit_address_byte (code, 0, regno, base_regno); } else if (is_imm8 (disp)) { code = emit_address_byte (code, 1, regno, base_regno); code = emit_imm8 (code, disp); } else { code = emit_address_byte (code, 2, regno, base_regno); code = emit_imm32 (code, disp); } return code; } static uint8_t * emit_memindex (uint8_t *code, int regno, int base_regno, int disp, int index_regno, int shift) { int ebp_regno = reg_to_regno (EBP); assert (is_regno (regno)); assert (is_regno (base_regno)); assert (is_regno (index_regno)); if (index_regno == NO_REG) { code = emit_membase (code, regno, base_regno, disp); } else if (base_regno == NO_REG) { code = emit_address_byte (code, 0, regno, 4); code = emit_address_byte (code, shift, index_regno, 5); code = emit_imm32 (code, disp); } else if (disp == 0 && base_regno != ebp_regno) { code = emit_address_byte (code, 0, regno, 4); code = emit_address_byte (code, shift, index_regno, base_regno); } else if (is_imm8 (disp)) { code = emit_address_byte (code, 1, regno, 4); code = emit_address_byte (code, shift, index_regno, base_regno); code = emit_imm8 (code, disp); } else { code = emit_address_byte (code, 2, regno, 4); code = emit_address_byte (code, shift, index_regno, 5); code = emit_imm32 (code, disp); } return code; } static uint8_t is_mem (op_t op) { return op.type == MEM; } static int is_reg (op_t op) { return op.type == REG || op.type == REGNO; } static uint8_t is_gp (op_t op) { return (op.type == REG) && (op.reg.reg >= EAX && op.reg.reg <= EDI); } static int is_sse (op_t op) { return (op.type == REG) && (op.reg.reg >= XMM0 && op.reg.reg <= XMM7); } static int is_mmx (op_t op) { return (op.type == REG) && (op.reg.reg >= MM0 && op.reg.reg <= MM7); } static int is_imm (op_t op) { return op.type == IMM; } /* Register-to-register op. Regmem is the operand that *could* have been * memory, reg is the operand that *must* be a register * for the opcode * * E.g., in * * pxor xmm1, xmm2/m128 * * give xmm1 as reg and xmm2/m128 as regmem, but in * * movd r/m32, mm * * give mm must be in reg and r/m32 in regmem. */ static uint8_t * emit_reg_reg (uint8_t *code, op_t reg, op_t regmem) { if (is_reg (reg) && is_reg (regmem)) { return emit_address_byte ( code, 3, op_to_regno (reg), op_to_regno (regmem)); } else { assert (0); } } static uint8_t * emit_reg_mem (uint8_t *code, op_t op, op_t dest) { op_t mem; int regno; int base_regno; int index_regno; int shift, disp; if (is_mem (op) && is_reg (dest)) { mem = op; regno = op_to_regno (dest); } else if (is_reg (op) && is_mem (dest)) { mem = dest; regno = op_to_regno (op); } else { assert (0); } base_regno = reg_to_regno (mem.mem.base_reg); index_regno = reg_to_regno (mem.mem.index_reg); shift = mem.mem.shift; disp = mem.mem.disp; return emit_memindex (code, regno, base_regno, disp, index_regno, shift); } /* * Instructions */ void x86_lea (Asm *a, op_t op, op_t dest) { if (is_mem (op) && is_gp (dest)) { *(a->code)++ = 0x8d; a->code = emit_reg_mem (a->code, dest, op); } else { assert (0); } } void x86_mov (Asm *a, op_t from, op_t to) { if (is_mem (from) && is_gp (to)) { *(a->code)++ = 0x8b; a->code = emit_reg_mem (a->code, from, to); } else if (is_gp (from) && is_mem (to)) { *(a->code)++ = 0x89; a->code = emit_reg_mem (a->code, from, to); } else if (is_gp (to) && is_gp (from)) { *(a->code)++ = 0x89; a->code = emit_reg_reg (a->code, from, to); } else if (is_mem (to) && is_imm (from)) { *(a->code)++ = 0xc7; a->code = emit_reg_mem (a->code, regno (0), to); a->code = emit_imm32 (a->code, from.immediate.value); } else if (is_gp (to) && is_imm (from)) { *(a->code)++ = 0xb8 + op_to_regno (to); a->code = emit_imm32 (a->code, from.immediate.value); } else { assert (0); } } void x86_movd (Asm *a, op_t from, op_t to) { if (is_gp (from) && is_sse (to)) { *(a->code++) = 0x66; *(a->code++) = 0x0f; *(a->code++) = 0x6e; a->code = emit_reg_reg (a->code, to, from); } else { assert (0); } } void x86_movzwx (Asm *a, op_t from, op_t to) { if (is_mem (from) && is_gp (to)) { *(a->code++) = 0x0f; *(a->code++) = 0xb7; a->code = emit_reg_mem (a->code, from, to); } else { assert (0); } } void x86_movdqa (Asm *a, op_t from, op_t to) { if (is_sse (to)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x6f; if (is_mem (from)) a->code = emit_reg_mem (a->code, to, from); else if (is_sse (from)) a->code = emit_reg_reg (a->code, to, from); else assert (0); } else if (is_sse (from) && is_mem (to)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x7f; a->code = emit_reg_mem (a->code, to, from); } else { assert (0); } } void x86_movdqu (Asm *a, op_t from, op_t to) { if (is_sse (to)) { *(a->code)++ = 0xf3; *(a->code)++ = 0x0f; *(a->code)++ = 0x6f; if (is_mem (from)) a->code = emit_reg_mem (a->code, to, from); else if (is_sse (from)) a->code = emit_reg_reg (a->code, to, from); else assert (0); } else if (is_mem (to) && is_sse (from)) { *(a->code)++ = 0xf3; *(a->code)++ = 0x0f; *(a->code)++ = 0x7f; a->code = emit_reg_mem (a->code, to, from); } else { assert (0); } } void x86_movq (Asm *a, op_t from, op_t to) { if (is_sse (to)) { *(a->code)++ = 0xf3; *(a->code)++ = 0x0f; *(a->code) = 0x7e; if (is_mem (from)) a->code = emit_reg_mem (a->code, from, to); else if (is_sse (from)) a->code = emit_reg_reg (a->code, from, to); else assert (0); } else if (is_sse (from) && is_mem (to)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0xd6; a->code = emit_reg_mem (a->code, from, to); } else if (is_mmx (from) && is_mem (to)) { *(a->code)++ = 0x0f; *(a->code)++ = 0x7f; a->code = emit_reg_mem (a->code, from, to); } else if (is_mmx (to)) { *(a->code)++ = 0x0f; *(a->code)++ = 0x6f; if (is_mem (from)) a->code = emit_reg_mem (a->code, to, from); else if (is_mmx (from)) a->code = emit_reg_reg (a->code, to, from); else assert (0); } else { assert (0); } } void x86_neg (Asm *a, op_t dest) { if (is_gp (dest)) { *(a->code++) = 0xf7; a->code = emit_reg_reg (a->code, regno (3), dest); } else if (is_mem (dest)) { *(a->code++) = 0xf7; a->code = emit_reg_mem (a->code, regno (3), dest); } else { assert (0); } } void x86_push (Asm *a, op_t what) { if (is_gp (what)) { *(a->code)++ = 0x50 + op_to_regno (what); } else if (is_imm (what)) { *(a->code)++ = 0x68; a->code = emit_imm32 (a->code, what.immediate.value); } else if (is_mem (what)) { *(a->code)++ = 0xff; a->code = emit_reg_mem (a->code, regno (6), what); } else { assert (0); } } void x86_pusha (Asm *a) { *(a->code)++ = 0x60; } void x86_popa (Asm *a) { *(a->code)++ = 0x61; } void x86_pushf (Asm *a) { *(a->code)++ = 0x9c; } void x86_popf (Asm *a) { *(a->code)++ = 0x9d; } void x86_pop (Asm *a, op_t what) { if (is_mem (what)) { *(a->code)++ = 0x8f; a->code = emit_reg_mem (a->code, regno (0), what); } else if (is_gp (what)) { *(a->code)++ = 0x58 + op_to_regno (what); } else { assert (0); } } void x86_packuswb (Asm *a, op_t op, op_t dest) { if (is_sse (op) && is_sse (dest)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x67; a->code = emit_reg_reg (a->code, dest, op); } else { assert (0); } } void x86_punpcklbw (Asm *a, op_t op, op_t dest) { if (is_sse (op) && is_sse (dest)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x60; a->code = emit_reg_reg (a->code, dest, op); } else { assert (0); } } void x86_punpckhbw (Asm *a, op_t op, op_t dest) { if (is_sse (op) && is_sse (dest)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x68; a->code = emit_reg_reg (a->code, dest, op); } else { assert (0); } } void x86_pshufb (Asm *a, op_t op, op_t dest) { /* SSE3 */ if (is_mem (op) && is_sse (dest)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x38; *(a->code)++ = 0x00; a->code = emit_reg_mem (a->code, dest, op); } else { assert (0); } } void x86_pshuflw (Asm *a, op_t op, op_t dest, op_t imm) { if (is_sse (op) && is_sse (dest) && is_imm (imm)) { uint32_t i = imm.immediate.value; assert (i >= 0 && i <= 255); *(a->code)++ = 0xf2; *(a->code)++ = 0x0f; *(a->code)++ = 0x70; a->code = emit_reg_reg (a->code, dest, op); *(a->code)++ = (i & 0xff); } else { assert (0); } } void x86_pshufhw (Asm *a, op_t op, op_t dest, op_t imm) { if (is_sse (op) && is_sse (dest) && is_imm (imm)) { uint32_t i = imm.immediate.value; assert (i >= 0 && i <= 255); *(a->code)++ = 0xf3; *(a->code)++ = 0x0f; *(a->code)++ = 0x70; a->code = emit_reg_reg (a->code, dest, op); *(a->code)++ = (i & 0xff); } else { assert (0); } } void x86_pshufd (Asm *a, op_t op, op_t dest, op_t imm) { uint32_t i = imm.immediate.value; if (is_sse (op) && is_sse (dest) && is_imm (imm)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x70; a->code = emit_reg_reg (a->code, dest, op); *(a->code)++ = (i & 0xff); } else { assert (0); } } static uint8_t * alu_reg_imm (uint8_t *code, AluOpcode opc, op_t dest, op_t imm) { if (is_gp (dest) && is_imm (imm)) { uint32_t i = imm.immediate.value; if (op_to_regno (dest) == reg_to_regno (EAX)) { *code++ = (opc << 3) + 5; return emit_imm32 (code, i); } else if (is_imm8 (i)) { *code++ = 0x83; code = emit_reg_reg (code, regno (opc), dest); code = emit_imm8 (code, i); } else { *code++ = 0x81; code = emit_reg_reg (code, regno (opc), dest); code = emit_imm32 (code, i); } return code; } else { assert (0); } } static void x86_alu (Asm *a, AluOpcode opc, op_t op, op_t dest) { if (is_gp (dest) && is_imm (op)) { a->code = alu_reg_imm (a->code, opc, dest, op); } else if (is_mem (dest) && is_gp (op)) { *(a->code)++ = (opc << 3) + 1; a->code = emit_reg_mem (a->code, dest, op); } else if (is_gp (dest) && is_mem (op)) { *(a->code)++ = (opc << 3) + 3; a->code = emit_reg_mem (a->code, dest, op); } else if (is_gp (dest) && is_gp (op)) { *(a->code)++ = (opc << 3) + 3; a->code = emit_reg_reg (a->code, dest, op); } else { assert (0); } } void x86_sub (Asm *a, op_t op, op_t dest) { x86_alu (a, ALU_SUB, op, dest); } void x86_add (Asm *a, op_t op, op_t dest) { x86_alu (a, ALU_ADD, op, dest); } void x86_xor (Asm *a, op_t op, op_t dest) { x86_alu (a, ALU_XOR, op, dest); } void x86_cmp (Asm *a, op_t op, op_t dest) { x86_alu (a, ALU_CMP, op, dest); } void x86_test (Asm *a, op_t op, op_t dest) { if (is_gp (dest) && is_imm (op)) { uint32_t i = op.immediate.value; if (dest.reg.reg == EAX) { if (is_imm8 (i)) { *(a->code)++ = 0xa8; a->code = emit_imm8 (a->code, i); } else { *(a->code)++ = 0xa9; a->code = emit_imm32 (a->code, i); } } else { *(a->code)++ = 0xf7; a->code = emit_imm32 (a->code, i); } } else { assert (0); } } void x86_paddusb (Asm *a, op_t op, op_t dest) { if (is_mem (op) && is_mmx (dest)) { *(a->code)++ = 0x0f; *(a->code)++ = 0xdc; a->code = emit_reg_mem (a->code, op, dest); } else if (is_sse (op) && is_sse (dest)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0xdc; a->code = emit_reg_reg (a->code, dest, op); } else { assert (0); } } void x86_pmullw (Asm *a, op_t op, op_t dest) { if (is_sse (op) && is_sse (dest)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0xd5; a->code = emit_reg_reg (a->code, dest, op); } else { assert (0); } } void x86_pcmpeqw (Asm *a, op_t op, op_t dest) { if (is_sse (op) && is_sse (dest)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x75; a->code = emit_reg_reg (a->code, dest, op); } else { assert (0); } } void x86_psrlw (Asm *a, op_t op, op_t amount) { if (is_sse (op) && is_imm (amount)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x71; a->code = emit_reg_reg (a->code, regno (2), op); *(a->code)++ = (amount.immediate.value & 0xff); } else { assert (0); } } void x86_psllw (Asm *a, op_t op, op_t amount) { if (is_sse (op) && is_imm (amount)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0x71; a->code = emit_reg_reg (a->code, regno (6), op); *(a->code)++ = (amount.immediate.value & 0xff); } else { assert (0); } } void x86_paddusw (Asm *a, op_t op, op_t dest) { if (is_sse (op) && is_sse (dest)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0xdd; a->code = emit_reg_reg (a->code, dest, op); } else { assert (0); } } void x86_pxor (Asm *a, op_t op, op_t dest) { if (is_sse (op) && is_sse (dest)) { *(a->code)++ = 0x66; *(a->code)++ = 0x0f; *(a->code)++ = 0xef; a->code = emit_reg_reg (a->code, dest, op); } else { assert (0); } } void x86_cpuid (Asm *a) { *(a->code)++ = 0x0f; *(a->code)++ = 0xa2; } void x86_ret (Asm *a) { *(a->code)++ = 0xc3; } static void x86_shift (Asm *a, ShiftOpcode shift, op_t dest, op_t amount) { if (is_gp (dest) && is_imm (amount)) { int i = amount.immediate.value; assert (i <= 255); if (i == 1) *(a->code++) = 0xd1; else *(a->code++) = 0xc1; a->code = emit_reg_reg (a->code, regno (shift), dest); if (i > 1) *(a->code++) = i; } else { assert (0); } } void x86_shr (Asm *a, op_t what, op_t imm) { x86_shift (a, SHIFT_SHR, what, imm); } void x86_call (Asm *a, op_t op) { if (is_mem (op)) { *(a->code++) = 0xff; a->code = emit_reg_mem (a->code, regno (2), op); } else if (is_imm (op)) { int32_t offset = (uint8_t *)op.immediate.value - ((a->code - a->start) + a->exec); offset -= 5; *(a->code++) = 0xe8; a->code = emit_imm32 (a->code, offset); } else { assert (0); } } void x86_call_label (Asm *a, const char *label) { add_jump (a, label); x86_call (a, x86_imm (0)); } /* Register constructors */ op_t x86_eax (void){return make_reg (EAX);} op_t x86_ebx (void){return make_reg (EBX);} op_t x86_ecx (void){return make_reg (ECX);} op_t x86_edx (void){return make_reg (EDX);} op_t x86_esp (void){return make_reg (ESP);} op_t x86_ebp (void){return make_reg (EBP);} op_t x86_esi (void){return make_reg (ESI);} op_t x86_edi (void){return make_reg (EDI);} op_t x86_mm0 (void){return make_reg (MM0);} op_t x86_mm1 (void){return make_reg (MM1);} op_t x86_mm2 (void){return make_reg (MM2);} op_t x86_mm3 (void){return make_reg (MM3);} op_t x86_mm4 (void){return make_reg (MM4);} op_t x86_mm5 (void){return make_reg (MM5);} op_t x86_mm6 (void){return make_reg (MM6);} op_t x86_mm7 (void){return make_reg (MM7);} op_t x86_xmm0 (void){return make_reg (XMM0);} op_t x86_xmm1 (void){return make_reg (XMM1);} op_t x86_xmm2 (void){return make_reg (XMM2);} op_t x86_xmm3 (void){return make_reg (XMM3);} op_t x86_xmm4 (void){return make_reg (XMM4);} op_t x86_xmm5 (void){return make_reg (XMM5);} op_t x86_xmm6 (void){return make_reg (XMM6);} op_t x86_xmm7 (void){return make_reg (XMM7);}