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.
969 lines
23 KiB
969 lines
23 KiB
/*
|
|
* jit-rules-arm.c - Rules that define the characteristics of the ARM.
|
|
*
|
|
* 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_ARM)
|
|
|
|
#include "jit-gen-arm.h"
|
|
#include "jit-reg-alloc.h"
|
|
#include "jit-setjmp.h"
|
|
#include <stdio.h>
|
|
|
|
/*
|
|
* Round a size up to a multiple of the stack word size.
|
|
*/
|
|
#define ROUND_STACK(size) \
|
|
(((size) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
|
|
|
|
/*
|
|
* Load the instruction pointer from the generation context.
|
|
*/
|
|
#define jit_gen_load_inst_ptr(gen,inst) \
|
|
do { \
|
|
arm_inst_buf_init((inst), (gen)->posn.ptr, (gen)->posn.limit); \
|
|
} while (0)
|
|
|
|
/*
|
|
* Save the instruction pointer back to the generation context.
|
|
*/
|
|
#define jit_gen_save_inst_ptr(gen,inst) \
|
|
do { \
|
|
(gen)->posn.ptr = (unsigned char *)arm_inst_get_posn(inst); \
|
|
} while (0)
|
|
|
|
void _jit_init_backend(void)
|
|
{
|
|
/* Nothing to do here */
|
|
}
|
|
|
|
void _jit_gen_get_elf_info(jit_elf_info_t *info)
|
|
{
|
|
info->machine = 40; /* EM_ARM */
|
|
info->abi = 0; /* ELFOSABI_SYSV */
|
|
info->abi_version = 0;
|
|
}
|
|
|
|
/*
|
|
* Flush the contents of the constant pool.
|
|
*/
|
|
static void flush_constants(jit_gencode_t gen, int after_epilog)
|
|
{
|
|
arm_inst_buf inst;
|
|
arm_inst_word *patch;
|
|
arm_inst_word *current;
|
|
arm_inst_word *fixup;
|
|
int index, value, offset;
|
|
|
|
/* Bail out if there are no constants to flush */
|
|
if(!(gen->num_constants))
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Initialize the cache output pointer */
|
|
jit_gen_load_inst_ptr(gen, inst);
|
|
|
|
/* Jump over the constant pool if it is being output inline */
|
|
if(!after_epilog)
|
|
{
|
|
patch = arm_inst_get_posn(inst);
|
|
arm_jump_imm(inst, 0);
|
|
}
|
|
else
|
|
{
|
|
patch = 0;
|
|
}
|
|
|
|
/* Align the constant pool, if requested */
|
|
if(gen->align_constants && (((int)arm_inst_get_posn(inst)) & 7) != 0)
|
|
{
|
|
arm_inst_add(inst, 0);
|
|
}
|
|
|
|
/* Output the constant values and apply the necessary fixups */
|
|
for(index = 0; index < gen->num_constants; ++index)
|
|
{
|
|
current = arm_inst_get_posn(inst);
|
|
arm_inst_add(inst, gen->constants[index]);
|
|
fixup = gen->fixup_constants[index];
|
|
while(fixup != 0)
|
|
{
|
|
if((*fixup & 0x0F000000) == 0x05000000)
|
|
{
|
|
/* Word constant fixup */
|
|
value = *fixup & 0x0FFF;
|
|
offset = ((arm_inst_get_posn(inst) - 1 - fixup) * 4) - 8;
|
|
*fixup = ((*fixup & ~0x0FFF) | offset);
|
|
}
|
|
else
|
|
{
|
|
/* Floating-point constant fixup */
|
|
value = (*fixup & 0x00FF) * 4;
|
|
offset = ((arm_inst_get_posn(inst) - 1 - fixup) * 4) - 8;
|
|
*fixup = ((*fixup & ~0x00FF) | (offset / 4));
|
|
}
|
|
if(value)
|
|
{
|
|
fixup -= value;
|
|
}
|
|
else
|
|
{
|
|
fixup = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Backpatch the jump if necessary */
|
|
if(!after_epilog)
|
|
{
|
|
arm_patch(inst, patch, arm_inst_get_posn(inst));
|
|
}
|
|
|
|
/* Flush the pool state and restart */
|
|
gen->num_constants = 0;
|
|
gen->align_constants = 0;
|
|
gen->first_constant_use = 0;
|
|
jit_gen_save_inst_ptr(gen, inst);
|
|
}
|
|
|
|
/*
|
|
* Perform a constant pool flush if we are too far from the starting point.
|
|
*/
|
|
static int flush_if_too_far(jit_gencode_t gen)
|
|
{
|
|
if(gen->first_constant_use &&
|
|
(((arm_inst_word *)(gen->posn.ptr)) -
|
|
((arm_inst_word *)(gen->first_constant_use))) >= 100)
|
|
{
|
|
flush_constants(gen, 0);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add a fixup for a particular constant pool entry.
|
|
*/
|
|
static void add_constant_fixup
|
|
(jit_gencode_t gen, int index, arm_inst_word *fixup)
|
|
{
|
|
arm_inst_word *prev;
|
|
int value;
|
|
if(((unsigned char *)fixup) >= gen->posn.limit)
|
|
{
|
|
/* The instruction buffer is full, so don't record this fixup */
|
|
return;
|
|
}
|
|
prev = gen->fixup_constants[index];
|
|
if(prev)
|
|
{
|
|
value = fixup - prev;
|
|
}
|
|
else
|
|
{
|
|
value = 0;
|
|
}
|
|
if((*fixup & 0x0F000000) == 0x05000000)
|
|
{
|
|
*fixup = ((*fixup & ~0x0FFF) | value);
|
|
}
|
|
else
|
|
{
|
|
*fixup = ((*fixup & ~0x00FF) | (value / 4));
|
|
}
|
|
gen->fixup_constants[index] = fixup;
|
|
if(!(gen->first_constant_use))
|
|
{
|
|
gen->first_constant_use = fixup;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add an immediate value to the constant pool. The constant
|
|
* is loaded from the instruction at "fixup".
|
|
*/
|
|
static void add_constant(jit_gencode_t gen, int value, arm_inst_word *fixup)
|
|
{
|
|
int index;
|
|
|
|
/* Search the constant pool for an existing copy of the value */
|
|
for(index = 0; index < gen->num_constants; ++index)
|
|
{
|
|
if(gen->constants[index] == value)
|
|
{
|
|
add_constant_fixup(gen, index, fixup);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Flush the constant pool if there is insufficient space */
|
|
if(gen->num_constants >= JIT_ARM_MAX_CONSTANTS)
|
|
{
|
|
flush_constants(gen, 0);
|
|
}
|
|
|
|
/* Add the constant value to the pool */
|
|
gen->constants[gen->num_constants] = value;
|
|
gen->fixup_constants[gen->num_constants] = 0;
|
|
++(gen->num_constants);
|
|
add_constant_fixup(gen, gen->num_constants - 1, fixup);
|
|
}
|
|
|
|
/*
|
|
* Add a double-word immedite value to the constant pool.
|
|
*/
|
|
static void add_constant_dword
|
|
(jit_gencode_t gen, int value1, int value2, arm_inst_word *fixup, int align)
|
|
{
|
|
int index;
|
|
|
|
/* Make sure that the constant pool is properly aligned when output */
|
|
if(align)
|
|
{
|
|
gen->align_constants = 1;
|
|
}
|
|
|
|
/* Search the constant pool for an existing copy of the value */
|
|
for(index = 0; index < (gen->num_constants - 1); ++index)
|
|
{
|
|
if(gen->constants[index] == value1 &&
|
|
gen->constants[index + 1] == value2)
|
|
{
|
|
if(!align || (index % 2) == 0)
|
|
{
|
|
add_constant_fixup(gen, index, fixup);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Flush the constant pool if there is insufficient space */
|
|
if(gen->num_constants >= (JIT_ARM_MAX_CONSTANTS - 1))
|
|
{
|
|
flush_constants(gen, 0);
|
|
}
|
|
|
|
/* Align the constant pool on a 64-bit boundary if necessary */
|
|
if(align && (gen->num_constants % 2) != 0)
|
|
{
|
|
gen->constants[gen->num_constants] = 0;
|
|
gen->fixup_constants[gen->num_constants] = 0;
|
|
++(gen->num_constants);
|
|
}
|
|
|
|
/* Add the double word constant value to the pool */
|
|
gen->constants[gen->num_constants] = value1;
|
|
gen->fixup_constants[gen->num_constants] = 0;
|
|
gen->constants[gen->num_constants] = value2;
|
|
gen->fixup_constants[gen->num_constants] = 0;
|
|
gen->num_constants += 2;
|
|
add_constant_fixup(gen, gen->num_constants - 2, fixup);
|
|
}
|
|
|
|
/*
|
|
* Load an immediate value into a word register. If the value is
|
|
* complicated, then add an entry to the constant pool.
|
|
*/
|
|
static void mov_reg_imm
|
|
(jit_gencode_t gen, arm_inst_buf *inst, int reg, int value)
|
|
{
|
|
arm_inst_word *fixup;
|
|
|
|
/* Bail out if the value is not complex enough to need a pool entry */
|
|
if(!arm_is_complex_imm(value))
|
|
{
|
|
arm_mov_reg_imm(*inst, reg, value);
|
|
return;
|
|
}
|
|
|
|
/* Output a placeholder to load the value later */
|
|
fixup = arm_inst_get_posn(*inst);
|
|
arm_load_membase(*inst, reg, ARM_PC, 0);
|
|
|
|
/* Add the constant to the pool, which may cause a flush */
|
|
jit_gen_save_inst_ptr(gen, *inst);
|
|
add_constant(gen, value, fixup);
|
|
jit_gen_load_inst_ptr(gen, *inst);
|
|
}
|
|
|
|
/*
|
|
* Load a float32 immediate value into a float register. If the value is
|
|
* complicated, then add an entry to the constant pool.
|
|
*/
|
|
static void mov_freg_imm_32
|
|
(jit_gencode_t gen, arm_inst_buf *inst, int reg, int value)
|
|
{
|
|
arm_inst_word *fixup;
|
|
|
|
/* Output a placeholder to load the value later */
|
|
fixup = arm_inst_get_posn(*inst);
|
|
arm_load_membase_float32(*inst, reg, ARM_PC, 0);
|
|
|
|
/* Add the constant to the pool, which may cause a flush */
|
|
jit_gen_save_inst_ptr(gen, *inst);
|
|
add_constant(gen, value, fixup);
|
|
jit_gen_load_inst_ptr(gen, *inst);
|
|
}
|
|
|
|
/*
|
|
* Load a float64 immediate value into a float register. If the value is
|
|
* complicated, then add an entry to the constant pool.
|
|
*/
|
|
static void mov_freg_imm_64
|
|
(jit_gencode_t gen, arm_inst_buf *inst, int reg, int value1, int value2)
|
|
{
|
|
arm_inst_word *fixup;
|
|
|
|
/* Output a placeholder to load the value later */
|
|
fixup = arm_inst_get_posn(*inst);
|
|
arm_load_membase_float64(*inst, reg, ARM_PC, 0);
|
|
|
|
/* Add the constant to the pool, which may cause a flush */
|
|
jit_gen_save_inst_ptr(gen, *inst);
|
|
add_constant_dword(gen, value1, value2, fixup, 1);
|
|
jit_gen_load_inst_ptr(gen, *inst);
|
|
}
|
|
|
|
int _jit_setup_indirect_pointer(jit_function_t func, jit_value_t value)
|
|
{
|
|
return jit_insn_outgoing_reg(func, value, ARM_WORK);
|
|
}
|
|
|
|
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;
|
|
|
|
/* Bail out now if we don't need to worry about return values */
|
|
return_type = jit_type_normalize(jit_type_get_return(signature));
|
|
ptr_return = jit_type_return_via_pointer(return_type);
|
|
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;
|
|
}
|
|
}
|
|
#ifdef JIT_ARM_HAS_FLOAT_REGS
|
|
else if(return_type->kind == JIT_TYPE_FLOAT32 ||
|
|
return_type->kind == JIT_TYPE_FLOAT64 ||
|
|
return_type->kind == JIT_TYPE_NFLOAT)
|
|
{
|
|
if(!jit_insn_return_reg(func, return_value, 16 /* f0 */))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
else if(return_type->kind != JIT_TYPE_VOID)
|
|
{
|
|
if(!jit_insn_return_reg(func, return_value, 0 /* r0 */))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Everything is back where it needs to be */
|
|
return 1;
|
|
}
|
|
|
|
int _jit_opcode_is_supported(int opcode)
|
|
{
|
|
switch(opcode)
|
|
{
|
|
#define JIT_INCLUDE_SUPPORTED
|
|
#include "jit-rules-arm.slc"
|
|
#undef JIT_INCLUDE_SUPPORTED
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void *_jit_gen_prolog(jit_gencode_t gen, jit_function_t func, void *buf)
|
|
{
|
|
unsigned int prolog[JIT_PROLOG_SIZE / sizeof(int)];
|
|
arm_inst_buf inst;
|
|
int reg, regset;
|
|
unsigned int saved;
|
|
unsigned int frame_size;
|
|
|
|
/* Initialize the instruction buffer */
|
|
arm_inst_buf_init(inst, prolog, prolog + JIT_PROLOG_SIZE / sizeof(int));
|
|
|
|
/* Determine which registers need to be preserved */
|
|
regset = 0;
|
|
saved = 0;
|
|
for(reg = 0; reg <= 15; ++reg)
|
|
{
|
|
if(jit_reg_is_used(gen->touched, reg) &&
|
|
(_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
|
|
{
|
|
regset |= (1 << reg);
|
|
saved += sizeof(void *);
|
|
}
|
|
}
|
|
|
|
/* Setup the frame, pushing all the callee-save registers */
|
|
arm_setup_frame(inst, regset);
|
|
|
|
/* Allocate space for the local variable frame. Subtract off
|
|
the space for the registers that we just saved. The pc, lr,
|
|
and fp registers are always saved, so account for them too */
|
|
frame_size = func->builder->frame_size - (saved + 3 * sizeof(void *));
|
|
frame_size += (unsigned int)(func->builder->param_area_size);
|
|
if(frame_size > 0)
|
|
{
|
|
arm_alu_reg_imm(inst, ARM_SUB, ARM_SP, ARM_SP, frame_size);
|
|
}
|
|
|
|
/* Copy the prolog into place and return the adjusted entry position */
|
|
reg = (int)((arm_inst_get_posn(inst) - prolog) * sizeof(unsigned int));
|
|
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)
|
|
{
|
|
int reg, regset;
|
|
arm_inst_buf inst;
|
|
void **fixup;
|
|
void **next;
|
|
jit_nint offset;
|
|
|
|
/* Initialize the instruction buffer */
|
|
jit_gen_load_inst_ptr(gen, inst);
|
|
|
|
/* Determine which registers need to be restored when we return */
|
|
regset = 0;
|
|
for(reg = 0; reg <= 15; ++reg)
|
|
{
|
|
if(jit_reg_is_used(gen->touched, reg) &&
|
|
(_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
|
|
{
|
|
regset |= (1 << reg);
|
|
}
|
|
}
|
|
|
|
/* Apply fixups for blocks that jump to the epilog */
|
|
fixup = (void **)(gen->epilog_fixup);
|
|
while(fixup != 0)
|
|
{
|
|
offset = (((jit_nint)(fixup[0])) & 0x00FFFFFF) << 2;
|
|
if(!offset)
|
|
{
|
|
next = 0;
|
|
}
|
|
else
|
|
{
|
|
next = (void **)(((unsigned char *)fixup) - offset);
|
|
}
|
|
arm_patch(inst, fixup, arm_inst_get_posn(inst));
|
|
fixup = next;
|
|
}
|
|
gen->epilog_fixup = 0;
|
|
|
|
/* Pop the local stack frame and return */
|
|
arm_pop_frame(inst, regset);
|
|
jit_gen_save_inst_ptr(gen, inst);
|
|
|
|
/* Flush the remainder of the constant pool */
|
|
flush_constants(gen, 1);
|
|
}
|
|
|
|
void *_jit_gen_redirector(jit_gencode_t gen, jit_function_t func)
|
|
{
|
|
void *ptr, *entry;
|
|
arm_inst_buf inst;
|
|
jit_gen_load_inst_ptr(gen, inst);
|
|
ptr = (void *)&(func->entry_point);
|
|
entry = gen->posn.ptr;
|
|
arm_load_membase(inst, ARM_WORK, ARM_PC, 0);
|
|
arm_load_membase(inst, ARM_PC, ARM_WORK, 0);
|
|
arm_inst_add(inst, (unsigned int)ptr);
|
|
jit_gen_save_inst_ptr(gen, inst);
|
|
return entry;
|
|
}
|
|
|
|
/*
|
|
* Setup or teardown the ARM code output process.
|
|
*/
|
|
#define jit_cache_setup_output(needed) \
|
|
arm_inst_buf inst; \
|
|
jit_gen_load_inst_ptr(gen, inst)
|
|
#define jit_cache_end_output() \
|
|
jit_gen_save_inst_ptr(gen, 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(32);
|
|
if(flush_if_too_far(gen))
|
|
{
|
|
jit_gen_load_inst_ptr(gen, inst);
|
|
}
|
|
|
|
/* Output an appropriate instruction to spill the value */
|
|
if(value->has_global_register)
|
|
{
|
|
arm_mov_reg_reg(inst, _jit_reg_info[value->global_reg].cpu_reg,
|
|
_jit_reg_info[reg].cpu_reg);
|
|
}
|
|
else
|
|
{
|
|
_jit_gen_fix_value(value);
|
|
offset = (int)(value->frame_offset);
|
|
if(reg < 16)
|
|
{
|
|
arm_store_membase(inst, reg, ARM_FP, offset);
|
|
if(other_reg != -1)
|
|
{
|
|
/* Spill the other word register in a pair */
|
|
offset += sizeof(void *);
|
|
arm_store_membase(inst, other_reg, ARM_FP, offset);
|
|
}
|
|
}
|
|
else if(jit_type_normalize(value->type)->kind == JIT_TYPE_FLOAT32)
|
|
{
|
|
arm_store_membase_float32(inst, reg - 16, ARM_FP, offset);
|
|
}
|
|
else
|
|
{
|
|
arm_store_membase_float64(inst, reg - 16, ARM_FP, offset);
|
|
}
|
|
}
|
|
|
|
/* 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 don't have to do anything to free ARM registers */
|
|
}
|
|
|
|
void _jit_gen_load_value
|
|
(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(32);
|
|
if(flush_if_too_far(gen))
|
|
{
|
|
jit_gen_load_inst_ptr(gen, inst);
|
|
}
|
|
|
|
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:
|
|
{
|
|
mov_reg_imm(gen, &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);
|
|
mov_reg_imm(gen, &inst, _jit_reg_info[reg].cpu_reg,
|
|
(jit_int)long_value);
|
|
mov_reg_imm(gen, &inst, _jit_reg_info[reg].cpu_reg + 1,
|
|
(jit_int)(long_value >> 32));
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_FLOAT32:
|
|
{
|
|
jit_float32 float32_value;
|
|
float32_value = jit_value_get_float32_constant(value);
|
|
if(reg < 16)
|
|
{
|
|
mov_reg_imm(gen, &inst, _jit_reg_info[reg].cpu_reg,
|
|
*((int *)&float32_value));
|
|
}
|
|
else
|
|
{
|
|
mov_freg_imm_32
|
|
(gen, &inst, _jit_reg_info[reg].cpu_reg,
|
|
*((int *)&float32_value));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_FLOAT64:
|
|
case JIT_TYPE_NFLOAT:
|
|
{
|
|
jit_float64 float64_value;
|
|
float64_value = jit_value_get_float64_constant(value);
|
|
if(reg < 16)
|
|
{
|
|
mov_reg_imm
|
|
(gen, &inst, _jit_reg_info[reg].cpu_reg,
|
|
((int *)&float64_value)[0]);
|
|
mov_reg_imm
|
|
(gen, &inst, _jit_reg_info[reg].cpu_reg + 1,
|
|
((int *)&float64_value)[1]);
|
|
}
|
|
else
|
|
{
|
|
mov_freg_imm_64
|
|
(gen, &inst, _jit_reg_info[reg].cpu_reg,
|
|
((int *)&float64_value)[0],
|
|
((int *)&float64_value)[1]);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if(value->has_global_register)
|
|
{
|
|
/* Load the value out of a global register */
|
|
arm_mov_reg_reg(inst, _jit_reg_info[reg].cpu_reg,
|
|
_jit_reg_info[value->global_reg].cpu_reg);
|
|
}
|
|
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:
|
|
{
|
|
arm_load_membase_sbyte(inst, _jit_reg_info[reg].cpu_reg,
|
|
ARM_FP, offset);
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_UBYTE:
|
|
{
|
|
arm_load_membase_byte(inst, _jit_reg_info[reg].cpu_reg,
|
|
ARM_FP, offset);
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_SHORT:
|
|
{
|
|
arm_load_membase_short(inst, _jit_reg_info[reg].cpu_reg,
|
|
ARM_FP, offset);
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_USHORT:
|
|
{
|
|
arm_load_membase_ushort(inst, _jit_reg_info[reg].cpu_reg,
|
|
ARM_FP, offset);
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_INT:
|
|
case JIT_TYPE_UINT:
|
|
{
|
|
arm_load_membase(inst, _jit_reg_info[reg].cpu_reg,
|
|
ARM_FP, offset);
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_LONG:
|
|
case JIT_TYPE_ULONG:
|
|
{
|
|
arm_load_membase(inst, _jit_reg_info[reg].cpu_reg,
|
|
ARM_FP, offset);
|
|
arm_load_membase(inst, _jit_reg_info[reg].cpu_reg + 1,
|
|
ARM_FP, offset + 4);
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_FLOAT32:
|
|
{
|
|
if(reg < 16)
|
|
{
|
|
arm_load_membase(inst, _jit_reg_info[reg].cpu_reg,
|
|
ARM_FP, offset);
|
|
}
|
|
else
|
|
{
|
|
arm_load_membase_float32
|
|
(inst, _jit_reg_info[reg].cpu_reg, ARM_FP, offset);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_FLOAT64:
|
|
case JIT_TYPE_NFLOAT:
|
|
{
|
|
if(reg < 16)
|
|
{
|
|
arm_load_membase(inst, _jit_reg_info[reg].cpu_reg,
|
|
ARM_FP, offset);
|
|
arm_load_membase(inst, _jit_reg_info[reg].cpu_reg + 1,
|
|
ARM_FP, offset + 4);
|
|
}
|
|
else
|
|
{
|
|
arm_load_membase_float64
|
|
(inst, _jit_reg_info[reg].cpu_reg, ARM_FP, offset);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* End the code output process */
|
|
jit_cache_end_output();
|
|
}
|
|
|
|
void _jit_gen_load_global(jit_gencode_t gen, jit_value_t value)
|
|
{
|
|
jit_cache_setup_output(32);
|
|
arm_load_membase(inst, _jit_reg_info[value->global_reg].cpu_reg,
|
|
ARM_FP, value->frame_offset);
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output a branch instruction.
|
|
*/
|
|
static void output_branch
|
|
(jit_function_t func, arm_inst_buf *inst, int cond, jit_insn_t insn)
|
|
{
|
|
jit_block_t block;
|
|
int offset;
|
|
block = jit_block_from_label(func, (jit_label_t)(insn->dest));
|
|
if(!block)
|
|
{
|
|
return;
|
|
}
|
|
if(arm_inst_get_posn(*inst) >= arm_inst_get_limit(*inst))
|
|
{
|
|
/* The buffer has overflowed, so don't worry about fixups */
|
|
return;
|
|
}
|
|
if(block->address)
|
|
{
|
|
/* We already know the address of the block */
|
|
arm_branch(*inst, cond, block->address);
|
|
}
|
|
else
|
|
{
|
|
/* Output a placeholder and record on the block's fixup list */
|
|
if(block->fixup_list)
|
|
{
|
|
offset = (int)(((unsigned char *)arm_inst_get_posn(*inst)) -
|
|
((unsigned char *)(block->fixup_list)));
|
|
}
|
|
else
|
|
{
|
|
offset = 0;
|
|
}
|
|
arm_branch_imm(*inst, cond, offset);
|
|
block->fixup_list = (void *)(arm_inst_get_posn(*inst) - 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Throw a builtin exception.
|
|
*/
|
|
static void throw_builtin
|
|
(arm_inst_buf *inst, jit_function_t func, int cond, int type)
|
|
{
|
|
arm_inst_word *patch;
|
|
|
|
/* Branch past the following code if "cond" is not true */
|
|
patch = arm_inst_get_posn(*inst);
|
|
arm_branch_imm(*inst, cond ^ 0x01, 0);
|
|
|
|
/* We need to update "catch_pc" if we have a "try" block */
|
|
if(func->builder->setjmp_value != 0)
|
|
{
|
|
_jit_gen_fix_value(func->builder->setjmp_value);
|
|
arm_mov_reg_reg(*inst, ARM_WORK, ARM_PC);
|
|
arm_store_membase(*inst, ARM_WORK, ARM_FP,
|
|
func->builder->setjmp_value->frame_offset +
|
|
jit_jmp_catch_pc_offset);
|
|
}
|
|
|
|
/* Push the exception type onto the stack */
|
|
arm_mov_reg_imm(*inst, ARM_WORK, type);
|
|
arm_push_reg(*inst, ARM_WORK);
|
|
|
|
/* Call the "jit_exception_builtin" function, which will never return */
|
|
arm_call(*inst, jit_exception_builtin);
|
|
|
|
/* Back-patch the previous branch instruction */
|
|
arm_patch(*inst, patch, arm_inst_get_posn(*inst));
|
|
}
|
|
|
|
/*
|
|
* Jump to the current function's epilog.
|
|
*/
|
|
static void jump_to_epilog
|
|
(jit_gencode_t gen, arm_inst_buf *inst, jit_block_t block)
|
|
{
|
|
int offset;
|
|
|
|
/* If the epilog is the next thing that we will output,
|
|
then fall through to the epilog directly */
|
|
block = block->next;
|
|
while(block != 0 && block->first_insn > block->last_insn)
|
|
{
|
|
block = block->next;
|
|
}
|
|
if(!block)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Bail out if the instruction buffer has overflowed */
|
|
if(arm_inst_get_posn(*inst) >= arm_inst_get_limit(*inst))
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Output a placeholder for the jump and add it to the fixup list */
|
|
if(gen->epilog_fixup)
|
|
{
|
|
offset = (int)(((unsigned char *)arm_inst_get_posn(*inst)) -
|
|
((unsigned char *)(gen->epilog_fixup)));
|
|
}
|
|
else
|
|
{
|
|
offset = 0;
|
|
}
|
|
arm_branch_imm(*inst, ARM_CC_AL, offset);
|
|
gen->epilog_fixup = (void *)(arm_inst_get_posn(*inst) - 1);
|
|
}
|
|
|
|
#define TODO() \
|
|
do { \
|
|
fprintf(stderr, "TODO at %s, %d\n", __FILE__, (int)__LINE__); \
|
|
} while (0)
|
|
|
|
void _jit_gen_insn(jit_gencode_t gen, jit_function_t func,
|
|
jit_block_t block, jit_insn_t insn)
|
|
{
|
|
flush_if_too_far(gen);
|
|
switch(insn->opcode)
|
|
{
|
|
#define JIT_INCLUDE_RULES
|
|
#include "jit-rules-arm.slc"
|
|
#undef JIT_INCLUDE_RULES
|
|
|
|
default:
|
|
{
|
|
fprintf(stderr, "TODO(%x) at %s, %d\n",
|
|
(int)(insn->opcode), __FILE__, (int)__LINE__);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void _jit_gen_start_block(jit_gencode_t gen, jit_block_t block)
|
|
{
|
|
void **fixup;
|
|
void **next;
|
|
jit_nint offset;
|
|
arm_inst_buf inst;
|
|
|
|
/* 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)
|
|
{
|
|
offset = (((jit_nint)(fixup[0])) & 0x00FFFFFF) << 2;
|
|
if(!offset)
|
|
{
|
|
next = 0;
|
|
}
|
|
else
|
|
{
|
|
next = (void **)(((unsigned char *)fixup) - offset);
|
|
}
|
|
jit_gen_load_inst_ptr(gen, inst);
|
|
arm_patch(inst, fixup, block->address);
|
|
fixup = next;
|
|
}
|
|
block->fixup_list = 0;
|
|
}
|
|
|
|
void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block)
|
|
{
|
|
/* Nothing to do here for ARM */
|
|
}
|
|
|
|
int _jit_gen_is_global_candidate(jit_type_t type)
|
|
{
|
|
switch(jit_type_remove_tags(type)->kind)
|
|
{
|
|
case JIT_TYPE_INT:
|
|
case JIT_TYPE_UINT:
|
|
case JIT_TYPE_NINT:
|
|
case JIT_TYPE_NUINT:
|
|
case JIT_TYPE_PTR:
|
|
case JIT_TYPE_SIGNATURE: return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif /* JIT_BACKEND_ARM */
|
|
|