/* * jit-rules-x86.c - Rules that define the characteristics of the x86. * * Copyright (C) 2004 Southern Storm Software, Pty Ltd. * * This file is part of the libjit library. * * The libjit 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.1 of * the License, or (at your option) any later version. * * The libjit 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 the libjit library. If not, see * . */ #include "jit-internal.h" #include "jit-rules.h" #include "jit-apply-rules.h" #if defined(JIT_BACKEND_X86) #include "jit-gen-x86.h" #include "jit-reg-alloc.h" #include "jit-setjmp.h" #include /* * Pseudo register numbers for the x86 registers. These are not the * same as the CPU instruction register numbers. The order of these * values must match the order in "JIT_REG_INFO". */ #define X86_REG_EAX 0 #define X86_REG_ECX 1 #define X86_REG_EDX 2 #define X86_REG_EBX 3 #define X86_REG_ESI 4 #define X86_REG_EDI 5 #define X86_REG_EBP 6 #define X86_REG_ESP 7 #define X86_REG_ST0 8 #define X86_REG_ST1 9 #define X86_REG_ST2 10 #define X86_REG_ST3 11 #define X86_REG_ST4 12 #define X86_REG_ST5 13 #define X86_REG_ST6 14 #define X86_REG_ST7 15 /* * Determine if a pseudo register number is word-based or float-based. */ #define IS_WORD_REG(reg) ((reg) < X86_REG_ST0) #define IS_FLOAT_REG(reg) ((reg)>= X86_REG_ST0) /* * Round a size up to a multiple of the stack word size. */ #define ROUND_STACK(size) \ (((size) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1)) static _jit_regclass_t *x86_reg; static _jit_regclass_t *x86_breg; static _jit_regclass_t *x86_freg; static _jit_regclass_t *x86_lreg; void _jit_init_backend(void) { x86_reg = _jit_regclass_create( "reg", JIT_REG_WORD, 6, X86_REG_EAX, X86_REG_ECX, X86_REG_EDX, X86_REG_EBX, X86_REG_ESI, X86_REG_EDI); x86_breg = _jit_regclass_create( "breg", JIT_REG_WORD, 4, X86_REG_EAX, X86_REG_ECX, X86_REG_EDX, X86_REG_EBX); x86_freg = _jit_regclass_create( "freg", JIT_REG_X86_FLOAT | JIT_REG_IN_STACK, 8, X86_REG_ST0, X86_REG_ST1, X86_REG_ST2, X86_REG_ST3, X86_REG_ST4, X86_REG_ST5, X86_REG_ST6, X86_REG_ST7); x86_lreg = _jit_regclass_create( "lreg", JIT_REG_LONG, 2, X86_REG_EAX, X86_REG_ECX); } void _jit_gen_get_elf_info(jit_elf_info_t *info) { #ifdef JIT_NATIVE_INT32 info->machine = 3; /* EM_386 */ #else info->machine = 62; /* EM_X86_64 */ #endif #if JIT_APPLY_X86_FASTCALL == 0 info->abi = 0; /* ELFOSABI_SYSV */ #else info->abi = 186; /* Private code, indicating STDCALL/FASTCALL support */ #endif info->abi_version = 0; } int _jit_setup_indirect_pointer(jit_function_t func, jit_value_t value) { return jit_insn_outgoing_reg(func, value, X86_REG_EAX); } int _jit_create_call_return_insns (jit_function_t func, jit_type_t signature, jit_value_t *args, unsigned int num_args, jit_value_t return_value, int is_nested) { jit_nint pop_bytes; unsigned int size; jit_type_t return_type; int ptr_return; /* Calculate the number of bytes that we need to pop */ return_type = jit_type_normalize(jit_type_get_return(signature)); ptr_return = jit_type_return_via_pointer(return_type); #if JIT_APPLY_X86_FASTCALL == 1 if(jit_type_get_abi(signature) == jit_abi_stdcall || jit_type_get_abi(signature) == jit_abi_fastcall) { /* STDCALL and FASTCALL functions pop their own arguments */ pop_bytes = 0; } else #endif { pop_bytes = 0; while(num_args> 0) { --num_args; size = jit_type_get_size(jit_value_get_type(args[num_args])); pop_bytes += ROUND_STACK(size); } #if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 if(ptr_return && is_nested) { /* Note: we only need this for nested functions, because regular functions will pop the structure return for us */ pop_bytes += sizeof(void *); } #else if(ptr_return) { pop_bytes += sizeof(void *); } #endif if(is_nested) { pop_bytes += sizeof(void *); } } /* Pop the bytes from the system stack */ if(pop_bytes> 0) { if(!jit_insn_defer_pop_stack(func, pop_bytes)) { return 0; } } /* Bail out now if we don't need to worry about return values */ if(!return_value || ptr_return) { return 1; } /* Structure values must be flushed into the frame, and everything else ends up in a register */ if(jit_type_is_struct(return_type) || jit_type_is_union(return_type)) { if(!jit_insn_flush_struct(func, return_value)) { return 0; } } else if(return_type == jit_type_float32 || return_type == jit_type_float64 || return_type == jit_type_nfloat) { if(!jit_insn_return_reg(func, return_value, X86_REG_ST0)) { return 0; } } else if(return_type->kind != JIT_TYPE_VOID) { if(!jit_insn_return_reg(func, return_value, X86_REG_EAX)) { return 0; } } /* Everything is back where it needs to be */ return 1; } int _jit_opcode_is_supported(int opcode) { switch(opcode) { #define JIT_INCLUDE_SUPPORTED #include "jit-rules-x86.inc" #undef JIT_INCLUDE_SUPPORTED } return 0; } void *_jit_gen_prolog(jit_gencode_t gen, jit_function_t func, void *buf) { unsigned char prolog[JIT_PROLOG_SIZE]; unsigned char *inst = prolog; int reg; /* Push ebp onto the stack */ x86_push_reg(inst, X86_EBP); /* Initialize EBP for the current frame */ x86_mov_reg_reg(inst, X86_EBP, X86_ESP, sizeof(void *)); /* Allocate space for the local variable frame */ if(func->builder->frame_size> 0) { x86_alu_reg_imm(inst, X86_SUB, X86_ESP, (int)(func->builder->frame_size)); } /* Save registers that we need to preserve */ for(reg = 0; reg <= 7; ++reg) { if(jit_reg_is_used(gen->touched, reg) && (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) { x86_push_reg(inst, _jit_reg_info[reg].cpu_reg); } } /* Copy the prolog into place and return the adjusted entry position */ reg = (int)(inst - prolog); jit_memcpy(((unsigned char *)buf) + JIT_PROLOG_SIZE - reg, prolog, reg); return (void *)(((unsigned char *)buf) + JIT_PROLOG_SIZE - reg); } void _jit_gen_epilog(jit_gencode_t gen, jit_function_t func) { jit_nint pop_bytes = 0; int reg, offset; unsigned char *inst; int struct_return_offset = 0; void **fixup; void **next; /* Check if there is sufficient space for the epilog */ _jit_gen_check_space(gen, 48); #if JIT_APPLY_X86_FASTCALL == 1 /* Determine the number of parameter bytes to pop when we return */ { jit_type_t signature; unsigned int num_params; unsigned int param; signature = func->signature; if(jit_type_get_abi(signature) == jit_abi_stdcall || jit_type_get_abi(signature) == jit_abi_fastcall) { if(func->nested_parent) { pop_bytes += sizeof(void *); } if(jit_type_return_via_pointer(jit_type_get_return(signature))) { struct_return_offset = 2 * sizeof(void *) + pop_bytes; pop_bytes += sizeof(void *); } num_params = jit_type_num_params(signature); for(param = 0; param < num_params; ++param) { pop_bytes += ROUND_STACK (jit_type_get_size (jit_type_get_param(signature, param))); } if(jit_type_get_abi(signature) == jit_abi_fastcall) { /* The first two words are in fastcall registers */ if(pop_bytes> (2 * sizeof(void *))) { pop_bytes -= 2 * sizeof(void *); } else { pop_bytes = 0; } struct_return_offset = 0; } } else if(!(func->nested_parent) && jit_type_return_via_pointer(jit_type_get_return(signature))) { #if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 pop_bytes += sizeof(void *); #endif struct_return_offset = 2 * sizeof(void *); } } #else { /* We only need to pop structure pointers in non-nested functions */ jit_type_t signature; signature = func->signature; if(!(func->nested_parent) && jit_type_return_via_pointer(jit_type_get_return(signature))) { #if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 pop_bytes += sizeof(void *); #endif struct_return_offset = 2 * sizeof(void *); } } #endif /* Perform fixups on any blocks that jump to the epilog */ inst = gen->ptr; fixup = (void **)(gen->epilog_fixup); while(fixup != 0) { next = (void **)(fixup[0]); fixup[0] = (void *)(((jit_nint)inst) - ((jit_nint)fixup) - 4); fixup = next; } gen->epilog_fixup = 0; /* If we are returning a structure via a pointer, then copy the pointer value into EAX when we return */ if(struct_return_offset != 0) { x86_mov_reg_membase(inst, X86_EAX, X86_EBP, struct_return_offset, 4); } /* Restore the callee save registers that we used */ if(gen->stack_changed) { offset = -(func->builder->frame_size); for(reg = 0; reg <= 7; ++reg) { if(jit_reg_is_used(gen->touched, reg) && (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) { offset -= sizeof(void *); x86_mov_reg_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, sizeof(void *)); } } } else { for(reg = 7; reg>= 0; --reg) { if(jit_reg_is_used(gen->touched, reg) && (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) { x86_pop_reg(inst, _jit_reg_info[reg].cpu_reg); } } } /* Pop the stack frame and restore the saved copy of ebp */ if(gen->stack_changed || func->builder->frame_size> 0) { x86_mov_reg_reg(inst, X86_ESP, X86_EBP, sizeof(void *)); } x86_pop_reg(inst, X86_EBP); /* Return from the current function */ if(pop_bytes> 0) { x86_ret_imm(inst, pop_bytes); } else { x86_ret(inst); } gen->ptr = inst; } #if 0 /* * The x86 backend does not need this function because it uses * _jit_create_indirector() instead. */ void *_jit_gen_redirector(jit_gencode_t gen, jit_function_t func) { void *ptr, *entry; _jit_gen_check_space(gen, 8); ptr = (void *)&(func->entry_point); entry = gen->ptr; x86_jump_mem(gen->ptr, ptr); return entry; } #endif /* * Setup or teardown the x86 code output process. */ #define jit_cache_setup_output(needed) \ unsigned char *inst = gen->ptr; \ _jit_gen_check_space(gen, (needed)) #define jit_cache_end_output() \ gen->ptr = inst /* * Get a temporary register that isn't one of the specified registers. */ static int get_temp_reg(int reg1, int reg2, int reg3) { if(reg1 != X86_EAX && reg2 != X86_EAX && reg3 != X86_EAX) { return X86_EAX; } if(reg1 != X86_EDX && reg2 != X86_EDX && reg3 != X86_EDX) { return X86_EDX; } if(reg1 != X86_ECX && reg2 != X86_ECX && reg3 != X86_ECX) { return X86_ECX; } if(reg1 != X86_EBX && reg2 != X86_EBX && reg3 != X86_EBX) { return X86_EBX; } if(reg1 != X86_ESI && reg2 != X86_ESI && reg3 != X86_ESI) { return X86_ESI; } return X86_EDI; } /* * Store a byte value to a membase address. */ static unsigned char *mov_membase_reg_byte (unsigned char *inst, int basereg, int offset, int srcreg) { if(srcreg == X86_EAX || srcreg == X86_EBX || srcreg == X86_ECX || srcreg == X86_EDX) { x86_mov_membase_reg(inst, basereg, offset, srcreg, 1); } else if(basereg != X86_EAX) { x86_push_reg(inst, X86_EAX); x86_mov_reg_reg(inst, X86_EAX, srcreg, 4); x86_mov_membase_reg(inst, basereg, offset, X86_EAX, 1); x86_pop_reg(inst, X86_EAX); } else { x86_push_reg(inst, X86_EDX); x86_mov_reg_reg(inst, X86_EDX, srcreg, 4); x86_mov_membase_reg(inst, basereg, offset, X86_EDX, 1); x86_pop_reg(inst, X86_EDX); } return inst; } /* * Store a small structure from registers to a pointer. The base * register must not be either "reg" or "other_reg". */ static unsigned char *store_small_struct (unsigned char *inst, int reg, int other_reg, int base_reg, jit_nint offset, jit_nint size, int preserve) { switch(size) { case 1: { inst = mov_membase_reg_byte(inst, base_reg, offset, reg); } break; case 2: { x86_mov_membase_reg(inst, base_reg, offset, reg, 2); } break; case 3: { if(preserve) { x86_push_reg(inst, reg); } x86_mov_membase_reg(inst, base_reg, offset, reg, 2); x86_shift_reg_imm(inst, reg, X86_SHR, 16); inst = mov_membase_reg_byte(inst, base_reg, offset + 2, reg); if(preserve) { x86_pop_reg(inst, reg); } } break; case 4: { x86_mov_membase_reg(inst, base_reg, offset, reg, 4); } break; case 5: { x86_mov_membase_reg(inst, base_reg, offset, reg, 4); inst = mov_membase_reg_byte(inst, base_reg, offset + 4, other_reg); } break; case 6: { x86_mov_membase_reg(inst, base_reg, offset, reg, 4); x86_mov_membase_reg(inst, base_reg, offset + 4, other_reg, 2); } break; case 7: { if(preserve) { x86_push_reg(inst, other_reg); } x86_mov_membase_reg(inst, base_reg, offset, reg, 4); x86_mov_membase_reg(inst, base_reg, offset + 4, other_reg, 2); x86_shift_reg_imm(inst, other_reg, X86_SHR, 16); inst = mov_membase_reg_byte(inst, base_reg, offset + 6, other_reg); if(preserve) { x86_pop_reg(inst, other_reg); } } break; case 8: { x86_mov_membase_reg(inst, base_reg, offset, reg, 4); x86_mov_membase_reg(inst, base_reg, offset + 4, other_reg, 4); } break; } return inst; } void _jit_gen_spill_reg(jit_gencode_t gen, int reg, int other_reg, jit_value_t value) { int offset; /* Make sure that we have sufficient space */ jit_cache_setup_output(16); /* If the value is associated with a global register, then copy to that */ if(value->has_global_register) { reg = _jit_reg_info[reg].cpu_reg; other_reg = _jit_reg_info[value->global_reg].cpu_reg; x86_mov_reg_reg(inst, other_reg, reg, sizeof(void *)); jit_cache_end_output(); return; } /* Fix the value in place within the local variable frame */ _jit_gen_fix_value(value); /* Output an appropriate instruction to spill the value */ offset = (int)(value->frame_offset); if(IS_WORD_REG(reg)) { /* Spill a word register. If the value is smaller than a word, then we write the entire word. The local stack frame is allocated such that the extra bytes will be simply ignored */ reg = _jit_reg_info[reg].cpu_reg; x86_mov_membase_reg(inst, X86_EBP, offset, reg, 4); if(other_reg != -1) { /* Spill the other word register in a pair */ reg = _jit_reg_info[other_reg].cpu_reg; offset += sizeof(void *); x86_mov_membase_reg(inst, X86_EBP, offset, reg, 4); } } else { /* Spill the top of the floating-point register stack */ switch(jit_type_normalize(value->type)->kind) { case JIT_TYPE_FLOAT32: { x86_fst_membase(inst, X86_EBP, offset, 0, 1); } break; case JIT_TYPE_FLOAT64: { x86_fst_membase(inst, X86_EBP, offset, 1, 1); } break; case JIT_TYPE_NFLOAT: { x86_fst80_membase(inst, X86_EBP, offset); } break; } } /* End the code output process */ jit_cache_end_output(); } void _jit_gen_free_reg(jit_gencode_t gen, int reg, int other_reg, int value_used) { /* We only need to take explicit action if we are freeing a floating-point register whose value hasn't been used yet */ if(!value_used && IS_FLOAT_REG(reg)) { _jit_gen_check_space(gen, 2); x86_fstp(gen->ptr, reg - X86_REG_ST0); } } static int fp_stack_index(jit_gencode_t gen, int reg) { return gen->reg_stack_top - reg - 1; } void _jit_gen_exch_top(jit_gencode_t gen, int reg) { if(IS_FLOAT_REG(reg)) { jit_cache_setup_output(2); x86_fxch(inst, fp_stack_index(gen, reg)); jit_cache_end_output(); } } void _jit_gen_move_top(jit_gencode_t gen, int reg) { if(IS_FLOAT_REG(reg)) { jit_cache_setup_output(2); x86_fstp(inst, fp_stack_index(gen, reg)); jit_cache_end_output(); } } void _jit_gen_spill_top(jit_gencode_t gen, int reg, jit_value_t value, int pop) { int offset; if(IS_FLOAT_REG(reg)) { /* Make sure that we have sufficient space */ jit_cache_setup_output(16); /* Fix the value in place within the local variable frame */ _jit_gen_fix_value(value); /* Output an appropriate instruction to spill the value */ offset = (int)(value->frame_offset); /* Spill the top of the floating-point register stack */ switch(jit_type_normalize(value->type)->kind) { case JIT_TYPE_FLOAT32: { x86_fst_membase(inst, X86_EBP, offset, 0, pop); } break; case JIT_TYPE_FLOAT64: { x86_fst_membase(inst, X86_EBP, offset, 1, pop); } break; case JIT_TYPE_NFLOAT: { x86_fst80_membase(inst, X86_EBP, offset); if(!pop) { x86_fld80_membase(inst, X86_EBP, offset); } } break; } /* End the code output process */ jit_cache_end_output(); } } void _jit_gen_load_value(jit_gencode_t gen, int reg, int other_reg, jit_value_t value) { jit_type_t type; int src_reg, other_src_reg; void *ptr; int offset; /* Make sure that we have sufficient space */ jit_cache_setup_output(16); type = jit_type_normalize(value->type); /* Load zero */ if(value->is_constant) { switch(type->kind) { case JIT_TYPE_SBYTE: case JIT_TYPE_UBYTE: case JIT_TYPE_SHORT: case JIT_TYPE_USHORT: case JIT_TYPE_INT: case JIT_TYPE_UINT: { if((jit_nint)(value->address) == 0) { x86_clear_reg(inst, _jit_reg_info[reg].cpu_reg); } else { x86_mov_reg_imm(inst, _jit_reg_info[reg].cpu_reg, (jit_nint)(value->address)); } } break; case JIT_TYPE_LONG: case JIT_TYPE_ULONG: { jit_long long_value; long_value = jit_value_get_long_constant(value); if(long_value == 0) { #ifdef JIT_NATIVE_INT64 x86_clear_reg(inst, _jit_reg_info[reg].cpu_reg); #else x86_clear_reg(inst, _jit_reg_info[reg].cpu_reg); x86_clear_reg(inst, _jit_reg_info[other_reg].cpu_reg); #endif } else { #ifdef JIT_NATIVE_INT64 x86_mov_reg_imm(inst, _jit_reg_info[reg].cpu_reg, (jit_nint)long_value); #else x86_mov_reg_imm(inst, _jit_reg_info[reg].cpu_reg, (jit_int)long_value); x86_mov_reg_imm(inst, _jit_reg_info[other_reg].cpu_reg, (jit_int)(long_value>> 32)); #endif } } break; case JIT_TYPE_FLOAT32: { jit_float32 float32_value; float32_value = jit_value_get_float32_constant(value); if(IS_WORD_REG(reg)) { union { jit_float32 float32_value; jit_int int_value; } un; un.float32_value = float32_value; x86_mov_reg_imm(inst, _jit_reg_info[reg].cpu_reg, un.int_value); } else { if(float32_value == (jit_float32) 0.0) { x86_fldz(inst); } else if(float32_value == (jit_float32) 1.0) { x86_fld1(inst); } else { ptr = _jit_gen_alloc(gen, sizeof(jit_float32)); jit_memcpy(ptr, &float32_value, sizeof(float32_value)); x86_fld(inst, ptr, 0); } } } break; case JIT_TYPE_FLOAT64: { jit_float64 float64_value; float64_value = jit_value_get_float64_constant(value); if(IS_WORD_REG(reg)) { union { jit_float64 float64_value; jit_int int_value[2]; } un; un.float64_value = float64_value; x86_mov_reg_imm(inst, _jit_reg_info[reg].cpu_reg, un.int_value[0]); x86_mov_reg_imm(inst, _jit_reg_info[other_reg].cpu_reg, un.int_value[1]); } else { if(float64_value == (jit_float64) 0.0) { x86_fldz(inst); } else if(float64_value == (jit_float64) 1.0) { x86_fld1(inst); } else { ptr = _jit_gen_alloc(gen, sizeof(jit_float64)); jit_memcpy(ptr, &float64_value, sizeof(float64_value)); x86_fld(inst, ptr, 1); } } } break; case JIT_TYPE_NFLOAT: { jit_nfloat nfloat_value; nfloat_value = jit_value_get_nfloat_constant(value); if(IS_WORD_REG(reg) && sizeof(jit_nfloat) == sizeof(jit_float64)) { union { jit_nfloat nfloat_value; jit_int int_value[2]; } un; un.nfloat_value = nfloat_value; x86_mov_reg_imm(inst, _jit_reg_info[reg].cpu_reg, un.int_value[0]); x86_mov_reg_imm(inst, _jit_reg_info[other_reg].cpu_reg, un.int_value[1]); } else { if(nfloat_value == (jit_nfloat) 0.0) { x86_fldz(inst); } else if(nfloat_value == (jit_nfloat) 1.0) { x86_fld1(inst); } else { ptr = _jit_gen_alloc(gen, sizeof(jit_nfloat)); jit_memcpy(ptr, &nfloat_value, sizeof(nfloat_value)); if(sizeof(jit_nfloat) == sizeof(jit_float64)) { x86_fld(inst, ptr, 1); } else { x86_fld80_mem(inst, ptr); } } } } break; } } else if(value->in_register || value->in_global_register) { if(value->in_register) { src_reg = value->reg; if(other_reg>= 0) { other_src_reg = jit_reg_other_reg(src_reg); } else { other_src_reg = -1; } } else { src_reg = value->global_reg; other_src_reg = -1; } switch(type->kind) { #if 0 case JIT_TYPE_SBYTE: { x86_widen_reg(inst, _jit_reg_info[reg].cpu_reg, _jit_reg_info[src_reg].cpu_reg, 1, 0); } break; case JIT_TYPE_UBYTE: { x86_widen_reg(inst, _jit_reg_info[reg].cpu_reg, _jit_reg_info[src_reg].cpu_reg, 0, 0); } break; case JIT_TYPE_SHORT: { x86_widen_reg(inst, _jit_reg_info[reg].cpu_reg, _jit_reg_info[src_reg].cpu_reg, 1, 1); } break; case JIT_TYPE_USHORT: { x86_widen_reg(inst, _jit_reg_info[reg].cpu_reg, _jit_reg_info[src_reg].cpu_reg, 0, 1); } break; #else case JIT_TYPE_SBYTE: case JIT_TYPE_UBYTE: case JIT_TYPE_SHORT: case JIT_TYPE_USHORT: #endif case JIT_TYPE_INT: case JIT_TYPE_UINT: { x86_mov_reg_reg(inst, _jit_reg_info[reg].cpu_reg, _jit_reg_info[src_reg].cpu_reg, 4); } break; case JIT_TYPE_LONG: case JIT_TYPE_ULONG: { #ifdef JIT_NATIVE_INT64 x86_mov_reg_reg(inst, _jit_reg_info[reg].cpu_reg, _jit_reg_info[src_reg].cpu_reg, 8); #else x86_mov_reg_reg(inst, _jit_reg_info[reg].cpu_reg, _jit_reg_info[src_reg].cpu_reg, 4); x86_mov_reg_reg(inst, _jit_reg_info[other_reg].cpu_reg, _jit_reg_info[other_src_reg].cpu_reg, 4); #endif } break; case JIT_TYPE_FLOAT32: case JIT_TYPE_FLOAT64: case JIT_TYPE_NFLOAT: { if(!IS_WORD_REG(reg)) { x86_fld_reg(inst, fp_stack_index(gen, src_reg)); } } break; } } else { /* Fix the position of the value in the stack frame */ _jit_gen_fix_value(value); offset = (int)(value->frame_offset); /* Load the value into the specified register */ switch(type->kind) { case JIT_TYPE_SBYTE: { x86_widen_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 1, 0); } break; case JIT_TYPE_UBYTE: { x86_widen_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 0, 0); } break; case JIT_TYPE_SHORT: { x86_widen_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 1, 1); } break; case JIT_TYPE_USHORT: { x86_widen_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 0, 1); } break; case JIT_TYPE_INT: case JIT_TYPE_UINT: { x86_mov_reg_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 4); } break; case JIT_TYPE_LONG: case JIT_TYPE_ULONG: { #ifdef JIT_NATIVE_INT64 x86_mov_reg_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 8); #else x86_mov_reg_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 4); x86_mov_reg_membase(inst, _jit_reg_info[other_reg].cpu_reg, X86_EBP, offset + 4, 4); #endif } break; case JIT_TYPE_FLOAT32: { if(IS_WORD_REG(reg)) { x86_mov_reg_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 4); } else { x86_fld_membase(inst, X86_EBP, offset, 0); } } break; case JIT_TYPE_FLOAT64: { if(IS_WORD_REG(reg)) { x86_mov_reg_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 4); x86_mov_reg_membase(inst, _jit_reg_info[other_reg].cpu_reg, X86_EBP, offset + 4, 4); } else { x86_fld_membase(inst, X86_EBP, offset, 1); } } break; case JIT_TYPE_NFLOAT: { if(IS_WORD_REG(reg) && sizeof(jit_nfloat) == sizeof(jit_float64)) { x86_mov_reg_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, 4); x86_mov_reg_membase(inst, _jit_reg_info[other_reg].cpu_reg, X86_EBP, offset + 4, 4); } else if(sizeof(jit_nfloat) == sizeof(jit_float64)) { x86_fld_membase(inst, X86_EBP, offset, 1); } else { x86_fld80_membase(inst, X86_EBP, offset); } } break; } } /* End the code output process */ jit_cache_end_output(); } void _jit_gen_spill_global(jit_gencode_t gen, int reg, jit_value_t value) { jit_cache_setup_output(16); if(value) { _jit_gen_fix_value(value); x86_mov_membase_reg(inst, X86_EBP, value->frame_offset, _jit_reg_info[value->global_reg].cpu_reg, sizeof(void *)); } else { x86_push_reg(inst, _jit_reg_info[reg].cpu_reg); } jit_cache_end_output(); } void _jit_gen_load_global(jit_gencode_t gen, int reg, jit_value_t value) { jit_cache_setup_output(16); if(value) { x86_mov_reg_membase(inst, _jit_reg_info[value->global_reg].cpu_reg, X86_EBP, value->frame_offset, sizeof(void *)); } else { x86_pop_reg(inst, _jit_reg_info[reg].cpu_reg); } jit_cache_end_output(); } void _jit_gen_fix_value(jit_value_t value) { if(!(value->has_frame_offset) && !(value->is_constant)) { jit_nint size = (jit_nint)(ROUND_STACK(jit_type_get_size(value->type))); value->block->func->builder->frame_size += size; value->frame_offset = -(value->block->func->builder->frame_size); value->has_frame_offset = 1; } } /* * Set a register value based on a condition code. */ static unsigned char *setcc_reg (unsigned char *inst, int reg, int cond, int is_signed) { if(reg == X86_EAX || reg == X86_EBX || reg == X86_ECX || reg == X86_EDX) { /* Use a SETcc instruction if we have a basic register */ x86_set_reg(inst, cond, reg, is_signed); x86_widen_reg(inst, reg, reg, 0, 0); } else { /* The register is not useable as an 8-bit destination */ unsigned char *patch1, *patch2; patch1 = inst; x86_branch8(inst, cond, 0, is_signed); x86_clear_reg(inst, reg); patch2 = inst; x86_jump8(inst, 0); x86_patch(patch1, inst); x86_mov_reg_imm(inst, reg, 1); x86_patch(patch2, inst); } return inst; } /* * Get the long form of a branch opcode. */ static int long_form_branch(int opcode) { if(opcode == 0xEB) { return 0xE9; } else { return opcode + 0x0F10; } } /* * Output a branch instruction. */ static unsigned char *output_branch (jit_function_t func, unsigned char *inst, int opcode, jit_insn_t insn) { jit_block_t block; int offset; if((insn->flags & JIT_INSN_VALUE1_IS_LABEL) != 0) { /* "address_of_label" instruction */ block = jit_block_from_label(func, (jit_label_t)(insn->value1)); } else { block = jit_block_from_label(func, (jit_label_t)(insn->dest)); } if(!block) { return inst; } if(block->address) { /* We already know the address of the block */ offset = ((unsigned char *)(block->address)) - (inst + 2); if(x86_is_imm8(offset)) { /* We can output a short-form backwards branch */ *inst++ = (unsigned char)opcode; *inst++ = (unsigned char)offset; } else { /* We need to output a long-form backwards branch */ offset -= 3; opcode = long_form_branch(opcode); if(opcode < 256) { *inst++ = (unsigned char)opcode; } else { *inst++ = (unsigned char)(opcode>> 8); *inst++ = (unsigned char)opcode; --offset; } x86_imm_emit32(inst, offset); } } else { /* Output a placeholder and record on the block's fixup list */ opcode = long_form_branch(opcode); if(opcode < 256) { *inst++ = (unsigned char)opcode; } else { *inst++ = (unsigned char)(opcode>> 8); *inst++ = (unsigned char)opcode; } x86_imm_emit32(inst, (int)(block->fixup_list)); block->fixup_list = (void *)(inst - 4); } return inst; } /* * Jump to the current function's epilog. */ static unsigned char * jump_to_epilog(jit_gencode_t gen, unsigned char *inst, jit_block_t block) { /* If the epilog is the next thing that we will output, then fall through to the epilog directly */ if(_jit_block_is_final(block)) { return inst; } /* Output a placeholder for the jump and add it to the fixup list */ *inst++ = (unsigned char)0xE9; x86_imm_emit32(inst, (int)(gen->epilog_fixup)); gen->epilog_fixup = (void *)(inst - 4); return inst; } /* * Throw a builtin exception. */ static unsigned char * throw_builtin(unsigned char *inst, jit_function_t func, int type) { /* We need to update "catch_pc" if we have a "try" block */ if(func->builder->setjmp_value != 0) { _jit_gen_fix_value(func->builder->setjmp_value); if(func->builder->position_independent) { x86_call_imm(inst, 0); x86_pop_membase(inst, X86_EBP, func->builder->setjmp_value->frame_offset + jit_jmp_catch_pc_offset); } else { int pc = (int) inst; x86_mov_membase_imm(inst, X86_EBP, func->builder->setjmp_value->frame_offset + jit_jmp_catch_pc_offset, pc, 4); } } /* Push the exception type onto the stack */ x86_push_imm(inst, type); /* Call the "jit_exception_builtin" function, which will never return */ x86_call_code(inst, jit_exception_builtin); return inst; } /* * Copy a block of memory that has a specific size. Other than * the parameter pointers, all registers must be unused at this point. */ static unsigned char *memory_copy (jit_gencode_t gen, unsigned char *inst, int dreg, jit_nint doffset, int sreg, jit_nint soffset, jit_nuint size) { int temp_reg = get_temp_reg(dreg, sreg, -1); if(size <= 4 * sizeof(void *)) { /* Use direct copies to copy the memory */ int offset = 0; while(size>= sizeof(void *)) { x86_mov_reg_membase(inst, temp_reg, sreg, soffset + offset, sizeof(void *)); x86_mov_membase_reg(inst, dreg, doffset + offset, temp_reg, sizeof(void *)); size -= sizeof(void *); offset += sizeof(void *); } #ifdef JIT_NATIVE_INT64 if(size>= 4) { x86_mov_reg_membase(inst, temp_reg, sreg, soffset + offset, 4); x86_mov_membase_reg(inst, dreg, doffset + offset, temp_reg, 4); size -= 4; offset += 4; } #endif if(size>= 2) { x86_mov_reg_membase(inst, temp_reg, sreg, soffset + offset, 2); x86_mov_membase_reg(inst, dreg, doffset + offset, temp_reg, 2); size -= 2; offset += 2; } if(size>= 1) { /* We assume that temp_reg is EAX, ECX, or EDX, which it should be after calling "get_temp_reg" */ x86_mov_reg_membase(inst, temp_reg, sreg, soffset + offset, 1); x86_mov_membase_reg(inst, dreg, doffset + offset, temp_reg, 1); } } else { /* Call out to "jit_memcpy" to effect the copy */ x86_push_imm(inst, size); if(soffset == 0) { x86_push_reg(inst, sreg); } else { x86_lea_membase(inst, temp_reg, sreg, soffset); x86_push_reg(inst, temp_reg); } if(dreg != X86_ESP) { if(doffset == 0) { x86_push_reg(inst, dreg); } else { x86_lea_membase(inst, temp_reg, dreg, doffset); x86_push_reg(inst, temp_reg); } } else { /* Copying a structure value onto the stack */ x86_lea_membase(inst, temp_reg, X86_ESP, doffset + 2 * sizeof(void *)); x86_push_reg(inst, temp_reg); } x86_call_code(inst, jit_memcpy); x86_alu_reg_imm(inst, X86_ADD, X86_ESP, 3 * sizeof(void *)); } return inst; } #define TODO() \ do { \ fprintf(stderr, "TODO at %s, %d\n", __FILE__, (int)__LINE__); \ } while (0) void _jit_gen_insn(jit_gencode_t gen, jit_function_t func, jit_block_t block, jit_insn_t insn) { switch(insn->opcode) { #define JIT_INCLUDE_RULES #include "jit-rules-x86.inc" #undef JIT_INCLUDE_RULES default: { fprintf(stderr, "TODO(%x) at %s, %d\n", (int)(insn->opcode), __FILE__, (int)__LINE__); } break; } } void _jit_gen_start_block(jit_gencode_t gen, jit_block_t block) { void **fixup; void **next; /* Set the address of this block */ block->address = (void *)(gen->ptr); /* If this block has pending fixups, then apply them now */ fixup = (void **)(block->fixup_list); while(fixup != 0) { next = (void **)(fixup[0]); fixup[0] = (void *) (((jit_nint)(block->address)) - ((jit_nint)fixup) - 4); fixup = next; } block->fixup_list = 0; fixup = (void**)(block->fixup_absolute_list); while(fixup != 0) { next = (void **)(fixup[0]); fixup[0] = (void *)((jit_nint)(block->address)); fixup = next; } block->fixup_absolute_list = 0; } void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block) { /* Nothing to do here for x86 */ } int _jit_gen_is_global_candidate(jit_type_t type) { switch(jit_type_remove_tags(type)->kind) { case JIT_TYPE_INT: case JIT_TYPE_UINT: case JIT_TYPE_NINT: case JIT_TYPE_NUINT: case JIT_TYPE_PTR: case JIT_TYPE_SIGNATURE: return 1; } return 0; } int _jit_reg_get_pair(jit_type_t type, int reg) { type = jit_type_normalize(type); if(type) { if(type->kind == JIT_TYPE_LONG || type->kind == JIT_TYPE_ULONG) { return jit_reg_other_reg(reg); } } return -1; } #endif /* JIT_BACKEND_X86 */

AltStyle によって変換されたページ (->オリジナル) /