mirror of https://github.com/ademakov/libjit
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1618 lines
35 KiB
1618 lines
35 KiB
/*
|
|
* jit-rules-x86.c - Rules that define the characteristics of the x86.
|
|
*
|
|
* Copyright (C) 2004 Southern Storm Software, Pty Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "jit-internal.h"
|
|
#include "jit-rules.h"
|
|
#include "jit-apply-rules.h"
|
|
|
|
#if defined(JIT_BACKEND_X86)
|
|
|
|
#include "jit-gen-x86.h"
|
|
|
|
/*
|
|
* 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 X85_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
|
|
|
|
/*
|
|
* Round a size up to a multiple of the stack word size.
|
|
*/
|
|
#define ROUND_STACK(size) \
|
|
(((size) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
|
|
|
|
void _jit_init_backend(void)
|
|
{
|
|
/* Nothing to do here for the x86 */
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Force values out of fastcall registers that cannot be easily
|
|
* accessed in register form (i.e. long, float, and struct values).
|
|
*/
|
|
static int force_out_of_regs(jit_function_t func, jit_value_t param,
|
|
int num_regs, unsigned int num_stack_words)
|
|
{
|
|
jit_value_t address;
|
|
jit_value_t temp;
|
|
jit_nint offset = 0;
|
|
jit_nint frame_offset = 2 * sizeof(void *);
|
|
|
|
/* Get the address of the parameter, to force it into the frame,
|
|
and to set up for the later "jit_insn_store_relative" calls */
|
|
address = jit_insn_address_of(func, param);
|
|
if(!address)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Force the values out of the registers */
|
|
while(num_regs > 0)
|
|
{
|
|
temp = jit_value_create(func, jit_type_void_ptr);
|
|
if(!temp)
|
|
{
|
|
return 0;
|
|
}
|
|
if(num_regs == 2)
|
|
{
|
|
if(!jit_insn_incoming_reg(func, temp, X86_REG_ECX))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!jit_insn_incoming_reg(func, temp, X86_REG_EDX))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
if(!jit_insn_store_relative(func, address, offset, temp))
|
|
{
|
|
return 0;
|
|
}
|
|
offset += sizeof(void *);
|
|
--num_regs;
|
|
}
|
|
|
|
|
|
/* Force the rest of the value out of the incoming stack frame */
|
|
while(num_stack_words > 0)
|
|
{
|
|
temp = jit_value_create(func, jit_type_void_ptr);
|
|
if(!temp)
|
|
{
|
|
return 0;
|
|
}
|
|
if(!jit_insn_incoming_frame_posn(func, temp, frame_offset))
|
|
{
|
|
return 0;
|
|
}
|
|
if(!jit_insn_store_relative(func, address, offset, temp))
|
|
{
|
|
return 0;
|
|
}
|
|
offset += sizeof(void *);
|
|
frame_offset += sizeof(void *);
|
|
--num_stack_words;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int _jit_create_entry_insns(jit_function_t func)
|
|
{
|
|
jit_type_t signature = func->signature;
|
|
jit_type_t type;
|
|
int num_regs;
|
|
jit_nint offset;
|
|
jit_value_t value;
|
|
unsigned int num_params;
|
|
unsigned int param;
|
|
unsigned int size;
|
|
unsigned int num_stack_words;
|
|
|
|
/* Reset the frame size for this function. We start by assuming
|
|
that ESI, EDI, and EBX need to be saved in the local frame */
|
|
func->builder->frame_size = 3 * sizeof(void *);
|
|
|
|
/* Determine the number of registers to allocate to parameters */
|
|
#if JIT_APPLY_X86_FASTCALL == 1
|
|
if(jit_type_get_abi(signature) == jit_abi_fastcall)
|
|
{
|
|
num_regs = 2;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
num_regs = 0;
|
|
}
|
|
|
|
/* The starting parameter offset (saved ebp and return address on stack) */
|
|
offset = 2 * sizeof(void *);
|
|
|
|
/* If the function is nested, then we need an extra parameter
|
|
to pass the pointer to the parent's local variable frame */
|
|
if(func->nested_parent)
|
|
{
|
|
if(num_regs > 0)
|
|
{
|
|
--num_regs;
|
|
}
|
|
else
|
|
{
|
|
offset += sizeof(void *);
|
|
}
|
|
}
|
|
|
|
/* Allocate the structure return pointer */
|
|
value = jit_value_get_struct_pointer(func);
|
|
if(value)
|
|
{
|
|
if(num_regs == 2)
|
|
{
|
|
if(!jit_insn_incoming_reg(func, value, X86_REG_ECX))
|
|
{
|
|
return 0;
|
|
}
|
|
--num_regs;
|
|
}
|
|
else if(num_regs == 1)
|
|
{
|
|
if(!jit_insn_incoming_reg(func, value, X86_REG_EDX))
|
|
{
|
|
return 0;
|
|
}
|
|
--num_regs;
|
|
}
|
|
else
|
|
{
|
|
if(!jit_insn_incoming_frame_posn(func, value, offset))
|
|
{
|
|
return 0;
|
|
}
|
|
offset += sizeof(void *);
|
|
}
|
|
}
|
|
|
|
/* Allocate the parameter offsets */
|
|
num_params = jit_type_num_params(signature);
|
|
for(param = 0; param < num_params; ++param)
|
|
{
|
|
value = jit_value_get_param(func, param);
|
|
if(!value)
|
|
{
|
|
continue;
|
|
}
|
|
type = jit_type_normalize(jit_value_get_type(value));
|
|
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:
|
|
case JIT_TYPE_NINT:
|
|
case JIT_TYPE_NUINT:
|
|
case JIT_TYPE_SIGNATURE:
|
|
case JIT_TYPE_PTR:
|
|
{
|
|
if(num_regs == 2)
|
|
{
|
|
if(!jit_insn_incoming_reg(func, value, X86_REG_ECX))
|
|
{
|
|
return 0;
|
|
}
|
|
--num_regs;
|
|
}
|
|
else if(num_regs == 1)
|
|
{
|
|
if(!jit_insn_incoming_reg(func, value, X86_REG_EDX))
|
|
{
|
|
return 0;
|
|
}
|
|
--num_regs;
|
|
}
|
|
else
|
|
{
|
|
if(!jit_insn_incoming_frame_posn(func, value, offset))
|
|
{
|
|
return 0;
|
|
}
|
|
offset += sizeof(void *);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_LONG:
|
|
case JIT_TYPE_ULONG:
|
|
case JIT_TYPE_FLOAT32:
|
|
case JIT_TYPE_FLOAT64:
|
|
case JIT_TYPE_NFLOAT:
|
|
case JIT_TYPE_STRUCT:
|
|
case JIT_TYPE_UNION:
|
|
{
|
|
/* Deal with the possibility that the value may be split
|
|
between registers and the stack */
|
|
size = ROUND_STACK(jit_type_get_size(type));
|
|
if(num_regs == 2)
|
|
{
|
|
if(size <= sizeof(void *))
|
|
{
|
|
if(!force_out_of_regs(func, value, 1, 0))
|
|
{
|
|
return 0;
|
|
}
|
|
--num_regs;
|
|
}
|
|
else
|
|
{
|
|
num_stack_words = (size / sizeof(void *)) - 2;
|
|
if(!force_out_of_regs(func, value, 2, num_stack_words))
|
|
{
|
|
return 0;
|
|
}
|
|
num_regs = 0;
|
|
offset += num_stack_words * sizeof(void *);
|
|
}
|
|
}
|
|
else if(num_regs == 1)
|
|
{
|
|
num_stack_words = (size / sizeof(void *)) - 1;
|
|
if(!force_out_of_regs(func, value, 1, num_stack_words))
|
|
{
|
|
return 0;
|
|
}
|
|
num_regs = 0;
|
|
offset += num_stack_words * sizeof(void *);
|
|
}
|
|
else
|
|
{
|
|
if(!jit_insn_incoming_frame_posn(func, value, offset))
|
|
{
|
|
return 0;
|
|
}
|
|
offset += size;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int _jit_create_call_setup_insns
|
|
(jit_function_t func, jit_type_t signature,
|
|
jit_value_t *args, unsigned int num_args,
|
|
int is_nested, int nesting_level, jit_value_t *struct_return)
|
|
{
|
|
jit_type_t type = jit_type_get_return(signature);
|
|
jit_value_t value;
|
|
unsigned int size;
|
|
unsigned int index;
|
|
unsigned int num_stack_args;
|
|
unsigned int word_regs;
|
|
jit_value_t partial;
|
|
|
|
/* Determine which values are going to end up in fastcall registers */
|
|
#if JIT_APPLY_X86_FASTCALL == 1
|
|
if(jit_type_get_abi(signature) == jit_abi_fastcall)
|
|
{
|
|
word_regs = 0;
|
|
if(func->nested_parent)
|
|
{
|
|
++word_regs;
|
|
}
|
|
if(jit_type_return_via_pointer(type))
|
|
{
|
|
++word_regs;
|
|
}
|
|
index = 0;
|
|
partial = 0;
|
|
while(index < num_args && word_regs < 2)
|
|
{
|
|
size = jit_type_get_size(jit_value_get_type(args[index]));
|
|
size = ROUND_STACK(size) / sizeof(void *);
|
|
if(size <= (2 - word_regs))
|
|
{
|
|
/* This argument will fit entirely in registers */
|
|
word_regs += size;
|
|
++index;
|
|
}
|
|
else
|
|
{
|
|
/* Partly in registers and partly on the stack.
|
|
We first copy it into a buffer that we can address */
|
|
partial = jit_value_create
|
|
(func, jit_value_get_type(args[index]));
|
|
if(!partial)
|
|
{
|
|
return 0;
|
|
}
|
|
jit_value_set_addressable(partial);
|
|
if(!jit_insn_store(func, partial, args[index]))
|
|
{
|
|
return 0;
|
|
}
|
|
++index;
|
|
break;
|
|
}
|
|
}
|
|
num_stack_args = num_args - index;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
word_regs = 0;
|
|
partial = 0;
|
|
num_stack_args = num_args;
|
|
}
|
|
|
|
/* Push all of the stacked arguments in reverse order */
|
|
while(num_stack_args > 0)
|
|
{
|
|
--num_stack_args;
|
|
--num_args;
|
|
if(!jit_insn_push(func, args[num_args]))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Handle a value that is partly on the stack and partly in registers */
|
|
if(partial)
|
|
{
|
|
--num_args;
|
|
index = (2 - word_regs) * sizeof(void *);
|
|
size = ROUND_STACK(jit_type_get_size(jit_value_get_type(partial)));
|
|
while(size > index)
|
|
{
|
|
size -= sizeof(void *);
|
|
value = jit_value_create(func, jit_type_void_ptr);
|
|
if(!value)
|
|
{
|
|
return 0;
|
|
}
|
|
value = jit_insn_load_relative
|
|
(func, value, (jit_nint)size, jit_type_void_ptr);
|
|
if(!value)
|
|
{
|
|
return 0;
|
|
}
|
|
if(!jit_insn_push(func, value))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
while(size > 0)
|
|
{
|
|
size -= sizeof(void *);
|
|
value = jit_value_create(func, jit_type_void_ptr);
|
|
if(!value)
|
|
{
|
|
return 0;
|
|
}
|
|
value = jit_insn_load_relative
|
|
(func, value, (jit_nint)size, jit_type_void_ptr);
|
|
if(!value)
|
|
{
|
|
return 0;
|
|
}
|
|
if(word_regs == 2)
|
|
{
|
|
if(!jit_insn_outgoing_reg(func, value, X86_REG_EDX))
|
|
{
|
|
return 0;
|
|
}
|
|
--word_regs;
|
|
}
|
|
else
|
|
{
|
|
if(!jit_insn_outgoing_reg(func, value, X86_REG_ECX))
|
|
{
|
|
return 0;
|
|
}
|
|
--word_regs;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Push arguments that will end up entirely in registers */
|
|
while(num_args > 0)
|
|
{
|
|
--num_args;
|
|
size = jit_type_get_size(jit_value_get_type(args[num_args]));
|
|
size = ROUND_STACK(size) / sizeof(void *);
|
|
if(size == 2)
|
|
{
|
|
if(!jit_insn_outgoing_reg(func, args[num_args], X86_REG_ECX))
|
|
{
|
|
return 0;
|
|
}
|
|
word_regs = 0;
|
|
}
|
|
else if(word_regs == 2)
|
|
{
|
|
if(!jit_insn_outgoing_reg(func, args[num_args], X86_REG_EDX))
|
|
{
|
|
return 0;
|
|
}
|
|
--word_regs;
|
|
}
|
|
else
|
|
{
|
|
if(!jit_insn_outgoing_reg(func, args[num_args], X86_REG_ECX))
|
|
{
|
|
return 0;
|
|
}
|
|
--word_regs;
|
|
}
|
|
}
|
|
|
|
/* Do we need to add a structure return pointer argument? */
|
|
if(jit_type_return_via_pointer(type))
|
|
{
|
|
value = jit_value_create(func, type);
|
|
if(!value)
|
|
{
|
|
return 0;
|
|
}
|
|
*struct_return = value;
|
|
value = jit_insn_address_of(func, value);
|
|
if(!value)
|
|
{
|
|
return 0;
|
|
}
|
|
if(word_regs == 2)
|
|
{
|
|
if(!jit_insn_outgoing_reg(func, value, X86_REG_EDX))
|
|
{
|
|
return 0;
|
|
}
|
|
--word_regs;
|
|
}
|
|
else if(word_regs == 1)
|
|
{
|
|
if(!jit_insn_outgoing_reg(func, value, X86_REG_ECX))
|
|
{
|
|
return 0;
|
|
}
|
|
--word_regs;
|
|
}
|
|
else
|
|
{
|
|
if(!jit_insn_push(func, value))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*struct_return = 0;
|
|
}
|
|
|
|
/* Do we need to add nested function scope information? */
|
|
if(is_nested)
|
|
{
|
|
if(word_regs > 0)
|
|
{
|
|
if(!jit_insn_setup_for_nested(func, nesting_level, X86_REG_ECX))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!jit_insn_setup_for_nested(func, nesting_level, -1))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The call is ready to proceed */
|
|
return 1;
|
|
}
|
|
|
|
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_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 0;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
/* TODO: float and long opcodes */
|
|
|
|
/*
|
|
* Conversion opcodes.
|
|
*/
|
|
case JIT_OP_TRUNC_SBYTE:
|
|
case JIT_OP_TRUNC_UBYTE:
|
|
case JIT_OP_TRUNC_SHORT:
|
|
case JIT_OP_TRUNC_USHORT:
|
|
|
|
/*
|
|
* Arithmetic opcodes.
|
|
*/
|
|
case JIT_OP_IADD:
|
|
case JIT_OP_ISUB:
|
|
case JIT_OP_IMUL:
|
|
case JIT_OP_INEG:
|
|
|
|
/*
|
|
* Bitwise opcodes.
|
|
*/
|
|
case JIT_OP_IAND:
|
|
case JIT_OP_IOR:
|
|
case JIT_OP_IXOR:
|
|
case JIT_OP_INOT:
|
|
case JIT_OP_ISHL:
|
|
case JIT_OP_ISHR:
|
|
case JIT_OP_ISHR_UN:
|
|
|
|
/*
|
|
* Branch opcodes.
|
|
*/
|
|
case JIT_OP_BR:
|
|
case JIT_OP_BR_IFALSE:
|
|
case JIT_OP_BR_ITRUE:
|
|
case JIT_OP_BR_IEQ:
|
|
case JIT_OP_BR_INE:
|
|
case JIT_OP_BR_ILT:
|
|
case JIT_OP_BR_ILT_UN:
|
|
case JIT_OP_BR_ILE:
|
|
case JIT_OP_BR_ILE_UN:
|
|
case JIT_OP_BR_IGT:
|
|
case JIT_OP_BR_IGT_UN:
|
|
case JIT_OP_BR_IGE:
|
|
case JIT_OP_BR_IGE_UN: return 1;
|
|
}
|
|
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;
|
|
unsigned int saved;
|
|
|
|
/* 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 *));
|
|
|
|
/* Save registers that we need to preserve */
|
|
saved = 0;
|
|
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);
|
|
saved += sizeof(void *);
|
|
}
|
|
}
|
|
|
|
/* Allocate space for the local variable frame. Subtract off
|
|
the space for the registers that we just saved */
|
|
if((func->builder->frame_size - saved) > 0)
|
|
{
|
|
x86_alu_reg_imm(inst, X86_SUB, X86_ESP,
|
|
(int)(func->builder->frame_size - saved));
|
|
}
|
|
|
|
/* 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 num_regs, reg;
|
|
unsigned char *inst;
|
|
int struct_return_offset = 0;
|
|
|
|
/* Bail out if there is insufficient space for the epilog */
|
|
if(!jit_cache_check_for_n(&(gen->posn), 32))
|
|
{
|
|
jit_cache_mark_full(&(gen->posn));
|
|
return;
|
|
}
|
|
|
|
#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_abi(signature) == jit_type_stdcall ||
|
|
jit_type_abi(signature) == jit_type_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_abi(signature) == jit_type_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
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* Determine the number of callee save registers on the stack */
|
|
num_regs = 0;
|
|
for(reg = 7; reg >= 0; --reg)
|
|
{
|
|
if(jit_reg_is_used(gen->touched, reg) &&
|
|
(_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
|
|
{
|
|
++num_regs;
|
|
}
|
|
}
|
|
|
|
/* Pop the local stack frame, and get back to the callee save regs */
|
|
inst = gen->posn.ptr;
|
|
if(num_regs == 0)
|
|
{
|
|
x86_mov_reg_reg(inst, X86_ESP, X86_EBP, sizeof(void *));
|
|
}
|
|
else
|
|
{
|
|
x86_lea_membase(inst, X86_ESP, X86_EBP, -(sizeof(void *) * num_regs));
|
|
}
|
|
|
|
/* Pop the callee save registers that we used */
|
|
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 saved copy of ebp */
|
|
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->posn.ptr = inst;
|
|
}
|
|
|
|
void *_jit_gen_redirector(jit_gencode_t gen, jit_function_t func)
|
|
{
|
|
void *ptr, *entry;
|
|
if(!jit_cache_check_for_n(&(gen->posn), 8))
|
|
{
|
|
jit_cache_mark_full(&(gen->posn));
|
|
return 0;
|
|
}
|
|
ptr = (void *)&(func->entry_point);
|
|
entry = gen->posn.ptr;
|
|
x86_jump_mem(gen->posn.ptr, ptr);
|
|
return entry;
|
|
}
|
|
|
|
/*
|
|
* Setup or teardown the x86 code output process.
|
|
*/
|
|
#define jit_cache_setup_output(needed) \
|
|
unsigned char *inst = gen->posn.ptr; \
|
|
if(!jit_cache_check_for_n(&(gen->posn), (needed))) \
|
|
{ \
|
|
jit_cache_mark_full(&(gen->posn)); \
|
|
return; \
|
|
}
|
|
#define jit_cache_end_output() \
|
|
gen->posn.ptr = 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);
|
|
|
|
/* 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(reg < X86_REG_ST0)
|
|
{
|
|
/* 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 && reg >= X86_REG_ST0 && reg <= X86_REG_ST7)
|
|
{
|
|
if(jit_cache_check_for_n(&(gen->posn), 2))
|
|
{
|
|
x86_fstp(gen->posn.ptr, reg - X86_REG_ST0);
|
|
}
|
|
else
|
|
{
|
|
jit_cache_mark_full(&(gen->posn));
|
|
}
|
|
}
|
|
}
|
|
|
|
void _jit_gen_load_value
|
|
(jit_gencode_t gen, int reg, int other_reg, jit_value_t value)
|
|
{
|
|
void *ptr;
|
|
int offset;
|
|
|
|
/* Make sure that we have sufficient space */
|
|
jit_cache_setup_output(16);
|
|
|
|
if(value->is_constant)
|
|
{
|
|
/* Determine the type of constant to be loaded */
|
|
switch(jit_type_normalize(value->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:
|
|
{
|
|
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);
|
|
#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(!jit_cache_check_for_n(&(gen->posn), 32))
|
|
{
|
|
jit_cache_mark_full(&(gen->posn));
|
|
return;
|
|
}
|
|
ptr = _jit_cache_alloc(&(gen->posn), 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(!jit_cache_check_for_n(&(gen->posn), 32))
|
|
{
|
|
jit_cache_mark_full(&(gen->posn));
|
|
return;
|
|
}
|
|
ptr = _jit_cache_alloc(&(gen->posn), 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(!jit_cache_check_for_n(&(gen->posn), 32))
|
|
{
|
|
jit_cache_mark_full(&(gen->posn));
|
|
return;
|
|
}
|
|
ptr = _jit_cache_alloc(&(gen->posn), sizeof(jit_nfloat));
|
|
jit_memcpy(ptr, &nfloat_value, sizeof(nfloat_value));
|
|
x86_fld80_mem(inst, ptr);
|
|
}
|
|
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(jit_type_normalize(value->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:
|
|
{
|
|
x86_fld_membase(inst, X86_EBP, offset, 0);
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_FLOAT64:
|
|
{
|
|
x86_fld_membase(inst, X86_EBP, offset, 1);
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_NFLOAT:
|
|
{
|
|
x86_fld80_membase(inst, X86_EBP, offset);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* End the code output process */
|
|
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 up for a unary 32-bit integer operator.
|
|
*/
|
|
static int setup_for_int_unary(jit_gencode_t gen, jit_insn_t insn)
|
|
{
|
|
return _jit_regs_load_value
|
|
(gen, insn->value1, 1,
|
|
(insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
|
|
JIT_INSN_VALUE1_LIVE)));
|
|
}
|
|
|
|
/*
|
|
* Store the destination for a 32-bit integer operator.
|
|
*/
|
|
static void store_for_int(jit_gencode_t gen, jit_insn_t insn, int reg)
|
|
{
|
|
if((insn->flags & JIT_INSN_DEST_NEXT_USE) != 0)
|
|
{
|
|
/* Record that the destination is in "reg" */
|
|
_jit_regs_set_value(gen, reg, insn->dest, 0);
|
|
}
|
|
else
|
|
{
|
|
/* No next use, so store to the destination */
|
|
_jit_gen_spill_reg(gen, reg, -1, insn->dest);
|
|
insn->dest->in_frame = 1;
|
|
_jit_regs_free_reg(gen, reg, 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set up for a binary 32-bit integer operator.
|
|
*/
|
|
static int setup_for_int_binary(jit_gencode_t gen, jit_insn_t insn, int *reg2)
|
|
{
|
|
int reg;
|
|
reg = _jit_regs_load_value
|
|
(gen, insn->value1, 1,
|
|
(insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
|
|
JIT_INSN_VALUE1_LIVE)));
|
|
*reg2 = _jit_regs_load_value
|
|
(gen, insn->value2, 0,
|
|
(insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
|
|
JIT_INSN_VALUE1_LIVE)));
|
|
return reg;
|
|
}
|
|
|
|
/*
|
|
* Set up for a binary 32-bit integer operator, where reg2 may be destroyed.
|
|
*/
|
|
static int setup_for_int_binary_destroy
|
|
(jit_gencode_t gen, jit_insn_t insn, int *reg2)
|
|
{
|
|
int reg;
|
|
reg = _jit_regs_load_value
|
|
(gen, insn->value1, 1,
|
|
(insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
|
|
JIT_INSN_VALUE1_LIVE)));
|
|
*reg2 = _jit_regs_load_value
|
|
(gen, insn->value2, 1,
|
|
(insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
|
|
JIT_INSN_VALUE1_LIVE)));
|
|
return reg;
|
|
}
|
|
|
|
/*
|
|
* Widen a byte register.
|
|
*/
|
|
static unsigned char *widen_byte(unsigned char *inst, int reg, int isSigned)
|
|
{
|
|
if(reg == X86_EAX || reg == X86_EBX || reg == X86_ECX || reg == X86_EDX)
|
|
{
|
|
x86_widen_reg(inst, reg, reg, isSigned, 0);
|
|
}
|
|
else
|
|
{
|
|
x86_push_reg(inst, X86_EAX);
|
|
x86_mov_reg_reg(inst, X86_EAX, reg, 4);
|
|
x86_widen_reg(inst, reg, X86_EAX, isSigned, 0);
|
|
x86_pop_reg(inst, X86_EAX);
|
|
}
|
|
return inst;
|
|
}
|
|
|
|
/*
|
|
* Shift the contents of a register.
|
|
*/
|
|
static unsigned char *shift_reg(unsigned char *inst, int opc, int reg, int reg2)
|
|
{
|
|
if(reg2 == X86_ECX)
|
|
{
|
|
/* The shift value is already in ECX */
|
|
x86_shift_reg(inst, opc, reg1);
|
|
}
|
|
else if(reg1 == X86_ECX)
|
|
{
|
|
/* The value to be shifted is in ECX, so swap the order */
|
|
x86_xchg_reg_reg(inst, reg1, reg2, 4);
|
|
x86_shift_reg(inst, opc, reg2);
|
|
x86_mov_reg_reg(inst, reg1, reg2, 4);
|
|
}
|
|
else
|
|
{
|
|
/* Save ECX, perform the shift, and then restore ECX */
|
|
x86_push_reg(inst, X86_ECX);
|
|
x86_mov_reg_reg(inst, X86_ECX, reg2, 4);
|
|
x86_shift_reg(inst, opc, reg1);
|
|
x86_pop_reg(inst, X86_ECX);
|
|
}
|
|
return inst;
|
|
}
|
|
|
|
void _jit_gen_insn(jit_gencode_t gen, jit_function_t func,
|
|
jit_block_t block, jit_insn_t insn)
|
|
{
|
|
int reg, reg2;
|
|
switch(insn->opcode)
|
|
{
|
|
/* TODO: lots of optimizations to do here */
|
|
|
|
case JIT_OP_TRUNC_SBYTE:
|
|
{
|
|
/* Truncate a value to a signed 8-byte integer */
|
|
reg = setup_for_int_unary(gen, insn);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
inst = widen_byte(inst, _jit_reg_info[reg].cpu_reg, 1);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_TRUNC_UBYTE:
|
|
{
|
|
/* Truncate a value to an unsigned 8-byte integer */
|
|
reg = setup_for_int_unary(gen, insn);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
inst = widen_byte(inst, _jit_reg_info[reg].cpu_reg, 0);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_TRUNC_SHORT:
|
|
{
|
|
/* Truncate a value to a signed 16-byte integer */
|
|
reg = setup_for_int_unary(gen, insn);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_widen_reg(inst, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg].cpu_reg, 1, 1);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_TRUNC_USHORT:
|
|
{
|
|
/* Truncate a value to an unsigned 16-byte integer */
|
|
reg = setup_for_int_unary(gen, insn);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_widen_reg(inst, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg].cpu_reg, 0, 1);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_IADD:
|
|
{
|
|
/* Add two signed 32-bit integers */
|
|
reg = setup_for_int_binary(gen, insn, ®2);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_alu_reg_reg(inst, X86_ADD, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg2].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_ISUB:
|
|
{
|
|
/* Subtract two signed 32-bit integers */
|
|
reg = setup_for_int_binary(gen, insn, ®2);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_alu_reg_reg(inst, X86_SUB, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg2].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_IMUL:
|
|
{
|
|
/* Multiply two signed 32-bit integers. We spill first
|
|
so that the reload will hopefully end up in EAX and ECX,
|
|
and EDX will be free for us to destroy */
|
|
_jit_regs_spill_all(gen);
|
|
reg = setup_for_int_binary(gen, insn, ®2);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_mul_reg(inst, _jit_reg_info[reg2].cpu_reg, 1);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_INEG:
|
|
{
|
|
/* Negate a signed 32-bit integer */
|
|
reg = setup_for_int_unary(gen, insn);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_neg_reg(inst, _jit_reg_info[reg].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_IAND:
|
|
{
|
|
/* Bitwise AND two 32-bit integer values */
|
|
reg = setup_for_int_binary(gen, insn, ®2);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_alu_reg_reg(inst, X86_AND, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg2].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_IOR:
|
|
{
|
|
/* Bitwise OR two 32-bit integer values */
|
|
reg = setup_for_int_binary(gen, insn, ®2);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_alu_reg_reg(inst, X86_OR, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg2].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_IXOR:
|
|
{
|
|
/* Bitwise XOR two 32-bit integer values */
|
|
reg = setup_for_int_binary(gen, insn, ®2);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_alu_reg_reg(inst, X86_XOR, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg2].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_INOT:
|
|
{
|
|
/* Bitwise NOT a 32-bit integer value */
|
|
reg = setup_for_int_unary(gen, insn);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
x86_not_reg(inst, _jit_reg_info[reg].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_ISHL:
|
|
{
|
|
/* Shift a 32-bit integer value left */
|
|
reg = setup_for_int_binary_destroy(gen, insn, ®2);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
inst = shift_reg(inst, X86_SHL, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg2].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_ISHR:
|
|
{
|
|
/* Shift a 32-bit integer value right, with sign-extend */
|
|
reg = setup_for_int_binary_destroy(gen, insn, ®2);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
inst = shift_reg(inst, X86_SAR, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg2].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_ISHR_UN:
|
|
{
|
|
/* Shift a 32-bit integer value right, with zero-extend */
|
|
reg = setup_for_int_binary_destroy(gen, insn, ®2);
|
|
{
|
|
jit_cache_setup_output(16);
|
|
inst = shift_reg(inst, X86_SHR, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[reg2].cpu_reg);
|
|
jit_cache_end_output();
|
|
}
|
|
store_for_int(gen, insn, reg);
|
|
}
|
|
break;
|
|
|
|
case JIT_OP_BR:
|
|
case JIT_OP_BR_IFALSE:
|
|
case JIT_OP_BR_ITRUE:
|
|
case JIT_OP_BR_IEQ:
|
|
case JIT_OP_BR_INE:
|
|
case JIT_OP_BR_ILT:
|
|
case JIT_OP_BR_ILT_UN:
|
|
case JIT_OP_BR_ILE:
|
|
case JIT_OP_BR_ILE_UN:
|
|
case JIT_OP_BR_IGT:
|
|
case JIT_OP_BR_IGT_UN:
|
|
case JIT_OP_BR_IGE:
|
|
case JIT_OP_BR_IGE_UN:
|
|
/* TODO */
|
|
}
|
|
}
|
|
|
|
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->posn.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;
|
|
}
|
|
|
|
void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block)
|
|
{
|
|
/* Nothing to do here for x86 */
|
|
}
|
|
|
|
void _jit_gen_call_finally
|
|
(jit_gencode_t gen, jit_function_t func, jit_label_t label)
|
|
{
|
|
/* TODO */
|
|
}
|
|
|
|
void _jit_gen_unwind_stack(void *stacktop, void *catch_pc, void *object)
|
|
{
|
|
void *frame;
|
|
|
|
/* Fetch the proper EBP value from the stack, just before
|
|
where we need to unwind back to */
|
|
frame = ((void **)stacktop)[-2];
|
|
|
|
/* Unwind the stack and jump to "catch_pc" */
|
|
#if defined(__GNUC__)
|
|
__asm__ (
|
|
"movl %0, %%edx\n\t"
|
|
"movl %1, %%eax\n\t"
|
|
"movl %2, %%ecx\n\t"
|
|
"movl %3, %%ebx\n\t"
|
|
"movl %%ebx, %%ebp\n\t"
|
|
"movl %%edx, %%esp\n\t"
|
|
"jmp *(%%eax)\n\t"
|
|
: : "m"(stacktop), "m"(catch_pc), "m"(object), "m"(frame)
|
|
: "eax", "ebx", "ecx", "edx"
|
|
);
|
|
#elif defined(_MSC_VER)
|
|
__asm {
|
|
mov edx, dword ptr stacktop
|
|
mov eax, dword ptr catch_pc
|
|
mov ecx, dword ptr object
|
|
mov ebx, dword ptr frame
|
|
mov ebp, ebx
|
|
mov esp, edx
|
|
jmp [eax]
|
|
}
|
|
#else
|
|
#error "Don't know how to unwind the stack under x86"
|
|
#endif
|
|
}
|
|
|
|
void _jit_unwind_stack(void *frame, void *pc, void *object)
|
|
{
|
|
#if defined(__GNUC__)
|
|
__asm__ (
|
|
"movl %0, %%edx\n\t"
|
|
"movl %1, %%eax\n\t"
|
|
"movl %2, %%ecx\n\t"
|
|
"movl %%edx, %%ebp\n\t"
|
|
"popl %%ebp\n\t"
|
|
"popl %%edx\n\t"
|
|
"jmp *%%eax\n\t"
|
|
: : "m"(frame), "m"(pc), "m"(object)
|
|
: "eax", "ecx", "edx"
|
|
);
|
|
#elif defined(_MSC_VER)
|
|
__asm {
|
|
mov edx, dword ptr frame
|
|
mov eax, dword ptr pc
|
|
mov ecx, dword ptr object
|
|
mov ebp, edx
|
|
pop ebp
|
|
pop edx
|
|
jmp eax
|
|
}
|
|
#else
|
|
#error "Don't know how to unwind the stack on x86 platforms"
|
|
#endif
|
|
}
|
|
|
|
#endif /* JIT_BACKEND_X86 */
|
|
|