#include "jit-internal.h"
#include "jit-rules.h"
#include "jit-apply-rules.h"
#if defined(JIT_BACKEND_X86_64)
#include "jit-gen-x86-64.h"
#include "jit-reg-alloc.h"
#include "jit-setjmp.h"
#include <stdio.h>
#define X86_64_REG_RAX 0
#define X86_64_REG_RCX 1
#define X86_64_REG_RDX 2
#define X86_64_REG_RBX 3
#define X86_64_REG_RSI 4
#define X86_64_REG_RDI 5
#define X86_64_REG_R8 6
#define X86_64_REG_R9 7
#define X86_64_REG_R10 8
#define X86_64_REG_R11 9
#define X86_64_REG_R12 10
#define X86_64_REG_R13 11
#define X86_64_REG_R14 12
#define X86_64_REG_R15 13
#define X86_64_REG_RBP 14
#define X86_64_REG_RSP 15
#define X86_64_REG_XMM0 16
#define X86_64_REG_XMM1 17
#define X86_64_REG_XMM2 18
#define X86_64_REG_XMM3 19
#define X86_64_REG_XMM4 20
#define X86_64_REG_XMM5 21
#define X86_64_REG_XMM6 22
#define X86_64_REG_XMM7 23
#define X86_64_REG_XMM8 24
#define X86_64_REG_XMM9 25
#define X86_64_REG_XMM10 26
#define X86_64_REG_XMM11 27
#define X86_64_REG_XMM12 28
#define X86_64_REG_XMM13 29
#define X86_64_REG_XMM14 30
#define X86_64_REG_XMM15 31
#define X86_64_REG_ST0 32
#define X86_64_REG_ST1 33
#define X86_64_REG_ST2 34
#define X86_64_REG_ST3 35
#define X86_64_REG_ST4 36
#define X86_64_REG_ST5 37
#define X86_64_REG_ST6 38
#define X86_64_REG_ST7 39
#define IS_GENERAL_REG(reg) (((reg) & ~0x0f) == 0)
#define IS_XMM_REG(reg) (((reg) & ~0x0f) == 0x10)
#define IS_FPU_REG(reg) (((reg) & ~0x0f) == 0x20)
#define X86_64_SCRATCH X86_64_R11
#define HAVE_RED_ZONE 1
#define TODO() \
do
{ \
fprintf
(stderr,
"TODO at %s, %d\n"
, __FILE__, (
int
)__LINE__); \
}
while
(0)
#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
#define DEBUG_FIXUPS 0
#define _JIT_MAX_MEMCPY_INLINE 0x40
typedef
struct
{
unsigned
int
gp_offset;
unsigned
int
fp_offset;
void
*overflow_arg_area;
void
*reg_save_area;
} _jit_va_list;
static
int
_jit_word_arg_regs[] = {X86_64_REG_RDI, X86_64_REG_RSI,
X86_64_REG_RDX, X86_64_REG_RCX,
X86_64_REG_R8, X86_64_REG_R9};
#define _jit_num_word_regs 6
static
int
_jit_float_arg_regs[] = {X86_64_REG_XMM0, X86_64_REG_XMM1,
X86_64_REG_XMM2, X86_64_REG_XMM3,
X86_64_REG_XMM4, X86_64_REG_XMM5,
X86_64_REG_XMM6, X86_64_REG_XMM7};
#define _jit_num_float_regs 8
static
int
_jit_word_return_regs[] = {X86_64_REG_RAX, X86_64_REG_RDX};
#define _jit_num_word_return_regs 2
static
int
_jit_sse_return_regs[] = {X86_64_REG_XMM0, X86_64_REG_XMM1};
#define _jit_num_sse_return_regs 2
static
_jit_regclass_t *x86_64_reg;
static
_jit_regclass_t *x86_64_creg;
static
_jit_regclass_t *x86_64_dreg;
static
_jit_regclass_t *x86_64_rreg;
static
_jit_regclass_t *x86_64_sreg;
static
_jit_regclass_t *x86_64_freg;
static
_jit_regclass_t *x86_64_xreg;
void
_jit_init_backend(
void
)
{
x86_64_reg = _jit_regclass_create(
"reg"
, JIT_REG_WORD | JIT_REG_LONG, 14,
X86_64_REG_RAX, X86_64_REG_RCX,
X86_64_REG_RDX, X86_64_REG_RBX,
X86_64_REG_RSI, X86_64_REG_RDI,
X86_64_REG_R8, X86_64_REG_R9,
X86_64_REG_R10, X86_64_REG_R11,
X86_64_REG_R12, X86_64_REG_R13,
X86_64_REG_R14, X86_64_REG_R15);
x86_64_creg = _jit_regclass_create(
"creg"
, JIT_REG_WORD | JIT_REG_LONG, 9,
X86_64_REG_RAX, X86_64_REG_RCX,
X86_64_REG_RDX, X86_64_REG_RSI,
X86_64_REG_RDI, X86_64_REG_R8,
X86_64_REG_R9, X86_64_REG_R10,
X86_64_REG_R11);
x86_64_dreg = _jit_regclass_create(
"dreg"
, JIT_REG_WORD | JIT_REG_LONG, 12,
X86_64_REG_RCX, X86_64_REG_RBX,
X86_64_REG_RSI, X86_64_REG_RDI,
X86_64_REG_R8, X86_64_REG_R9,
X86_64_REG_R10, X86_64_REG_R11,
X86_64_REG_R12, X86_64_REG_R13,
X86_64_REG_R14, X86_64_REG_R15);
x86_64_rreg = _jit_regclass_create(
"rreg"
, JIT_REG_WORD | JIT_REG_LONG, 12,
X86_64_REG_RCX, X86_64_REG_RBX,
X86_64_REG_RSI, X86_64_REG_RDI,
X86_64_REG_R8, X86_64_REG_R9,
X86_64_REG_R10, X86_64_REG_R11,
X86_64_REG_R12, X86_64_REG_R13,
X86_64_REG_R14, X86_64_REG_R15);
x86_64_sreg = _jit_regclass_create(
"sreg"
, JIT_REG_WORD | JIT_REG_LONG, 13,
X86_64_REG_RAX, X86_64_REG_RDX,
X86_64_REG_RBX, X86_64_REG_RSI,
X86_64_REG_RDI, X86_64_REG_R8,
X86_64_REG_R9, X86_64_REG_R10,
X86_64_REG_R11, X86_64_REG_R12,
X86_64_REG_R13, X86_64_REG_R14,
X86_64_REG_R15);
x86_64_freg = _jit_regclass_create(
"freg"
, JIT_REG_X86_64_FLOAT | JIT_REG_IN_STACK, 8,
X86_64_REG_ST0, X86_64_REG_ST1,
X86_64_REG_ST2, X86_64_REG_ST3,
X86_64_REG_ST4, X86_64_REG_ST5,
X86_64_REG_ST6, X86_64_REG_ST7);
x86_64_xreg = _jit_regclass_create(
"xreg"
, JIT_REG_FLOAT32 | JIT_REG_FLOAT64, 16,
X86_64_REG_XMM0, X86_64_REG_XMM1,
X86_64_REG_XMM2, X86_64_REG_XMM3,
X86_64_REG_XMM4, X86_64_REG_XMM5,
X86_64_REG_XMM6, X86_64_REG_XMM7,
X86_64_REG_XMM8, X86_64_REG_XMM9,
X86_64_REG_XMM10, X86_64_REG_XMM11,
X86_64_REG_XMM12, X86_64_REG_XMM13,
X86_64_REG_XMM14, X86_64_REG_XMM15);
}
int
_jit_opcode_is_supported(
int
opcode)
{
switch
(opcode)
{
#define JIT_INCLUDE_SUPPORTED
#include "jit-rules-x86-64.inc"
#undef JIT_INCLUDE_SUPPORTED
}
return
0;
}
int
_jit_setup_indirect_pointer(jit_function_t func, jit_value_t value)
{
return
jit_insn_outgoing_reg(func, value, X86_64_REG_R11);
}
static
int
_jit_xmm1_reg_imm_size_float32(jit_gencode_t gen, unsigned
char
**inst_ptr,
X86_64_XMM1_OP opc,
int
reg,
jit_float32 *float32_value)
{
void
*ptr;
jit_nint offset;
unsigned
char
*inst;
inst = *inst_ptr;
ptr = _jit_gen_alloc(gen,
sizeof
(jit_float32));
if
(!ptr)
{
return
0;
}
jit_memcpy(ptr, float32_value,
sizeof
(jit_float32));
offset = (jit_nint)ptr - ((jit_nint)inst + (reg > 7 ? 9 : 8));
if
((offset >= jit_min_int) && (offset <= jit_max_int))
{
x86_64_xmm1_reg_membase(inst, opc, reg,
X86_64_RIP, offset, 0);
}
else
if
(((jit_nint)ptr >= jit_min_int) &&
((jit_nint)ptr <= jit_max_int))
{
x86_64_xmm1_reg_mem(inst, opc, reg, (jit_nint)ptr, 0);
}
else
{
TODO();
return
0;
}
*inst_ptr = inst;
return
1;
}
static
int
_jit_xmm1_reg_imm_size_float64(jit_gencode_t gen, unsigned
char
**inst_ptr,
X86_64_XMM1_OP opc,
int
reg,
jit_float64 *float64_value)
{
void
*ptr;
jit_nint offset;
unsigned
char
*inst;
inst = *inst_ptr;
ptr = _jit_gen_alloc(gen,
sizeof
(jit_float64));
if
(!ptr)
{
return
0;
}
jit_memcpy(ptr, float64_value,
sizeof
(jit_float64));
offset = (jit_nint)ptr - ((jit_nint)inst + (reg > 7 ? 9 : 8));
if
((offset >= jit_min_int) && (offset <= jit_max_int))
{
x86_64_xmm1_reg_membase(inst, opc, reg,
X86_64_RIP, offset, 1);
}
else
if
(((jit_nint)ptr >= jit_min_int) &&
((jit_nint)ptr <= jit_max_int))
{
x86_64_xmm1_reg_mem(inst, opc, reg, (jit_nint)ptr, 1);
}
else
{
TODO();
return
0;
}
*inst_ptr = inst;
return
1;
}
static
int
_jit_plops_reg_imm(jit_gencode_t gen, unsigned
char
**inst_ptr,
X86_64_XMM_PLOP opc,
int
reg,
void
*packed_value)
{
void
*ptr;
jit_nint offset;
unsigned
char
*inst;
inst = *inst_ptr;
ptr = _jit_gen_alloc(gen, 16);
if
(!ptr)
{
return
0;
}
jit_memcpy(ptr, packed_value, 16);
offset = (jit_nint)ptr - ((jit_nint)inst + (reg > 7 ? 8 : 7));
if
((offset >= jit_min_int) && (offset <= jit_max_int))
{
x86_64_plops_reg_membase(inst, opc, reg, X86_64_RIP, offset);
*inst_ptr = inst;
return
1;
}
if
(((jit_nint)ptr >= jit_min_int) &&
((jit_nint)ptr <= jit_max_int))
{
x86_64_plops_reg_mem(inst, opc, reg, (jit_nint)ptr);
*inst_ptr = inst;
return
1;
}
TODO();
return
0;
}
static
int
_jit_plopd_reg_imm(jit_gencode_t gen, unsigned
char
**inst_ptr,
X86_64_XMM_PLOP opc,
int
reg,
void
*packed_value)
{
void
*ptr;
jit_nint offset;
unsigned
char
*inst;
inst = *inst_ptr;
ptr = _jit_gen_alloc(gen, 16);
if
(!ptr)
{
return
0;
}
jit_memcpy(ptr, packed_value, 16);
offset = (jit_nint)ptr - ((jit_nint)inst + (reg > 7 ? 9 : 8));
if
((offset >= jit_min_int) && (offset <= jit_max_int))
{
x86_64_plopd_reg_membase(inst, opc, reg, X86_64_RIP, offset);
*inst_ptr = inst;
return
1;
}
if
(((jit_nint)ptr >= jit_min_int) &&
((jit_nint)ptr <= jit_max_int))
{
x86_64_plopd_reg_mem(inst, opc, reg, (jit_nint)ptr);
*inst_ptr = inst;
return
1;
}
TODO();
return
0;
}
static
unsigned
char
*
_x86_64_set_fpu_roundmode(unsigned
char
*inst,
int
scratch_reg,
int
sp_offset, X86_64_ROUNDMODE mode)
{
int
fpcw_save_offset = sp_offset + 4;
int
fpcw_new_offset = sp_offset;
int
round_mode = ((
int
)mode) << 10;
int
round_mode_mask = ~(((
int
)X86_ROUND_ZERO) << 10);
x86_64_fnstcw_membase(inst, X86_64_RSP, fpcw_save_offset);
x86_64_mov_reg_membase_size(inst, scratch_reg, X86_64_RSP, fpcw_save_offset, 2);
if
(mode != X86_ROUND_ZERO)
{
x86_64_and_reg_imm_size(inst, scratch_reg, round_mode_mask, 2);
}
x86_64_or_reg_imm_size(inst, scratch_reg, round_mode, 2);
x86_64_mov_membase_reg_size(inst, X86_64_RSP, fpcw_new_offset, scratch_reg, 2);
x86_64_fldcw_membase(inst, X86_64_RSP, fpcw_new_offset);
return
inst;
}
static
unsigned
char
*
_x86_64_restore_fpcw(unsigned
char
*inst,
int
sp_offset)
{
int
fpcw_save_offset = sp_offset + 4;
x86_64_fldcw_membase(inst, X86_64_RSP, fpcw_save_offset);
return
inst;
}
static
unsigned
char
*
_x86_64_set_xmm_roundmode(unsigned
char
*inst,
int
scratch_reg,
int
sp_offset, X86_64_ROUNDMODE mode)
{
int
mxcsr_save_offset = sp_offset + 4;
int
mxcsr_new_offset = sp_offset;
int
round_mode = ((
int
)mode) << 13;
int
round_mode_mask = ~(((
int
)X86_ROUND_ZERO) << 13);
x86_64_stmxcsr_membase(inst, X86_64_RSP, mxcsr_save_offset);
x86_64_mov_reg_membase_size(inst, scratch_reg, X86_64_RSP, mxcsr_save_offset, 4);
if
(mode != X86_ROUND_ZERO)
{
x86_64_and_reg_imm_size(inst, scratch_reg, round_mode_mask, 4);
}
x86_64_or_reg_imm_size(inst, scratch_reg, round_mode, 4);
x86_64_mov_membase_reg_size(inst, X86_64_RSP, mxcsr_new_offset, scratch_reg, 4);
x86_64_ldmxcsr_membase(inst, X86_64_RSP, mxcsr_new_offset);
return
inst;
}
static
unsigned
char
*
_x86_64_restore_mxcsr(unsigned
char
*inst,
int
sp_offset)
{
int
mxcsr_save_offset = sp_offset + 4;
x86_64_ldmxcsr_membase(inst, X86_64_RSP, mxcsr_save_offset);
return
inst;
}
static
unsigned
char
*
x86_64_rounds_reg_reg(unsigned
char
*inst,
int
dreg,
int
sreg,
int
scratch_reg, X86_64_ROUNDMODE mode)
{
#ifdef HAVE_RED_ZONE
#ifdef HAVE_X86_SSE_4_1
x86_64_roundss_reg_reg(inst, dreg, sreg, mode);
#else
x86_64_movss_membase_reg(inst, X86_64_RSP, -16, sreg);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, -8, mode);
x86_64_fld_membase_size(inst, X86_64_RSP, -16, 4);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, -8);
x86_64_fstp_membase_size(inst, X86_64_RSP, -16, 4);
x86_64_movss_reg_membase(inst, dreg, X86_64_RSP, -16);
#endif
#else
#ifdef HAVE_X86_SSE_4_1
x86_64_roundss_reg_reg(inst, dreg, sreg, mode);
#else
x86_64_sub_reg_imm_size(inst, X86_64_RSP, 16, 8);
x86_64_movss_regp_reg(inst, X86_64_RSP, sreg);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, 8, mode);
x86_64_fld_regp_size(inst, X86_64_RSP, 4);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, 8);
x86_64_fstp_regp_size(inst, X86_64_RSP, 4);
x86_64_movss_reg_regp(inst, dreg, X86_64_RSP);
x86_64_add_reg_imm_size(inst, X86_64_RSP, 16, 8);
#endif
#endif
return
inst;
}
static
unsigned
char
*
x86_64_rounds_reg_membase(unsigned
char
*inst,
int
dreg,
int
offset,
int
scratch_reg, X86_64_ROUNDMODE mode)
{
#ifdef HAVE_RED_ZONE
#ifdef HAVE_X86_SSE_4_1
x86_64_roundss_reg_membase(inst, dreg, X86_64_RBP, offset, mode);
#else
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 4);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, -8, mode);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, -8);
x86_64_fstp_membase_size(inst, X86_64_RSP, -16, 4);
x86_64_movss_reg_membase(inst, dreg, X86_64_RSP, -16);
#endif
#else
#ifdef HAVE_X86_SSE_4_1
x86_64_roundss_reg_membase(inst, dreg, X86_64_RBP, offset, mode);
#else
x86_64_sub_reg_imm_size(inst, X86_64_RSP, 16, 8);
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 4);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, 8, mode);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, 8);
x86_64_fstp_regp_size(inst, X86_64_RSP, 4);
x86_64_movss_reg_regp(inst, dreg, X86_64_RSP);
x86_64_add_reg_imm_size(inst, X86_64_RSP, 16, 8);
#endif
#endif
return
inst;
}
static
unsigned
char
*
x86_64_roundd_reg_reg(unsigned
char
*inst,
int
dreg,
int
sreg,
int
scratch_reg, X86_64_ROUNDMODE mode)
{
#ifdef HAVE_RED_ZONE
#ifdef HAVE_X86_SSE_4_1
x86_64_roundsd_reg_reg(inst, dreg, sreg, mode);
#else
x86_64_movsd_membase_reg(inst, X86_64_RSP, -16, sreg);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, -8, mode);
x86_64_fld_membase_size(inst, X86_64_RSP, -16, 8);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, -8);
x86_64_fstp_membase_size(inst, X86_64_RSP, -16, 8);
x86_64_movsd_reg_membase(inst, dreg, X86_64_RSP, -16);
#endif
#else
#ifdef HAVE_X86_SSE_4_1
x86_64_roundsd_reg_reg(inst, dreg, sreg, mode);
#else
x86_64_sub_reg_imm_size(inst, X86_64_RSP, 16, 8);
x86_64_movsd_regp_reg(inst, X86_64_RSP, sreg);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, 8, mode);
x86_64_fld_regp_size(inst, X86_64_RSP, 8);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, 8);
x86_64_fstp_regp_size(inst, X86_64_RSP, 8);
x86_64_movsd_reg_regp(inst, dreg, X86_64_RSP);
x86_64_add_reg_imm_size(inst, X86_64_RSP, 16, 8);
#endif
#endif
return
inst;
}
static
unsigned
char
*
x86_64_roundd_reg_membase(unsigned
char
*inst,
int
dreg,
int
offset,
int
scratch_reg, X86_64_ROUNDMODE mode)
{
#ifdef HAVE_RED_ZONE
#ifdef HAVE_X86_SSE_4_1
x86_64_roundsd_reg_membase(inst, dreg, X86_64_RBP, offset, mode);
#else
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 8);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, -8, mode);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, -8);
x86_64_fstp_membase_size(inst, X86_64_RSP, -16, 8);
x86_64_movsd_reg_membase(inst, dreg, X86_64_RSP, -16);
#endif
#else
#ifdef HAVE_X86_SSE_4_1
x86_64_roundsd_reg_membase(inst, dreg, X86_64_RBP, offset, mode);
#else
x86_64_sub_reg_imm_size(inst, X86_64_RSP, 16, 8);
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 8);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, 8, mode);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, 8);
x86_64_fstp_regp_size(inst, X86_64_RSP, 8);
x86_64_movsd_reg_regp(inst, dreg, X86_64_RSP);
x86_64_add_reg_imm_size(inst, X86_64_RSP, 16, 8);
#endif
#endif
return
inst;
}
static
unsigned
char
*
x86_64_roundnf(unsigned
char
*inst,
int
scratch_reg, X86_64_ROUNDMODE mode)
{
#ifdef HAVE_RED_ZONE
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, -8, mode);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, -8);
#else
x86_64_sub_reg_imm_size(inst, X86_64_RSP, 8, 8);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, 0, mode);
x86_64_frndint(inst);
inst = _x86_64_restore_fpcw(inst, 0);
x86_64_add_reg_imm_size(inst, X86_64_RSP, 8, 8);
#endif
return
inst;
}
static
unsigned
char
*
x86_64_nfloat_to_int(unsigned
char
*inst,
int
dreg,
int
scratch_reg,
int
size)
{
#ifdef HAVE_RED_ZONE
#ifdef HAVE_X86_FISTTP
x86_64_fisttp_membase_size(inst, X86_64_RSP, -8, 4);
x86_64_mov_reg_membase_size(inst, dreg, X86_64_RSP, -8, 4);
#else
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, -8, X86_ROUND_ZERO);
x86_64_fistp_membase_size(inst, X86_64_RSP, -16, size);
inst = _x86_64_restore_fpcw(inst, -8);
x86_64_mov_reg_membase_size(inst, dreg, X86_64_RSP, -16, size);
#endif
#else
#ifdef HAVE_X86_FISTTP
x86_64_sub_reg_imm_size(inst, X86_64_RSP, 8, 8);
x86_64_fisttp_regp_size(inst, X86_64_RSP, 4);
x86_64_mov_reg_regp_size(inst, dreg, X86_64_RSP, 4);
x86_64_add_reg_imm_size(inst, X86_64_RSP, 8, 8);
#else
x86_64_sub_reg_imm_size(inst, X86_64_RSP, 16, 8);
inst = _x86_64_set_fpu_roundmode(inst, scratch_reg, 8, X86_ROUND_ZERO);
x86_64_fistp_regp_size(inst, X86_64_RSP, size);
inst = _x86_64_restore_fpcw(inst, 8);
x86_64_mov_reg_regp_size(inst, dreg, X86_64_RSP, size);
x86_64_add_reg_imm_size(inst, X86_64_RSP, 16, 8);
#endif
#endif
return
inst;
}
static
unsigned
char
*
x86_64_call_code(unsigned
char
*inst, jit_nint func)
{
jit_nint offset;
x86_64_mov_reg_imm_size(inst, X86_64_RAX, 8, 4);
offset = func - ((jit_nint)inst + 5);
if
(offset >= jit_min_int && offset <= jit_max_int)
{
x86_64_call_imm(inst, offset);
}
else
{
x86_64_mov_reg_imm_size(inst, X86_64_SCRATCH, func, 8);
x86_64_call_reg(inst, X86_64_SCRATCH);
}
return
inst;
}
static
unsigned
char
*
x86_64_jump_to_code(unsigned
char
*inst, jit_nint func)
{
jit_nint offset;
offset = func - ((jit_nint)inst + 5);
if
(offset >= jit_min_int && offset <= jit_max_int)
{
x86_64_jmp_imm(inst, offset);
}
else
{
x86_64_mov_reg_imm_size(inst, X86_64_SCRATCH, func, 8);
x86_64_jmp_reg(inst, X86_64_SCRATCH);
}
return
inst;
}
static
unsigned
char
*
throw_builtin(unsigned
char
*inst, jit_function_t func,
int
type)
{
if
(func->builder->setjmp_value != 0)
{
_jit_gen_fix_value(func->builder->setjmp_value);
x86_64_lea_membase_size(inst, X86_64_RDI, X86_64_RIP, 0, 8);
x86_64_mov_membase_reg_size(inst, X86_64_RBP,
func->builder->setjmp_value->frame_offset
+ jit_jmp_catch_pc_offset, X86_64_RDI, 8);
}
x86_64_mov_reg_imm_size(inst, X86_64_RDI, type, 4);
return
x86_64_call_code(inst, (jit_nint)jit_exception_builtin);
}
static
void
_spill_reg(unsigned
char
**inst_ptr, jit_type_t type,
jit_int reg, jit_int offset)
{
unsigned
char
*inst = *inst_ptr;
if
(IS_GENERAL_REG(reg))
{
switch
(type->kind)
{
#if 0
case
JIT_TYPE_SBYTE:
case
JIT_TYPE_UBYTE:
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg, 1);
}
break
;
case
JIT_TYPE_SHORT:
case
JIT_TYPE_USHORT:
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg, 2);
}
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:
case
JIT_TYPE_FLOAT32:
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg, 4);
}
break
;
case
JIT_TYPE_LONG:
case
JIT_TYPE_ULONG:
case
JIT_TYPE_FLOAT64:
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg, 8);
}
break
;
case
JIT_TYPE_STRUCT:
case
JIT_TYPE_UNION:
{
jit_nuint size = jit_type_get_size(type);
if
(size == 1)
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg, 1);
}
else
if
(size == 2)
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg, 2);
}
else
if
(size <= 4)
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg, 4);
}
else
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg, 8);
}
}
}
}
else
if
(IS_XMM_REG(reg))
{
switch
(type->kind)
{
case
JIT_TYPE_FLOAT32:
{
x86_64_movss_membase_reg(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg);
}
break
;
case
JIT_TYPE_FLOAT64:
{
x86_64_movsd_membase_reg(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg);
}
break
;
case
JIT_TYPE_STRUCT:
case
JIT_TYPE_UNION:
{
jit_nuint size = jit_type_get_size(type);
if
(size <= 4)
{
x86_64_movss_membase_reg(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg);
}
else
if
(size <= 8)
{
x86_64_movsd_membase_reg(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg);
}
else
{
jit_nint alignment = jit_type_get_alignment(type);
if
((alignment & 0xf) == 0)
{
x86_64_movaps_membase_reg(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg);
}
else
{
x86_64_movups_membase_reg(inst, X86_64_RBP, offset,
_jit_reg_info[reg].cpu_reg);
}
}
}
break
;
}
}
else
if
(IS_FPU_REG(reg))
{
switch
(type->kind)
{
case
JIT_TYPE_FLOAT32:
{
x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 4);
}
break
;
case
JIT_TYPE_FLOAT64:
{
x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 8);
}
break
;
case
JIT_TYPE_NFLOAT:
{
if
(
sizeof
(jit_nfloat) ==
sizeof
(jit_float64))
{
x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 8);
}
else
{
x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 10);
}
}
break
;
}
}
*inst_ptr = inst;
}
void
_jit_gen_fix_value(jit_value_t value)
{
if
(!(value->has_frame_offset) && !(value->is_constant))
{
jit_nuint alignment = jit_type_get_alignment(value->type);
jit_nint size =jit_type_get_size(value->type);
jit_nint frame_size = value->block->func->builder->frame_size;
size = (jit_nint)(ROUND_STACK(size));
frame_size += size;
frame_size = (frame_size + (alignment - 1)) & ~(alignment - 1);
value->block->func->builder->frame_size = frame_size;
value->frame_offset = -frame_size;
value->has_frame_offset = 1;
}
}
void
_jit_gen_spill_global(jit_gencode_t gen,
int
reg, jit_value_t value)
{
jit_cache_setup_output(16);
if
(value)
{
jit_type_t type = jit_type_normalize(value->type);
_jit_gen_fix_value(value);
_spill_reg(&inst, type, value->global_reg, value->frame_offset);
}
else
{
x86_64_push_reg_size(inst, _jit_reg_info[reg].cpu_reg, 8);
}
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_64_mov_reg_membase_size(inst,
_jit_reg_info[value->global_reg].cpu_reg,
X86_64_RBP, value->frame_offset, 8);
}
else
{
x86_64_pop_reg_size(inst, _jit_reg_info[reg].cpu_reg, 8);
}
jit_cache_end_output();
}
void
_jit_gen_spill_reg(jit_gencode_t gen,
int
reg,
int
other_reg, jit_value_t value)
{
jit_type_t type;
jit_cache_setup_output(16);
if
(value->has_global_register)
{
reg = _jit_reg_info[reg].cpu_reg;
other_reg = _jit_reg_info[value->global_reg].cpu_reg;
x86_64_mov_reg_reg_size(inst, other_reg, reg,
sizeof
(
void
*));
jit_cache_end_output();
return
;
}
_jit_gen_fix_value(value);
type = jit_type_normalize(value->type);
_spill_reg(&inst, type, reg, value->frame_offset);
jit_cache_end_output();
}
void
_jit_gen_free_reg(jit_gencode_t gen,
int
reg,
int
other_reg,
int
value_used)
{
if
(!value_used && IS_FPU_REG(reg))
{
_jit_gen_check_space(gen, 2);
x86_fstp(gen->ptr, reg - X86_64_REG_ST0);
}
}
static
unsigned
char
*
setcc_reg(unsigned
char
*inst,
int
reg,
int
cond,
int
is_signed)
{
x86_64_set_reg(inst, cond, reg, is_signed);
x86_64_movzx8_reg_reg_size(inst, reg, reg, 4);
return
inst;
}
#define _JIT_CALC_FIXUP(fixup_list, inst) \
((jit_int)((jit_nint)(inst) - (jit_nint)(fixup_list)))
#define _JIT_CALC_NEXT_FIXUP(fixup_list, fixup) \
((fixup) ? ((jit_nint)(fixup_list) - (jit_nint)(fixup)) : (jit_nint)0)
static
int
long_form_branch(
int
opcode)
{
if
(opcode == 0xEB)
{
return
0xE9;
}
else
{
return
opcode + 0x0F10;
}
}
static
unsigned
char
*
output_branch(jit_function_t func, unsigned
char
*inst,
int
opcode,
jit_insn_t insn)
{
jit_block_t block;
if
((insn->flags & JIT_INSN_VALUE1_IS_LABEL) != 0)
{
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)
{
jit_nint offset;
offset = ((unsigned
char
*)(block->address)) - (inst + 2);
if
(x86_is_imm8(offset))
{
*inst++ = (unsigned
char
)opcode;
*inst++ = (unsigned
char
)offset;
}
else
{
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
{
jit_int fixup;
opcode = long_form_branch(opcode);
if
(opcode < 256)
{
*inst++ = (unsigned
char
)opcode;
}
else
{
*inst++ = (unsigned
char
)(opcode >> 8);
*inst++ = (unsigned
char
)opcode;
}
if
(block->fixup_list)
{
fixup = _JIT_CALC_FIXUP(block->fixup_list, inst);
}
else
{
fixup = 0;
}
block->fixup_list = (
void
*)inst;
x86_imm_emit32(inst, fixup);
if
(DEBUG_FIXUPS)
{
fprintf
(stderr,
"Block: %lx, Current Fixup: %lx, Next fixup: %lx\n"
,
(jit_nint)block, (jit_nint)(block->fixup_list),
(jit_nint)fixup);
}
}
return
inst;
}
static
unsigned
char
*
jump_to_epilog(jit_gencode_t gen, unsigned
char
*inst, jit_block_t block)
{
jit_int fixup;
if
(_jit_block_is_final(block))
{
return
inst;
}
*inst++ = (unsigned
char
)0xE9;
if
(gen->epilog_fixup)
{
fixup = _JIT_CALC_FIXUP(gen->epilog_fixup, inst);
}
else
{
fixup = 0;
}
gen->epilog_fixup = (
void
*)inst;
x86_imm_emit32(inst, fixup);
return
inst;
}
static
unsigned
char
*
fixup_alloca(jit_gencode_t gen, unsigned
char
*inst,
int
reg)
{
#ifdef JIT_USE_PARAM_AREA
jit_int fixup;
jit_int temp;
temp = 1234567;
x86_64_add_reg_imm_size(inst, reg, temp, 8);
inst -= 4;
if
(gen->alloca_fixup)
{
fixup = _JIT_CALC_FIXUP(gen->alloca_fixup, inst);
}
else
{
fixup = 0;
}
gen->alloca_fixup = (
void
*)inst;
x86_imm_emit32(inst, fixup);
#else /* !JIT_USE_PARAM_AREA */
#endif /* JIT_USE_PARAM_AREA */
return
inst;
}
static
unsigned
char
*
xmm_cmp_reg_imm(jit_gencode_t gen, unsigned
char
*inst,
int
xreg,
void
*imm,
int
is_double)
{
int
inst_len = 7 + (is_double ? 1 : 0) + (xreg > 7 ? 1 : 0);
void
*ptr;
jit_nint offset;
if
(is_double)
{
ptr = _jit_gen_alloc(gen,
sizeof
(jit_float64));
if
(!ptr)
{
return
0;
}
jit_memcpy(ptr, imm,
sizeof
(jit_float64));
}
else
{
ptr = _jit_gen_alloc(gen,
sizeof
(jit_float32));
if
(!ptr)
{
return
0;
}
jit_memcpy(ptr, imm,
sizeof
(jit_float32));
}
offset = (jit_nint)ptr - ((jit_nint)inst + inst_len);
if
((offset >= jit_min_int) && (offset <= jit_max_int))
{
if
(is_double)
{
x86_64_ucomisd_reg_membase(inst, xreg, X86_64_RIP, offset);
}
else
{
x86_64_ucomiss_reg_membase(inst, xreg, X86_64_RIP, offset);
}
}
else
if
(((jit_nint)ptr >= jit_min_int) &&
((jit_nint)ptr <= jit_max_int))
{
if
(is_double)
{
x86_64_ucomisd_reg_mem(inst, xreg, (jit_nint)ptr);
}
else
{
x86_64_ucomiss_reg_mem(inst, xreg, (jit_nint)ptr);
}
}
else
{
TODO();
return
0;
}
return
inst;
}
static
unsigned
char
*
xmm_setcc(unsigned
char
*inst,
int
dreg,
int
cond,
int
sreg,
int
nan_result)
{
x86_64_set_reg(inst, cond, dreg, 0);
if
(nan_result)
{
if
((cond != 0) && (cond != 2) && (cond != 3))
{
x86_64_set_reg(inst, 8
, sreg, 0);
x86_64_or_reg_reg_size(inst, dreg, sreg, 4);
}
}
else
{
if
((cond != 1) && (cond != 4) && (cond != 5))
{
x86_64_set_reg(inst, 9
, sreg, 0);
x86_64_and_reg_reg_size(inst, dreg, sreg, 4);
}
}
x86_64_movzx8_reg_reg_size(inst, dreg, dreg, 4);
return
inst;
}
static
unsigned
char
*
xmm_cmp_setcc_reg_imm(jit_gencode_t gen, unsigned
char
*inst,
int
dreg,
int
cond,
int
xreg,
void
*imm,
int
sreg,
int
is_double,
int
nan_result)
{
inst = xmm_cmp_reg_imm(gen, inst, xreg, imm, is_double);
return
xmm_setcc(inst, dreg, cond, sreg, nan_result);
}
static
unsigned
char
*
xmm_cmp_setcc_reg_reg(unsigned
char
*inst,
int
dreg,
int
cond,
int
xreg1,
int
xreg2,
int
sreg,
int
is_double,
int
nan_result)
{
if
(is_double)
{
x86_64_ucomisd_reg_reg(inst, xreg1, xreg2);
}
else
{
x86_64_ucomiss_reg_reg(inst, xreg1, xreg2);
}
return
xmm_setcc(inst, dreg, cond, sreg, nan_result);
}
static
unsigned
char
*
xmm_brcc(jit_function_t func, unsigned
char
*inst,
int
cond,
int
nan_result,
jit_insn_t insn)
{
if
(nan_result)
{
if
((cond != 0) && (cond != 2) && (cond != 3))
{
inst = output_branch(func, inst,
x86_cc_unsigned_map[8], insn);
}
inst = output_branch(func, inst, x86_cc_unsigned_map[cond], insn);
}
else
{
if
((cond != 1) && (cond != 4) && (cond != 5))
{
unsigned
char
*patch;
patch = inst;
x86_branch8(inst, X86_CC_P, 0, 0);
inst = output_branch(func, inst,
x86_cc_unsigned_map[cond], insn);
x86_patch(patch, inst);
}
else
{
inst = output_branch(func, inst,
x86_cc_unsigned_map[cond], insn);
}
}
return
inst;
}
static
unsigned
char
*
xmm_cmp_brcc_reg_imm(jit_gencode_t gen, jit_function_t func,
unsigned
char
*inst,
int
cond,
int
xreg,
void
*imm,
int
is_double,
int
nan_result, jit_insn_t insn)
{
inst = xmm_cmp_reg_imm(gen, inst, xreg, imm, is_double);
return
xmm_brcc(func, inst, cond, nan_result, insn);
}
static
unsigned
char
*
xmm_cmp_brcc_reg_reg(jit_function_t func, unsigned
char
*inst,
int
cond,
int
xreg1,
int
xreg2,
int
is_double,
int
nan_result,
jit_insn_t insn)
{
if
(is_double)
{
x86_64_ucomisd_reg_reg(inst, xreg1, xreg2);
}
else
{
x86_64_ucomiss_reg_reg(inst, xreg1, xreg2);
}
return
xmm_brcc(func, inst, cond, nan_result, insn);
}
static
unsigned
char
*
xmm_cmp_brcc_reg_membase(jit_function_t func, unsigned
char
*inst,
int
cond,
int
xreg1,
int
basereg,
int
offset,
int
is_double,
int
nan_result, jit_insn_t insn)
{
if
(is_double)
{
x86_64_ucomisd_reg_membase(inst, xreg1, basereg, offset);
}
else
{
x86_64_ucomiss_reg_membase(inst, xreg1, basereg, offset);
}
return
xmm_brcc(func, inst, cond, nan_result, insn);
}
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_FPU_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_FPU_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)
{
if
(IS_FPU_REG(reg))
{
int
offset;
jit_cache_setup_output(16);
_jit_gen_fix_value(value);
offset = (
int
)(value->frame_offset);
switch
(jit_type_normalize(value->type)->kind)
{
case
JIT_TYPE_FLOAT32:
{
if
(pop)
{
x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 4);
}
else
{
x86_64_fst_membase_size(inst, X86_64_RBP, offset, 4);
}
}
break
;
case
JIT_TYPE_FLOAT64:
{
if
(pop)
{
x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 8);
}
else
{
x86_64_fst_membase_size(inst, X86_64_RBP, offset, 8);
}
}
break
;
case
JIT_TYPE_NFLOAT:
{
if
(
sizeof
(jit_nfloat) ==
sizeof
(jit_float64))
{
if
(pop)
{
x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 8);
}
else
{
x86_64_fst_membase_size(inst, X86_64_RBP, offset, 8);
}
}
else
{
x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 10);
if
(!pop)
{
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 10);
}
}
}
break
;
}
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;
jit_cache_setup_output(16);
type = jit_type_normalize(value->type);
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_64_clear_reg(inst, _jit_reg_info[reg].cpu_reg);
}
else
{
x86_64_mov_reg_imm_size(inst, _jit_reg_info[reg].cpu_reg,
(jit_nint)(value->address), 4);
}
}
break
;
case
JIT_TYPE_LONG:
case
JIT_TYPE_ULONG:
{
if
((jit_nint)(value->address) == 0)
{
x86_64_clear_reg(inst, _jit_reg_info[reg].cpu_reg);
}
else
{
if
((jit_nint)(value->address) > 0 && (jit_nint)(value->address) <= (jit_nint)jit_max_uint)
{
x86_64_mov_reg_imm_size(inst, _jit_reg_info[reg].cpu_reg,
(jit_nint)(value->address), 4);
}
else
{
x86_64_mov_reg_imm_size(inst, _jit_reg_info[reg].cpu_reg,
(jit_nint)(value->address), 8);
}
}
}
break
;
case
JIT_TYPE_FLOAT32:
{
jit_float32 float32_value;
float32_value = jit_value_get_float32_constant(value);
if
(IS_GENERAL_REG(reg))
{
union
{
jit_float32 float32_value;
jit_int int_value;
} un;
un.float32_value = float32_value;
x86_64_mov_reg_imm_size(inst, _jit_reg_info[reg].cpu_reg,
un.int_value, 4);
}
else
if
(IS_XMM_REG(reg))
{
int
xmm_reg = _jit_reg_info[reg].cpu_reg;
if
(float32_value == (jit_float32) 0.0)
{
x86_64_clear_xreg(inst, xmm_reg);
}
else
{
_jit_xmm1_reg_imm_size_float32(gen, &inst, XMM1_MOV,
xmm_reg, &float32_value);
}
}
else
{
if
(float32_value == (jit_float32) 0.0)
{
x86_fldz(inst);
}
else
if
(float32_value == (jit_float32) 1.0)
{
x86_fld1(inst);
}
else
{
jit_nint offset;
ptr = _jit_gen_alloc(gen,
sizeof
(jit_float32));
jit_memcpy(ptr, &float32_value,
sizeof
(float32_value));
offset = (jit_nint)ptr - ((jit_nint)inst + 6);
if
((offset >= jit_min_int) && (offset <= jit_max_int))
{
x86_64_fld_membase_size(inst, X86_64_RIP, offset, 4);
}
else
if
(((jit_nint)ptr >= jit_min_int) &&
((jit_nint)ptr <= jit_max_int))
{
x86_64_fld_mem_size(inst, (jit_nint)ptr, 4);
}
else
{
TODO();
}
}
}
}
break
;
case
JIT_TYPE_FLOAT64:
{
jit_float64 float64_value;
float64_value = jit_value_get_float64_constant(value);
if
(IS_GENERAL_REG(reg))
{
union
{
jit_float64 float64_value;
jit_long long_value;
} un;
un.float64_value = float64_value;
x86_64_mov_reg_imm_size(inst, _jit_reg_info[reg].cpu_reg,
un.long_value, 8);
}
else
if
(IS_XMM_REG(reg))
{
int
xmm_reg = _jit_reg_info[reg].cpu_reg;
if
(float64_value == (jit_float64) 0.0)
{
x86_64_clear_xreg(inst, xmm_reg);
}
else
{
_jit_xmm1_reg_imm_size_float64(gen, &inst, XMM1_MOV,
xmm_reg, &float64_value);
}
}
else
{
if
(float64_value == (jit_float64) 0.0)
{
x86_fldz(inst);
}
else
if
(float64_value == (jit_float64) 1.0)
{
x86_fld1(inst);
}
else
{
jit_nint offset;
ptr = _jit_gen_alloc(gen,
sizeof
(jit_float64));
jit_memcpy(ptr, &float64_value,
sizeof
(float64_value));
offset = (jit_nint)ptr - ((jit_nint)inst + 6);
if
((offset >= jit_min_int) && (offset <= jit_max_int))
{
x86_64_fld_membase_size(inst, X86_64_RIP, offset, 8);
}
else
if
(((jit_nint)ptr >= jit_min_int) &&
((jit_nint)ptr <= jit_max_int))
{
x86_64_fld_mem_size(inst, (jit_nint)ptr, 8);
}
else
{
TODO();
}
}
}
}
break
;
case
JIT_TYPE_NFLOAT:
{
jit_nfloat nfloat_value;
nfloat_value = jit_value_get_nfloat_constant(value);
if
(IS_GENERAL_REG(reg) &&
sizeof
(jit_nfloat) ==
sizeof
(jit_float64))
{
union
{
jit_nfloat nfloat_value;
jit_long long_value;
} un;
un.nfloat_value = nfloat_value;
x86_64_mov_reg_imm_size(inst, _jit_reg_info[reg].cpu_reg,
un.long_value, 8);
}
else
if
(IS_XMM_REG(reg) &&
sizeof
(jit_nfloat) ==
sizeof
(jit_float64))
{
jit_nint offset;
int
xmm_reg = _jit_reg_info[reg].cpu_reg;
ptr = _jit_gen_alloc(gen,
sizeof
(jit_nfloat));
jit_memcpy(ptr, &nfloat_value,
sizeof
(nfloat_value));
offset = (jit_nint)ptr -
((jit_nint)inst + (xmm_reg > 7 ? 9 : 8));
if
((offset >= jit_min_int) && (offset <= jit_max_int))
{
x86_64_movsd_reg_membase(inst, xmm_reg, X86_64_RIP, offset);
}
else
if
(((jit_nint)ptr >= jit_min_int) &&
((jit_nint)ptr <= jit_max_int))
{
x86_64_movsd_reg_mem(inst, xmm_reg, (jit_nint)ptr);
}
else
{
TODO();
}
}
else
{
if
(nfloat_value == (jit_nfloat) 0.0)
{
x86_fldz(inst);
}
else
if
(nfloat_value == (jit_nfloat) 1.0)
{
x86_fld1(inst);
}
else
{
jit_nint offset;
ptr = _jit_gen_alloc(gen,
sizeof
(jit_nfloat));
jit_memcpy(ptr, &nfloat_value,
sizeof
(nfloat_value));
offset = (jit_nint)ptr - ((jit_nint)inst + 6);
if
((offset >= jit_min_int) && (offset <= jit_max_int))
{
if
(
sizeof
(jit_nfloat) ==
sizeof
(jit_float64))
{
x86_64_fld_membase_size(inst, X86_64_RIP, offset, 8);
}
else
{
x86_64_fld_membase_size(inst, X86_64_RIP, offset, 10);
}
}
else
if
(((jit_nint)ptr >= jit_min_int) &&
((jit_nint)ptr <= jit_max_int))
{
if
(
sizeof
(jit_nfloat) ==
sizeof
(jit_float64))
{
x86_64_fld_mem_size(inst, (jit_nint)ptr, 8);
}
else
{
x86_64_fld_mem_size(inst, (jit_nint)ptr, 10);
}
}
else
{
TODO();
}
}
}
}
break
;
}
}
else
if
(value->in_register || value->in_global_register)
{
if
(value->in_register)
{
src_reg = value->reg;
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_64_mov_reg_reg_size(inst, _jit_reg_info[reg].cpu_reg,
_jit_reg_info[src_reg].cpu_reg, 4);
}
break
;
case
JIT_TYPE_LONG:
case
JIT_TYPE_ULONG:
{
x86_64_mov_reg_reg_size(inst, _jit_reg_info[reg].cpu_reg,
_jit_reg_info[src_reg].cpu_reg, 8);
}
break
;
case
JIT_TYPE_FLOAT32:
{
if
(IS_FPU_REG(reg))
{
if
(IS_FPU_REG(src_reg))
{
x86_fld_reg(inst, fp_stack_index(gen, src_reg));
}
else
if
(IS_XMM_REG(src_reg))
{
_jit_gen_fix_value(value);
offset = (
int
)(value->frame_offset);
x86_64_movss_membase_reg(inst, X86_64_RBP, offset,
_jit_reg_info[src_reg].cpu_reg);
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 4);
}
}
else
if
(IS_XMM_REG(reg))
{
if
(IS_FPU_REG(src_reg))
{
_jit_gen_fix_value(value);
offset = (
int
)(value->frame_offset);
x86_64_fst_membase_size(inst, X86_64_RBP, offset, 4);
x86_64_movss_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset);
}
else
if
(IS_XMM_REG(src_reg))
{
x86_64_movss_reg_reg(inst, _jit_reg_info[reg].cpu_reg,
_jit_reg_info[src_reg].cpu_reg);
}
}
}
break
;
case
JIT_TYPE_FLOAT64:
{
if
(IS_FPU_REG(reg))
{
if
(IS_FPU_REG(src_reg))
{
x86_fld_reg(inst, fp_stack_index(gen, src_reg));
}
else
if
(IS_XMM_REG(src_reg))
{
_jit_gen_fix_value(value);
offset = (
int
)(value->frame_offset);
x86_64_movsd_membase_reg(inst, X86_64_RBP, offset,
_jit_reg_info[src_reg].cpu_reg);
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 8);
}
}
else
if
(IS_XMM_REG(reg))
{
if
(IS_FPU_REG(src_reg))
{
_jit_gen_fix_value(value);
offset = (
int
)(value->frame_offset);
x86_64_fst_membase_size(inst, X86_64_RBP, offset, 8);
x86_64_movsd_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset);
}
else
if
(IS_XMM_REG(src_reg))
{
x86_64_movsd_reg_reg(inst, _jit_reg_info[reg].cpu_reg,
_jit_reg_info[src_reg].cpu_reg);
}
}
}
break
;
case
JIT_TYPE_NFLOAT:
{
if
(IS_FPU_REG(reg))
{
if
(IS_FPU_REG(src_reg))
{
x86_fld_reg(inst, fp_stack_index(gen, src_reg));
}
else
{
fputs
(
"Unsupported native float reg - reg move\n"
, stderr);
}
}
}
break
;
case
JIT_TYPE_STRUCT:
case
JIT_TYPE_UNION:
{
if
(IS_GENERAL_REG(reg))
{
if
(IS_GENERAL_REG(src_reg))
{
x86_64_mov_reg_reg_size(inst, _jit_reg_info[reg].cpu_reg,
_jit_reg_info[src_reg].cpu_reg, 8);
}
else
if
(IS_XMM_REG(src_reg))
{
x86_64_movq_reg_xreg(inst, _jit_reg_info[reg].cpu_reg,
_jit_reg_info[src_reg].cpu_reg);
}
else
{
fputs
(
"Unsupported struct/union reg - reg move\n"
, stderr);
}
}
else
if
(IS_XMM_REG(reg))
{
if
(IS_GENERAL_REG(src_reg))
{
x86_64_movq_xreg_reg(inst, _jit_reg_info[reg].cpu_reg,
_jit_reg_info[src_reg].cpu_reg);
}
else
if
(IS_XMM_REG(src_reg))
{
x86_64_movaps_reg_reg(inst, _jit_reg_info[reg].cpu_reg,
_jit_reg_info[src_reg].cpu_reg);
}
else
{
fputs
(
"Unsupported struct/union reg - reg move\n"
, stderr);
}
}
else
{
fputs
(
"Unsupported struct/union reg - reg move\n"
, stderr);
}
}
}
}
else
{
_jit_gen_fix_value(value);
offset = (
int
)(value->frame_offset);
switch
(type->kind)
{
case
JIT_TYPE_SBYTE:
{
x86_64_movsx8_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 4);
}
break
;
case
JIT_TYPE_UBYTE:
{
x86_64_movzx8_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 4);
}
break
;
case
JIT_TYPE_SHORT:
{
x86_64_movsx16_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 4);
}
break
;
case
JIT_TYPE_USHORT:
{
x86_64_movzx16_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 4);
}
break
;
case
JIT_TYPE_INT:
case
JIT_TYPE_UINT:
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 4);
}
break
;
case
JIT_TYPE_LONG:
case
JIT_TYPE_ULONG:
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 8);
}
break
;
case
JIT_TYPE_FLOAT32:
{
if
(IS_GENERAL_REG(reg))
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 4);
}
if
(IS_XMM_REG(reg))
{
x86_64_movss_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset);
}
else
{
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 4);
}
}
break
;
case
JIT_TYPE_FLOAT64:
{
if
(IS_GENERAL_REG(reg))
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 8);
}
else
if
(IS_XMM_REG(reg))
{
x86_64_movsd_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset);
}
else
{
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 8);
}
}
break
;
case
JIT_TYPE_NFLOAT:
{
if
(
sizeof
(jit_nfloat) ==
sizeof
(jit_float64))
{
if
(IS_GENERAL_REG(reg))
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 8);
}
else
if
(IS_XMM_REG(reg))
{
x86_64_movsd_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset);
}
else
{
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 8);
}
}
else
{
x86_64_fld_membase_size(inst, X86_64_RBP, offset, 10);
}
}
break
;
case
JIT_TYPE_STRUCT:
case
JIT_TYPE_UNION:
{
jit_nuint size = jit_type_get_size(type);
if
(IS_GENERAL_REG(reg))
{
if
(size == 1)
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 1);
}
else
if
(size == 2)
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 2);
}
else
if
(size <= 4)
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 4);
}
else
if
(size <= 8)
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset, 8);
}
}
else
if
(IS_XMM_REG(reg))
{
if
(size <= 4)
{
x86_64_movss_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset);
}
else
if
(size <= 8)
{
x86_64_movsd_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset);
}
else
{
int
alignment = jit_type_get_alignment(type);
if
((alignment & 0xf) == 0)
{
x86_64_movaps_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset);
}
else
{
x86_64_movups_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, offset);
}
}
}
}
}
}
jit_cache_end_output();
}
void
_jit_gen_get_elf_info(jit_elf_info_t *info)
{
info->machine = 62;
info->abi = 0;
info->abi_version = 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;
int
frame_size = 0;
int
regs_to_save = 0;
x86_64_push_reg_size(inst, X86_64_RBP, 8);
x86_64_mov_reg_reg_size(inst, X86_64_RBP, X86_64_RSP, 8);
if
(func->builder->frame_size > 0)
{
frame_size = (func->builder->frame_size + 0x7) & ~0x7;
}
for
(reg = 0; reg < 14; ++reg)
{
if
(jit_reg_is_used(gen->touched, reg) &&
(_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
{
++regs_to_save;
}
}
frame_size += (regs_to_save << 3);
#ifdef JIT_USE_PARAM_AREA
if
(func->builder->param_area_size > 0 &&
(func->builder->param_area_size <= 0x50 || regs_to_save == 0))
{
frame_size += func->builder->param_area_size;
}
#endif /* JIT_USE_PARAM_AREA */
frame_size = (frame_size + 0xf) & ~0xf;
if
(frame_size > 0)
{
x86_64_sub_reg_imm_size(inst, X86_64_RSP, frame_size, 8);
}
if
(regs_to_save > 0)
{
int
current_offset;
#ifdef JIT_USE_PARAM_AREA
if
(func->builder->param_area_size > 0 &&
func->builder->param_area_size <= 0x50)
{
current_offset = func->builder->param_area_size;
}
else
#endif /* JIT_USE_PARAM_AREA */
{
current_offset = 0;
}
for
(reg = 0; reg <= 14; ++reg)
{
if
(jit_reg_is_used(gen->touched, reg) &&
(_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
{
x86_64_mov_membase_reg_size(inst, X86_64_RSP, current_offset,
_jit_reg_info[reg].cpu_reg, 8);
current_offset += 8;
}
}
}
#ifdef JIT_USE_PARAM_AREA
if
(func->builder->param_area_size > 0x50 && regs_to_save > 0)
{
x86_64_sub_reg_imm_size(inst, X86_64_RSP, func->builder->param_area_size, 8);
}
#endif /* JIT_USE_PARAM_AREA */
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)
{
unsigned
char
*inst;
int
reg;
int
current_offset;
jit_int *fixup;
jit_int *next;
_jit_gen_check_space(gen, 48);
inst = gen->ptr;
fixup = (jit_int *)(gen->epilog_fixup);
while
(fixup != 0)
{
if
(DEBUG_FIXUPS)
{
fprintf
(stderr,
"Fixup Address: %lx, Value: %x\n"
,
(jit_nint)fixup, fixup[0]);
}
next = (jit_int *)_JIT_CALC_NEXT_FIXUP(fixup, fixup[0]);
fixup[0] = (jit_int)(((jit_nint)inst) - ((jit_nint)fixup) - 4);
fixup = next;
}
gen->epilog_fixup = 0;
fixup = (jit_int *)(gen->alloca_fixup);
while
(fixup != 0)
{
next = (jit_int *)_JIT_CALC_NEXT_FIXUP(fixup, fixup[0]);
fixup[0] = func->builder->param_area_size;
if
(DEBUG_FIXUPS)
{
fprintf
(stderr,
"Fixup Param Area Size: %lx, Value: %x\n"
,
(jit_nint)fixup, fixup[0]);
}
fixup = next;
}
gen->alloca_fixup = 0;
if
(gen->stack_changed)
{
int
frame_size = func->builder->frame_size;
int
regs_saved = 0;
for
(reg = 0; reg < 14; ++reg)
{
if
(jit_reg_is_used(gen->touched, reg) &&
(_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
{
++regs_saved;
}
}
frame_size += (regs_saved << 3);
frame_size = (frame_size + 0xf) & ~0xf;
current_offset = -frame_size;
for
(reg = 0; reg <= 14; ++reg)
{
if
(jit_reg_is_used(gen->touched, reg) &&
(_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RBP, current_offset, 8);
current_offset += 8;
}
}
}
else
{
#ifdef JIT_USE_PARAM_AREA
if
(func->builder->param_area_size > 0)
{
current_offset = func->builder->param_area_size;
}
else
{
current_offset = 0;
}
#else /* !JIT_USE_PARAM_AREA */
current_offset = 0;
#endif /* !JIT_USE_PARAM_AREA */
for
(reg = 0; reg <= 14; ++reg)
{
if
(jit_reg_is_used(gen->touched, reg) &&
(_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
{
x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
X86_64_RSP, current_offset, 8);
current_offset += 8;
}
}
}
x86_64_mov_reg_reg_size(inst, X86_64_RSP, X86_64_RBP, 8);
x86_64_pop_reg_size(inst, X86_64_RBP, 8);
x86_64_ret(inst);
gen->ptr = inst;
}
static
unsigned
char
*
small_block_copy(jit_gencode_t gen, unsigned
char
*inst,
int
dreg, jit_nint doffset,
int
sreg, jit_nint soffset, jit_int size,
int
scratch_reg,
int
scratch_xreg,
int
is_aligned)
{
int
offset = 0;
while
(size >= 16)
{
if
(is_aligned)
{
x86_64_movaps_reg_membase(inst, scratch_xreg,
sreg, soffset + offset);
x86_64_movaps_membase_reg(inst, dreg, doffset + offset,
scratch_xreg);
}
else
{
x86_64_movups_reg_membase(inst, scratch_xreg,
sreg, soffset + offset);
x86_64_movups_membase_reg(inst, dreg, doffset + offset,
scratch_xreg);
}
size -= 16;
offset += 16;
}
if
(size >= 8)
{
x86_64_mov_reg_membase_size(inst, scratch_reg,
sreg, soffset + offset, 8);
x86_64_mov_membase_reg_size(inst, dreg, doffset + offset,
scratch_reg, 8);
size -= 8;
offset += 8;
}
if
(size >= 4)
{
x86_64_mov_reg_membase_size(inst, scratch_reg,
sreg, soffset + offset, 4);
x86_64_mov_membase_reg_size(inst, dreg, doffset + offset,
scratch_reg, 4);
size -= 4;
offset += 4;
}
if
(size >= 2)
{
x86_64_mov_reg_membase_size(inst, scratch_reg,
sreg, soffset + offset, 2);
x86_64_mov_membase_reg_size(inst, dreg, doffset + offset,
scratch_reg, 2);
size -= 2;
offset += 2;
}
if
(size >= 1)
{
x86_64_mov_reg_membase_size(inst, scratch_reg,
sreg, soffset + offset, 1);
x86_64_mov_membase_reg_size(inst, dreg, doffset + offset,
scratch_reg, 1);
size -= 1;
offset += 1;
}
return
inst;
}
static
unsigned
char
*
small_struct_copy(jit_gencode_t gen, unsigned
char
*inst,
int
dreg, jit_nint doffset,
int
sreg, jit_nint soffset, jit_type_t type,
int
scratch_reg,
int
scratch_xreg)
{
int
size = jit_type_get_size(type);
int
alignment = jit_type_get_alignment(type);
return
small_block_copy(gen, inst, dreg, doffset,
sreg, soffset, size, scratch_reg,
scratch_xreg, ((alignment & 0xf) == 0));
}
static
unsigned
char
*
memory_copy(jit_gencode_t gen, unsigned
char
*inst,
int
dreg, jit_nint doffset,
int
sreg, jit_nint soffset, jit_nint size)
{
if
(dreg == X86_64_RDI)
{
if
(sreg != X86_64_RSI)
{
x86_64_mov_reg_reg_size(inst, X86_64_RSI, sreg, 8);
}
}
else
if
(dreg == X86_64_RSI)
{
if
(sreg == X86_64_RDI)
{
x86_64_mov_reg_reg_size(inst, X86_64_RCX, X86_64_RSI, 8);
x86_64_mov_reg_reg_size(inst, X86_64_RSI, X86_64_RDI, 8);
x86_64_mov_reg_reg_size(inst, X86_64_RDI, X86_64_RCX, 8);
}
else
{
x86_64_mov_reg_reg_size(inst, X86_64_RDI, X86_64_RSI, 8);
if
(sreg != X86_64_RSI)
{
x86_64_mov_reg_reg_size(inst, X86_64_RSI, sreg, 8);
}
}
}
else
{
x86_64_mov_reg_reg_size(inst, X86_64_RSI, sreg, 8);
x86_64_mov_reg_reg_size(inst, X86_64_RDI, dreg, 8);
}
if
((size > 0) && (size <= jit_max_uint))
{
x86_64_mov_reg_imm_size(inst, X86_64_RDX, size, 4);
}
else
{
x86_64_mov_reg_imm_size(inst, X86_64_RDX, size, 8);
}
if
(soffset != 0)
{
x86_64_add_reg_imm_size(inst, X86_64_RSI, soffset, 8);
}
if
(doffset != 0)
{
x86_64_add_reg_imm_size(inst, X86_64_RDI, doffset, 8);
}
inst = x86_64_call_code(inst, (jit_nint)jit_memcpy);
return
inst;
}
void
_jit_gen_start_block(jit_gencode_t gen, jit_block_t block)
{
jit_int *fixup;
jit_int *next;
void
**absolute_fixup;
void
**absolute_next;
block->address = (
void
*)(gen->ptr);
fixup = (jit_int *)(block->fixup_list);
if
(DEBUG_FIXUPS && fixup)
{
fprintf
(stderr,
"Block: %lx\n"
, (jit_nint)block);
}
while
(fixup != 0)
{
if
(DEBUG_FIXUPS)
{
fprintf
(stderr,
"Fixup Address: %lx, Value: %x\n"
,
(jit_nint)fixup, fixup[0]);
}
next = (jit_int *)_JIT_CALC_NEXT_FIXUP(fixup, fixup[0]);
fixup[0] = (jit_int)
(((jit_nint)(block->address)) - ((jit_nint)fixup) - 4);
fixup = next;
}
block->fixup_list = 0;
absolute_fixup = (
void
**)(block->fixup_absolute_list);
while
(absolute_fixup != 0)
{
absolute_next = (
void
**)(absolute_fixup[0]);
absolute_fixup[0] = (
void
*)((jit_nint)(block->address));
absolute_fixup = absolute_next;
}
block->fixup_absolute_list = 0;
}
void
_jit_gen_end_block(jit_gencode_t gen, jit_block_t block)
{
}
int
_jit_gen_is_global_candidate(jit_type_t type)
{
switch
(jit_type_remove_tags(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_LONG:
case
JIT_TYPE_ULONG:
case
JIT_TYPE_NINT:
case
JIT_TYPE_NUINT:
case
JIT_TYPE_PTR:
case
JIT_TYPE_SIGNATURE:
{
return
1;
}
}
return
0;
}
static
int
is_struct_or_union(jit_type_t type)
{
type = jit_type_normalize(type);
if
(type)
{
if
(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION)
{
return
1;
}
}
return
0;
}
static
int
_jit_classify_struct_return(jit_param_passing_t *passing,
_jit_param_t *param, jit_type_t return_type)
{
jit_memset(passing, 0,
sizeof
(jit_param_passing_t));
jit_memset(param, 0,
sizeof
(_jit_param_t));
passing->word_regs = _jit_word_return_regs;
passing->max_word_regs = _jit_num_word_return_regs;
passing->float_regs = _jit_sse_return_regs;
passing->max_float_regs = _jit_num_sse_return_regs;
if
(!(_jit_classify_struct(passing, param, return_type)))
{
return
0;
}
return
1;
}
static
unsigned
char
*
return_struct(unsigned
char
*inst, jit_function_t func,
int
ptr_reg)
{
jit_type_t return_type;
jit_type_t signature = jit_function_get_signature(func);
return_type = jit_type_get_return(signature);
if
(is_struct_or_union(return_type))
{
jit_nuint size;
jit_param_passing_t passing;
_jit_param_t return_param;
if
(!_jit_classify_struct_return(&passing, &return_param,
return_type))
{
return
inst;
}
size = jit_type_get_size(return_type);
if
(size <= 8)
{
if
(IS_GENERAL_REG(return_param.un.reg_info[0].reg))
{
int
reg = _jit_reg_info[return_param.un.reg_info[0].reg].cpu_reg;
if
(size <= 4)
{
x86_64_mov_reg_regp_size(inst, reg, ptr_reg, 4);
}
else
{
x86_64_mov_reg_regp_size(inst, reg, ptr_reg, 8);
}
}
else
{
int
reg = _jit_reg_info[return_param.un.reg_info[0].reg].cpu_reg;
if
(size <= 4)
{
x86_64_movss_reg_regp(inst, reg, ptr_reg);
}
else
{
x86_64_movsd_reg_regp(inst, reg, ptr_reg);
}
}
}
else
{
if
(return_param.arg_class == 1)
{
int
reg = _jit_reg_info[return_param.un.reg_info[0].reg].cpu_reg;
int
alignment = jit_type_get_alignment(return_type);
if
((alignment & 0xf) == 0)
{
x86_64_movaps_reg_regp(inst, reg, ptr_reg);
}
else
{
x86_64_movups_reg_regp(inst, reg, ptr_reg);
}
}
else
{
int
reg = _jit_reg_info[return_param.un.reg_info[0].reg].cpu_reg;
if
(IS_GENERAL_REG(return_param.un.reg_info[0].reg))
{
x86_64_mov_reg_regp_size(inst, reg,
ptr_reg, 8);
}
else
{
x86_64_movsd_reg_regp(inst, reg, ptr_reg);
}
size -= 8;
reg = _jit_reg_info[return_param.un.reg_info[1].reg].cpu_reg;
if
(IS_GENERAL_REG(return_param.un.reg_info[1].reg))
{
if
(size <= 4)
{
x86_64_mov_reg_membase_size(inst, reg, ptr_reg,
8, 4);
}
else
{
x86_64_mov_reg_membase_size(inst, reg, ptr_reg,
8, 8);
}
}
else
{
if
(size <= 4)
{
x86_64_movss_reg_membase(inst, reg,
ptr_reg, 8);
}
else
{
x86_64_movsd_reg_membase(inst, reg,
ptr_reg, 8);
}
}
}
}
}
return
inst;
}
static
unsigned
char
*
flush_return_struct(unsigned
char
*inst, jit_value_t value)
{
jit_type_t return_type;
return_type = jit_value_get_type(value);
if
(is_struct_or_union(return_type))
{
jit_nuint size;
jit_nint offset;
jit_param_passing_t passing;
_jit_param_t return_param;
if
(!_jit_classify_struct_return(&passing, &return_param, return_type))
{
return
inst;
}
return_param.value = value;
_jit_gen_fix_value(value);
size = jit_type_get_size(return_type);
offset = value->frame_offset;
if
(size <= 8)
{
if
(IS_GENERAL_REG(return_param.un.reg_info[0].reg))
{
int
reg = _jit_reg_info[return_param.un.reg_info[0].reg].cpu_reg;
if
(size <= 4)
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset, reg, 4);
}
else
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset, reg, 8);
}
}
else
{
int
reg = _jit_reg_info[return_param.un.reg_info[0].reg].cpu_reg;
if
(size <= 4)
{
x86_64_movss_membase_reg(inst, X86_64_RBP, offset, reg);
}
else
{
x86_64_movsd_membase_reg(inst, X86_64_RBP, offset, reg);
}
}
}
else
{
if
(return_param.arg_class == 1)
{
int
reg = _jit_reg_info[return_param.un.reg_info[0].reg].cpu_reg;
int
alignment = jit_type_get_alignment(return_type);
if
((alignment & 0xf) == 0)
{
x86_64_movaps_membase_reg(inst, X86_64_RBP, offset, reg);
}
else
{
x86_64_movups_membase_reg(inst, X86_64_RBP, offset, reg);
}
}
else
{
int
reg = _jit_reg_info[return_param.un.reg_info[0].reg].cpu_reg;
if
(IS_GENERAL_REG(return_param.un.reg_info[0].reg))
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
reg, 8);
}
else
{
x86_64_movsd_membase_reg(inst, X86_64_RBP, offset, reg);
}
size -= 8;
reg = _jit_reg_info[return_param.un.reg_info[1].reg].cpu_reg;
if
(IS_GENERAL_REG(return_param.un.reg_info[1].reg))
{
if
(size <= 4)
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP,
offset + 8, reg, 4);
}
else
{
x86_64_mov_membase_reg_size(inst, X86_64_RBP,
offset + 8, reg, 8);
}
}
else
{
if
(size <= 4)
{
x86_64_movss_membase_reg(inst, X86_64_RBP,
offset + 8, reg);
}
else
{
x86_64_movsd_membase_reg(inst, X86_64_RBP,
offset + 8, reg);
}
}
}
}
}
return
inst;
}
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-64.inc"
#undef JIT_INCLUDE_RULES
default
:
{
fprintf
(stderr,
"TODO(%x) at %s, %d\n"
,
(
int
)(insn->opcode), __FILE__, (
int
)__LINE__);
}
break
;
}
}
void
_jit_fix_call_stack(jit_param_passing_t *passing)
{
if
((passing->stack_size & 0x0f) != 0)
{
passing->stack_size = (passing->stack_size + 0x0f) & ~((jit_nint)0x0f);
passing->stack_pad = 1;
}
}
#ifndef JIT_USE_PARAM_AREA
int
_jit_setup_call_stack(jit_function_t func, jit_param_passing_t *passing)
{
if
(passing->stack_pad)
{
int
current;
jit_value_t pad_value;
pad_value = jit_value_create_nint_constant(func, jit_type_nint, 0);
if
(!pad_value)
{
return
0;
}
for
(current = 0; current < passing->stack_pad; ++current)
{
if
(!jit_insn_push(func, pad_value))
{
return
0;
}
}
}
return
1;
}
#endif /* !JIT_USE_PARAM_AREA */
static
int
push_param(jit_function_t func, _jit_param_t *param, jit_type_t type)
{
if
(is_struct_or_union(type) && !is_struct_or_union(param->value->type))
{
jit_value_t value;
if
(!(value = jit_insn_address_of(func, param->value)))
{
return
0;
}
#ifdef JIT_USE_PARAM_AREA
if
(!jit_insn_set_param_ptr(func, value, type, param->un.offset))
{
return
0;
}
#else
if
(!jit_insn_push_ptr(func, value, type))
{
return
0;
}
if
(param->stack_pad)
{
int
current;
jit_value_t pad_value;
pad_value = jit_value_create_nint_constant(func, jit_type_nint, 0);
if
(!pad_value)
{
return
0;
}
for
(current = 0; current < param->stack_pad; ++current)
{
if
(!jit_insn_push(func, pad_value))
{
return
0;
}
}
}
#endif
}
else
{
#ifdef JIT_USE_PARAM_AREA
if
(!jit_insn_set_param(func, param->value, param->un.offset))
{
return
0;
}
#else
if
(!jit_insn_push(func, param->value))
{
return
0;
}
if
(param->stack_pad)
{
int
current;
jit_value_t pad_value;
pad_value = jit_value_create_nint_constant(func, jit_type_nint, 0);
if
(!pad_value)
{
return
0;
}
for
(current = 0; current < param->stack_pad; ++current)
{
if
(!jit_insn_push(func, pad_value))
{
return
0;
}
}
}
#endif
}
return
1;
}
int
_jit_setup_reg_param(jit_function_t func, _jit_param_t *param,
jit_type_t param_type)
{
if
(param->arg_class == 1)
{
param->un.reg_info[0].value = param->value;
}
else
if
(param->arg_class == 2)
{
jit_nint size = jit_type_get_size(param_type);
jit_value_t value_ptr;
if
(!(value_ptr = jit_insn_address_of(func, param->value)))
{
return
0;
}
if
(IS_GENERAL_REG(param->un.reg_info[0].reg))
{
param->un.reg_info[0].value =
jit_insn_load_relative(func, value_ptr, 0, jit_type_long);
if
(!(param->un.reg_info[0].value))
{
return
0;
}
}
else
{
param->un.reg_info[0].value =
jit_insn_load_relative(func, value_ptr, 0, jit_type_float64);
if
(!(param->un.reg_info[0].value))
{
return
0;
}
}
size -= 8;
if
(IS_GENERAL_REG(param->un.reg_info[1].reg))
{
if
(size <= 4)
{
param->un.reg_info[1].value =
jit_insn_load_relative(func, value_ptr, 8, jit_type_int);
if
(!(param->un.reg_info[1].value))
{
return
0;
}
}
else
{
param->un.reg_info[1].value =
jit_insn_load_relative(func, value_ptr, 8, jit_type_long);
if
(!(param->un.reg_info[1].value))
{
return
0;
}
}
}
else
{
if
(size <= 4)
{
param->un.reg_info[1].value =
jit_insn_load_relative(func, value_ptr, 8, jit_type_float32);
if
(!(param->un.reg_info[1].value))
{
return
0;
}
}
else
{
param->un.reg_info[1].value =
jit_insn_load_relative(func, value_ptr, 8, jit_type_float64);
if
(!(param->un.reg_info[1].value))
{
return
0;
}
}
}
}
return
1;
}
int
_jit_flush_incoming_struct(jit_function_t func, _jit_param_t *param,
jit_type_t param_type)
{
if
(param->arg_class == 2)
{
jit_value_t address;
if
(!(address = jit_insn_address_of(func, param->value)))
{
return
0;
}
if
(!jit_insn_store_relative(func, address, 0, param->un.reg_info[0].value))
{
return
0;
}
if
(!jit_insn_store_relative(func, address, 8, param->un.reg_info[1].value))
{
return
0;
}
}
return
1;
}
int
_jit_setup_incoming_param(jit_function_t func, _jit_param_t *param,
jit_type_t param_type)
{
if
(param->arg_class == JIT_ARG_CLASS_STACK)
{
if
(!jit_insn_incoming_frame_posn
(func, param->value, param->un.offset))
{
return
0;
}
}
else
{
param_type = jit_type_remove_tags(param_type);
switch
(param_type->kind)
{
case
JIT_TYPE_STRUCT:
case
JIT_TYPE_UNION:
{
if
(param->arg_class == 1)
{
if
(!jit_insn_incoming_reg(func, param->value, param->un.reg_info[0].reg))
{
return
0;
}
}
else
{
jit_nuint size = jit_type_get_size(param_type);
if
(IS_GENERAL_REG(param->un.reg_info[0].reg))
{
if
(!(param->un.reg_info[0].value = jit_value_create(func, jit_type_long)))
{
return
0;
}
}
else
{
if
(!(param->un.reg_info[0].value = jit_value_create(func, jit_type_float64)))
{
return
0;
}
}
size -= 8;
if
(IS_GENERAL_REG(param->un.reg_info[1].reg))
{
if
(size <= 4)
{
if
(!(param->un.reg_info[1].value =
jit_value_create(func, jit_type_int)))
{
return
0;
}
}
else
{
if
(!(param->un.reg_info[1].value =
jit_value_create(func, jit_type_long)))
{
return
0;
}
}
}
else
{
if
(size <= 4)
{
if
(!(param->un.reg_info[1].value =
jit_value_create(func, jit_type_float32)))
{
return
0;
}
}
else
{
if
(!(param->un.reg_info[1].value =
jit_value_create(func, jit_type_float64)))
{
return
0;
}
}
}
if
(!jit_insn_incoming_reg(func,
param->un.reg_info[0].value,
param->un.reg_info[0].reg))
{
return
0;
}
if
(!jit_insn_incoming_reg(func,
param->un.reg_info[1].value,
param->un.reg_info[1].reg))
{
return
0;
}
}
}
break
;
default
:
{
if
(!jit_insn_incoming_reg(func, param->value, param->un.reg_info[0].reg))
{
return
0;
}
}
break
;
}
}
return
1;
}
int
_jit_setup_outgoing_param(jit_function_t func, _jit_param_t *param,
jit_type_t param_type)
{
if
(param->arg_class == JIT_ARG_CLASS_STACK)
{
if
(!push_param(func, param, param_type))
{
return
0;
}
}
else
{
if
(!jit_insn_outgoing_reg(func, param->un.reg_info[0].value,
param->un.reg_info[0].reg))
{
return
0;
}
if
(param->arg_class == 2)
{
if
(!jit_insn_outgoing_reg(func, param->un.reg_info[1].value,
param->un.reg_info[1].reg))
{
return
0;
}
}
}
return
1;
}
int
_jit_setup_return_value(jit_function_t func, jit_value_t return_value,
jit_type_t return_type)
{
if
(is_struct_or_union(return_type))
{
jit_param_passing_t passing;
_jit_param_t return_param;
if
(!_jit_classify_struct_return(&passing, &return_param, return_type))
{
return
0;
}
if
(return_param.arg_class == 1)
{
if
(!jit_insn_return_reg(func, return_value,
return_param.un.reg_info[0].reg))
{
return
0;
}
}
else
{
if
(!jit_insn_flush_struct(func, return_value))
{
return
0;
}
}
}
else
if
(return_type == jit_type_float32 ||
return_type == jit_type_float64)
{
if
(!jit_insn_return_reg(func, return_value, X86_64_REG_XMM0))
{
return
0;
}
}
else
if
(return_type == jit_type_nfloat)
{
if
(!jit_insn_return_reg(func, return_value, X86_64_REG_ST0))
{
return
0;
}
}
else
if
(return_type->kind != JIT_TYPE_VOID)
{
if
(!jit_insn_return_reg(func, return_value, X86_64_REG_RAX))
{
return
0;
}
}
return
1;
}
void
_jit_init_args(
int
abi, jit_param_passing_t *passing)
{
passing->max_word_regs = _jit_num_word_regs;
passing->word_regs = _jit_word_arg_regs;
passing->max_float_regs = _jit_num_float_regs;
passing->float_regs = _jit_float_arg_regs;
}
int
_jit_create_entry_insns(jit_function_t func)
{
jit_value_t value;
int
has_struct_return = 0;
jit_type_t signature = func->signature;
int
abi = jit_type_get_abi(signature);
unsigned
int
num_args = jit_type_num_params(signature);
jit_param_passing_t passing;
_jit_param_t param[num_args];
_jit_param_t nested_param;
_jit_param_t struct_return_param;
int
current_param;
func->builder->frame_size = JIT_INITIAL_FRAME_SIZE;
jit_memset(&passing, 0,
sizeof
(jit_param_passing_t));
jit_memset(param, 0,
sizeof
(_jit_param_t) * num_args);
passing.params = param;
passing.stack_size = JIT_INITIAL_STACK_OFFSET;
_jit_init_args(abi, &passing);
if
(func->nested_parent)
{
jit_memset(&nested_param, 0,
sizeof
(_jit_param_t));
if
(!(_jit_classify_param(&passing, &nested_param,
jit_type_void_ptr)))
{
return
0;
}
}
if
((value = jit_value_get_struct_pointer(func)))
{
jit_memset(&struct_return_param, 0,
sizeof
(_jit_param_t));
if
(!(_jit_classify_param(&passing, &struct_return_param,
jit_type_void_ptr)))
{
return
0;
}
struct_return_param.value = value;
has_struct_return = 1;
}
for
(current_param = 0; current_param < num_args; current_param++)
{
jit_type_t param_type;
param_type = jit_type_get_param(signature, current_param);
param_type = jit_type_normalize(param_type);
if
(!(_jit_classify_param(&passing, &(passing.params[current_param]),
param_type)))
{
return
0;
}
}
for
(current_param = 0; current_param < num_args; current_param++)
{
jit_type_t param_type;
param_type = jit_type_get_param(signature, current_param);
if
(!(param[current_param].value))
{
if
(!(param[current_param].value = jit_value_get_param(func, current_param)))
{
return
0;
}
}
if
(!_jit_setup_incoming_param(func, &(param[current_param]), param_type))
{
return
0;
}
}
if
(has_struct_return)
{
if
(!_jit_setup_incoming_param(func, &struct_return_param, jit_type_void_ptr))
{
return
0;
}
}
for
(current_param = 0; current_param < num_args; current_param++)
{
if
(param[current_param].arg_class != JIT_ARG_CLASS_STACK)
{
jit_type_t param_type;
param_type = jit_type_get_param(signature, current_param);
if
(!_jit_flush_incoming_struct(func, &(param[current_param]),
param_type))
{
return
0;
}
}
}
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,
int
flags)
{
int
abi = jit_type_get_abi(signature);
jit_type_t return_type;
jit_value_t value;
jit_value_t return_ptr;
int
current_param;
jit_param_passing_t passing;
_jit_param_t param[num_args];
_jit_param_t nested_param;
_jit_param_t struct_return_param;
jit_memset(&passing, 0,
sizeof
(jit_param_passing_t));
jit_memset(param, 0,
sizeof
(_jit_param_t) * num_args);
passing.params = param;
passing.stack_size = 0;
_jit_init_args(abi, &passing);
if
(is_nested)
{
jit_memset(&nested_param, 0,
sizeof
(_jit_param_t));
if
(!(_jit_classify_param(&passing, &nested_param,
jit_type_void_ptr)))
{
return
0;
}
}
return_type = jit_type_get_return(signature);
if
(jit_type_return_via_pointer(return_type))
{
value = jit_value_create(func, return_type);
if
(!value)
{
return
0;
}
*struct_return = value;
return_ptr = jit_insn_address_of(func, value);
if
(!return_ptr)
{
return
0;
}
jit_memset(&struct_return_param, 0,
sizeof
(_jit_param_t));
struct_return_param.value = return_ptr;
if
(!(_jit_classify_param(&passing, &struct_return_param,
jit_type_void_ptr)))
{
return
0;
}
}
else
{
*struct_return = 0;
return_ptr = 0;
}
for
(current_param = 0; current_param < num_args; current_param++)
{
jit_type_t param_type;
param_type = jit_type_get_param(signature, current_param);
param_type = jit_type_normalize(param_type);
if
(!(_jit_classify_param(&passing, &(passing.params[current_param]),
param_type)))
{
return
0;
}
passing.params[current_param].value = args[current_param];
}
_jit_fix_call_stack(&passing);
#ifdef JIT_USE_PARAM_AREA
if
(passing.stack_size > func->builder->param_area_size)
{
func->builder->param_area_size = passing.stack_size;
}
#else
if
(!jit_insn_flush_defer_pop(func, 32 - passing.stack_size))
{
return
0;
}
if
(!_jit_setup_call_stack(func, &passing))
{
return
0;
}
#endif
current_param = num_args;
while
(current_param > 0)
{
--current_param;
if
(param[current_param].arg_class == JIT_ARG_CLASS_STACK)
{
jit_type_t param_type;
param_type = jit_type_get_param(signature, current_param);
if
(!_jit_setup_outgoing_param(func, &(param[current_param]), param_type))
{
return
0;
}
}
}
if
(return_ptr)
{
if
(struct_return_param.arg_class == JIT_ARG_CLASS_STACK)
{
if
(!_jit_setup_outgoing_param(func, &struct_return_param,
jit_type_void_ptr))
{
return
0;
}
}
}
current_param = num_args;
while
(current_param > 0)
{
--current_param;
if
(param[current_param].arg_class != JIT_ARG_CLASS_STACK)
{
jit_type_t param_type;
param_type = jit_type_get_param(signature, current_param);
if
(!_jit_setup_reg_param(func, &(param[current_param]), param_type))
{
return
0;
}
}
}
if
(return_ptr)
{
if
(struct_return_param.arg_class != JIT_ARG_CLASS_STACK)
{
if
(!_jit_setup_reg_param(func, &struct_return_param,
jit_type_void_ptr))
{
return
0;
}
}
}
current_param = num_args;
while
(current_param > 0)
{
--current_param;
if
(param[current_param].arg_class != JIT_ARG_CLASS_STACK)
{
jit_type_t param_type;
param_type = jit_type_get_param(signature, current_param);
if
(!_jit_setup_outgoing_param(func, &(param[current_param]),
param_type))
{
return
0;
}
}
}
if
(return_ptr)
{
if
(struct_return_param.arg_class != JIT_ARG_CLASS_STACK)
{
if
(!_jit_setup_outgoing_param(func, &struct_return_param,
jit_type_void_ptr))
{
return
0;
}
}
}
return
1;
}
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_type_t return_type;
int
ptr_return;
#ifndef JIT_USE_PARAM_AREA
int
abi = jit_type_get_abi(signature);
int
current_param;
jit_param_passing_t passing;
_jit_param_t param[num_args];
_jit_param_t nested_param;
_jit_param_t struct_return_param;
#endif /* !JIT_USE_PARAM_AREA */
return_type = jit_type_normalize(jit_type_get_return(signature));
ptr_return = jit_type_return_via_pointer(return_type);
#ifndef JIT_USE_PARAM_AREA
jit_memset(&passing, 0,
sizeof
(jit_param_passing_t));
jit_memset(param, 0,
sizeof
(_jit_param_t) * num_args);
passing.params = param;
passing.stack_size = 0;
_jit_init_args(abi, &passing);
if
(is_nested)
{
jit_memset(&nested_param, 0,
sizeof
(_jit_param_t));
if
(!(_jit_classify_param(&passing, &nested_param,
jit_type_void_ptr)))
{
return
0;
}
}
if
(ptr_return)
{
jit_memset(&struct_return_param, 0,
sizeof
(_jit_param_t));
if
(!(_jit_classify_param(&passing, &struct_return_param,
jit_type_void_ptr)))
{
return
0;
}
}
for
(current_param = 0; current_param < num_args; current_param++)
{
jit_type_t param_type;
param_type = jit_type_get_param(signature, current_param);
param_type = jit_type_normalize(param_type);
if
(!(_jit_classify_param(&passing, &(passing.params[current_param]),
param_type)))
{
return
0;
}
}
_jit_fix_call_stack(&passing);
if
(passing.stack_size > 0)
{
if
(!jit_insn_defer_pop_stack(func, passing.stack_size))
{
return
0;
}
}
#endif /* !JIT_USE_PARAM_AREA */
if
(!return_value || ptr_return)
{
return
1;
}
if
(!_jit_setup_return_value(func, return_value, return_type))
{
return
0;
}
return
1;
}
#endif /* JIT_BACKEND_X86_64 */