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.
 
 
 
 
 
 

2068 lines
49 KiB

/*
* jit-rules-arm.ins - Instruction selector for ARM.
*
* Copyright (C) 2004 Southern Storm Software, Pty Ltd.
* Copyright (C) 2008 Michele Tartara <mikyt@users.sourceforge.net>
*
* This file is part of the libjit library.
*
* The libjit library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 2.1 of
* the License, or (at your option) any later version.
*
* The libjit library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the libjit library. If not, see
* <http://www.gnu.org/licenses/>.
*/
%inst_type arm_inst_buf
/*
* Register classes
*/
%regclass reg arm_reg
%regclass freg arm_freg
%regclass freg32 arm_freg32
%regclass freg64 arm_freg64
%lregclass lreg arm_lreg
/*
* Conversion opcodes.
*/
JIT_OP_TRUNC_SBYTE:
[reg] -> {
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 24);
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, 24);
}
JIT_OP_TRUNC_UBYTE:
[reg] -> {
arm_alu_reg_imm8(inst, ARM_AND, $1, $1, 0xFF);
}
JIT_OP_TRUNC_SHORT:
[reg] -> {
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 16);
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, 16);
}
JIT_OP_TRUNC_USHORT:
[reg] -> {
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 16);
arm_shift_reg_imm8(inst, ARM_SHR, $1, $1, 16);
}
JIT_OP_INT_TO_NFLOAT:
[=freg64, local, scratch freg32] -> {
//Load int from a local variable stored in memory
arm_load_membase_float(inst, $3, ARM_FP, $2, 0);
arm_convert_float_signed_integer_double(inst, $1, $3);
}
[=freg64, reg, scratch freg32] -> {
//The int value is in a register
arm_mov_float_reg(inst, $3, $2);
arm_convert_float_signed_integer_double(inst, $1, $3)
}
JIT_OP_NFLOAT_TO_FLOAT32:
[=freg32, freg64] -> {
arm_convert_float_single_double(inst, $1, $2);
}
JIT_OP_NFLOAT_TO_FLOAT64, JIT_OP_FLOAT64_TO_NFLOAT: copy
[freg64] -> {
/* Nothing to do: float64 and nfloat are the same thing on ARM linux. Just copy the value */
}
JIT_OP_FLOAT32_TO_NFLOAT:
[=freg64, freg32] -> {
arm_convert_float_double_single(inst, $1, $2);
}
/*
* Arithmetic opcodes.
*/
JIT_OP_IADD:
[reg, immu8] -> {
arm_alu_reg_imm8(inst, ARM_ADD, $1, $1, $2);
}
[reg, reg] -> {
arm_alu_reg_reg(inst, ARM_ADD, $1, $1, $2);
}
JIT_OP_ISUB:
[reg, immu8] -> {
arm_alu_reg_imm8(inst, ARM_SUB, $1, $1, $2);
}
[reg, reg] -> {
arm_alu_reg_reg(inst, ARM_SUB, $1, $1, $2);
}
JIT_OP_IMUL:
[reg, immu8] -> {
/* Handle special cases of immediate multiplies */
switch($2)
{
case 0:
{
arm_mov_reg_imm8(inst, $1, 0);
}
break;
case 1: break;
case 2:
{
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 1);
}
break;
case 4:
{
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 2);
}
break;
case 8:
{
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 3);
}
break;
case 16:
{
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 4);
}
break;
case 32:
{
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 5);
}
break;
case 64:
{
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 6);
}
break;
case 128:
{
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, 7);
}
break;
default:
{
arm_mov_reg_imm8(inst, ARM_WORK, $2);
arm_mul_reg_reg(inst, $1, $1, ARM_WORK);
}
break;
}
}
[reg, reg] -> {
if($1 != $2)
{
arm_mul_reg_reg(inst, $1, $1, $2);
}
else
{
/* Cannot use the same register for both arguments */
arm_mov_reg_reg(inst, ARM_WORK, $2);
arm_mul_reg_reg(inst, $1, $1, ARM_WORK);
}
}
JIT_OP_IDIV:
[any, immzero] -> {
throw_builtin(&inst, func, ARM_CC_AL, JIT_RESULT_DIVISION_BY_ZERO);
}
[reg, immu8, if("$2 == 1")] -> {
/* Division by 1. Return the value itself */
}
[reg, immu8, if("($2 > 0) && (((jit_nuint)$2) & (((jit_nuint)$2) - 1)) == 0")] -> {
/* Handle special cases of small immediate divides: divisions by positive powers of two */
/* NB: (n & (n-1)) == 0 if and only if n is a power of 2 */
/* Move the dividend in the work register, setting the codes (in order to know if it's positive or negative) */
arm_alu_cc_reg(inst, ARM_MOV, ARM_WORK, $1);
/* If the dividend is negative, make it positive (0-x = -x)*/
arm_alu_reg_imm8_cond(inst, ARM_RSB, $1, ARM_WORK, 0, ARM_CC_MI);
switch($2)
{
//Integer divide by shifting
case 2:
{
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, 1);
}
break;
case 4:
{
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, 2);
}
break;
case 8:
{
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, 3);
}
break;
case 16:
{
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, 4);
}
break;
case 32:
{
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, 5);
}
break;
case 64:
{
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, 6);
}
break;
case 128:
{
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, 7);
}
break;
}
/* If the dividend was negative, make it negative again (0-x = -x)*/
arm_alu_reg_imm8_cond(inst, ARM_RSB, $1, $1, 0, ARM_CC_MI);
}
[reg, imm, if("$2 == -1")] -> {
/* Dividing by -1 simply negates */
/*TODO: if the value to be divided by -1 is jit_min_int,
an exception (JIT_RESULT_ARITHMETIC) should probably be thrown */
arm_alu_reg(inst, ARM_MVN, $1, $1);
}
[reg, imm, clobber("r0", "r1")] -> {
/* Every other immediate division:
ARM does not have an integer division operation. It's emulated via software. */
//Put the dividend in the right position
if ($1 != ARM_R0)
arm_mov_reg_reg(inst, ARM_R0, $1);
//Put the divisor in the right position
mov_reg_imm(gen, &inst, ARM_R1, $2);
//Perform the division by calling a function from the runtime ABI
extern int __aeabi_idiv(int numerator, int denominator);
arm_call(inst, __aeabi_idiv);
if($1 != ARM_R0)
{
//Move the result back where it is expected to be
arm_mov_reg_reg(inst, $1, ARM_R0);
}
}
[reg, reg, scratch reg, clobber("r0", "r1")] -> {
/* Every division taking data from two registers:
ARM does not have an integer division operation. It's emulated via software. */
int dividend = $1;
int divisor = $2;
int scratch = $3;
//Put the dividend in the right position
if (dividend != ARM_R0)
{
if (divisor==ARM_R0)
{
//Prevent the divisor from being overwritten
if(dividend != ARM_R1)
{
//The place where the divisor should be is free. Move it there
arm_mov_reg_reg(inst, ARM_R1, divisor);
divisor=1;
}
else
{
/* The dividend is where the divisor should be.
We must use a scratch register to swap them */
arm_mov_reg_reg(inst, scratch, divisor);
divisor=scratch;
}
}
arm_mov_reg_reg(inst, ARM_R0, dividend);
}
if (divisor != ARM_R1)
{
//Put the divisor in the right position
arm_mov_reg_reg(inst, ARM_R1, divisor);
}
//Perform the division by calling a function from the runtime ABI
extern int __aeabi_idiv(int numerator, int denominator);
arm_call(inst, __aeabi_idiv);
//Move the result back where it is expected to be
if($1 != ARM_R0)
{
arm_mov_reg_reg(inst, $1, ARM_R0);
}
}
JIT_OP_INEG:
[reg] -> {
/* -x is the same as (0 - x) */
arm_alu_reg_imm8(inst, ARM_RSB, $1, $1, 0);
}
JIT_OP_LADD:
[lreg, lreg] -> {
arm_alu_cc_reg_reg(inst, ARM_ADD, $1, $1, $2);
arm_alu_reg_reg(inst, ARM_ADC, %1, %1, %2);
}
JIT_OP_LSUB:
[lreg, lreg] -> {
arm_alu_cc_reg_reg(inst, ARM_SUB, $1, $1, $2);
arm_alu_reg_reg(inst, ARM_SBC, %1, %1, %2);
}
JIT_OP_LNEG:
[lreg] -> {
arm_alu_reg(inst, ARM_MVN, $1, $1);
arm_alu_reg(inst, ARM_MVN, %1, %1);
arm_alu_cc_reg_imm8(inst, ARM_ADD, $1, $1, 1);
arm_alu_reg_imm8(inst, ARM_ADC, %1, %1, 0);
}
JIT_OP_FADD (JIT_ARM_HAS_VFP):
[freg32, freg32] -> {
arm_alu_freg_freg_32(inst, ARM_FADD, $1, $1, $2);
}
JIT_OP_FADD (JIT_ARM_HAS_FPA): /*binary*/
[freg, freg] -> {
arm_alu_freg_freg_32(inst, ARM_ADF, $1, $1, $2);
}
JIT_OP_FSUB (JIT_ARM_HAS_VFP):
[freg32, freg32] -> {
arm_alu_freg_freg_32(inst, ARM_FSUB, $1, $1, $2);
}
JIT_OP_FSUB (JIT_ARM_HAS_FPA): /*binary*/
[freg, freg] -> {
arm_alu_freg_freg_32(inst, ARM_SUF, $1, $1, $2);
}
JIT_OP_FMUL (JIT_ARM_HAS_VFP):
[freg32, freg32] -> {
arm_alu_freg_freg_32(inst, ARM_FMUL, $1, $1, $2);
}
JIT_OP_FMUL (JIT_ARM_HAS_FPA): /*binary*/
[freg, freg] -> {
arm_alu_freg_freg_32(inst, ARM_MUF, $1, $1, $2);
}
JIT_OP_FDIV (JIT_ARM_HAS_VFP):
[freg32, freg32] -> {
arm_alu_freg_freg_32(inst, ARM_FDIV, $1, $1, $2);
}
JIT_OP_FDIV (JIT_ARM_HAS_FPA): /*binary*/
[freg, freg] -> {
arm_alu_freg_freg_32(inst, ARM_DVF, $1, $1, $2);
}
JIT_OP_FNEG (JIT_ARM_HAS_VFP):
[freg32] -> {
arm_alu_freg_32(inst, ARM_MNF, $1, $1);
}
JIT_OP_FNEG (JIT_ARM_HAS_FPA): /*unary*/
[freg] -> {
arm_alu_freg_32(inst, ARM_MNF, $1, $1);
}
JIT_OP_DADD, JIT_OP_NFADD (JIT_ARM_HAS_VFP):
[freg64, freg64] -> {
arm_alu_freg_freg(inst, ARM_FADD, $1, $1, $2);
}
JIT_OP_DADD, JIT_OP_NFADD (JIT_ARM_HAS_FPA): /*binary*/
[freg, freg] -> {
arm_alu_freg_freg(inst, ARM_ADF, $1, $1, $2);
}
JIT_OP_DSUB, JIT_OP_NFSUB (JIT_ARM_HAS_VFP):
[freg64, freg64] -> {
arm_alu_freg_freg(inst, ARM_FSUB, $1, $1, $2);
}
JIT_OP_DSUB, JIT_OP_NFSUB (JIT_ARM_HAS_FPA): /*binary*/
[freg, freg] -> {
arm_alu_freg_freg(inst, ARM_SUF, $1, $1, $2);
}
JIT_OP_DMUL, JIT_OP_NFMUL (JIT_ARM_HAS_VFP):
[freg64, freg64] -> {
arm_alu_freg_freg(inst, ARM_FMUL, $1, $1, $2);
}
JIT_OP_DMUL, JIT_OP_NFMUL (JIT_ARM_HAS_FPA): /*binary*/
[freg, freg] -> {
arm_alu_freg_freg(inst, ARM_MUF, $1, $1, $2);
}
JIT_OP_DDIV, JIT_OP_NFDIV (JIT_ARM_HAS_VFP):
[freg64, freg64] -> {
arm_alu_freg_freg(inst, ARM_FDIV, $1, $1, $2);
}
JIT_OP_DDIV, JIT_OP_NFDIV (JIT_ARM_HAS_FPA): /*binary*/
[freg, freg] -> {
arm_alu_freg_freg(inst, ARM_DVF, $1, $1, $2);
}
JIT_OP_DNEG, JIT_OP_NFNEG (JIT_ARM_HAS_VFP):
[freg64] -> {
arm_alu_freg(inst, ARM_MNF, $1, $1);
}
JIT_OP_DNEG, JIT_OP_NFNEG (JIT_ARM_HAS_FPA): /*unary*/
[freg] -> {
arm_alu_freg(inst, ARM_MNF, $1, $1);
}
/*
* Bitwise opcodes.
*/
JIT_OP_IAND:
[reg, immu8] -> {
arm_alu_reg_imm8(inst, ARM_AND, $1, $1, $2);
}
[reg, reg] -> {
arm_alu_reg_reg(inst, ARM_AND, $1, $1, $2);
}
JIT_OP_IOR:
[reg, immu8] -> {
arm_alu_reg_imm8(inst, ARM_ORR, $1, $1, $2);
}
[reg, reg] -> {
arm_alu_reg_reg(inst, ARM_ORR, $1, $1, $2);
}
JIT_OP_IXOR:
[reg, immu8] -> {
arm_alu_reg_imm8(inst, ARM_EOR, $1, $1, $2);
}
[reg, reg] -> {
arm_alu_reg_reg(inst, ARM_EOR, $1, $1, $2);
}
JIT_OP_INOT:
[reg] -> {
/* MVN == "move not" */
arm_alu_reg(inst, ARM_MVN, $1, $1);
}
JIT_OP_ISHL:
[reg, imm] -> {
arm_shift_reg_imm8(inst, ARM_SHL, $1, $1, ($2 & 0x1F));
}
[reg, reg] -> {
arm_alu_reg_imm8(inst, ARM_AND, ARM_WORK, $2, 0x1F);
arm_shift_reg_reg(inst, ARM_SHL, $1, $1, ARM_WORK);
}
JIT_OP_ISHR:
[reg, imm] -> {
arm_shift_reg_imm8(inst, ARM_SAR, $1, $1, ($2 & 0x1F));
}
[reg, reg] -> {
arm_alu_reg_imm8(inst, ARM_AND, ARM_WORK, $2, 0x1F);
arm_shift_reg_reg(inst, ARM_SAR, $1, $1, ARM_WORK);
}
JIT_OP_ISHR_UN:
[reg, imm] -> {
arm_shift_reg_imm8(inst, ARM_SHR, $1, $1, ($2 & 0x1F));
}
[reg, reg] -> {
arm_alu_reg_imm8(inst, ARM_AND, ARM_WORK, $2, 0x1F);
arm_shift_reg_reg(inst, ARM_SHR, $1, $1, ARM_WORK);
}
JIT_OP_LAND:
[lreg, lreg] -> {
arm_alu_reg_reg(inst, ARM_AND, $1, $1, $2);
arm_alu_reg_reg(inst, ARM_AND, %1, %1, %2);
}
JIT_OP_LOR:
[lreg, lreg] -> {
arm_alu_reg_reg(inst, ARM_ORR, $1, $1, $2);
arm_alu_reg_reg(inst, ARM_ORR, %1, %1, %2);
}
JIT_OP_LXOR:
[lreg, lreg] -> {
arm_alu_reg_reg(inst, ARM_EOR, $1, $1, $2);
arm_alu_reg_reg(inst, ARM_EOR, %1, %1, %2);
}
JIT_OP_LNOT:
[lreg] -> {
arm_alu_reg(inst, ARM_MVN, $1, $1);
arm_alu_reg(inst, ARM_MVN, %1, %1);
}
/*
* Branch opcodes.
*/
JIT_OP_BR: branch /*spill_before*/
[] -> {
/* ARM_CC_AL == "always branch" */
output_branch(func, &inst, ARM_CC_AL, insn);
/* Flush the constant pool now, to minimize the probability that
it is accidentally flushed in the middle of a loop body */
jit_gen_save_inst_ptr(gen, inst);
flush_constants(gen, 1);
jit_gen_load_inst_ptr(gen, inst);
}
JIT_OP_BR_IFALSE: branch
[reg] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, 0);
output_branch(func, &inst, ARM_CC_EQ, insn);
}
JIT_OP_BR_ITRUE: branch
[reg] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, 0);
output_branch(func, &inst, ARM_CC_NE, insn);
}
JIT_OP_BR_IEQ: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_EQ, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_EQ, insn);
}
JIT_OP_BR_INE: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_NE, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_NE, insn);
}
JIT_OP_BR_ILT: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_LT, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_LT, insn);
}
JIT_OP_BR_ILT_UN: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_LT_UN, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_LT_UN, insn);
}
JIT_OP_BR_ILE: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_LE, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_LE, insn);
}
JIT_OP_BR_ILE_UN: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_LE_UN, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_LE_UN, insn);
}
JIT_OP_BR_IGT: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_GT, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_GT, insn);
}
JIT_OP_BR_IGT_UN: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_GT_UN, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_GT_UN, insn);
}
JIT_OP_BR_IGE: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_GE, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_GE, insn);
}
JIT_OP_BR_IGE_UN: branch
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_GE_UN, insn);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
output_branch(func, &inst, ARM_CC_GE_UN, insn);
}
/*
* Comparison opcodes.
*/
JIT_OP_ICMP:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GT);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LE);
arm_alu_reg_cond(inst, ARM_MVN, $1, $1, ARM_CC_LT);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GT);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LE);
arm_alu_reg_cond(inst, ARM_MVN, $1, $1, ARM_CC_LT);
}
JIT_OP_ICMP_UN:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GT_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LE_UN);
arm_alu_reg_cond(inst, ARM_MVN, $1, $1, ARM_CC_LT_UN);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GT_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LE_UN);
arm_alu_reg_cond(inst, ARM_MVN, $1, $1, ARM_CC_LT_UN);
}
JIT_OP_IEQ:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_EQ);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_NE);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_EQ);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_NE);
}
JIT_OP_INE:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_NE);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_EQ);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_NE);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_EQ);
}
JIT_OP_ILT:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_LT);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_GE);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_LT);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_GE);
}
JIT_OP_ILT_UN:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_LT_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_GE_UN);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_LT_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_GE_UN);
}
JIT_OP_ILE:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_LE);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_GT);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_LE);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_GT);
}
JIT_OP_ILE_UN:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_LE_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_GT_UN);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_LE_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_GT_UN);
}
JIT_OP_IGT:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GT);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LE);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GT);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LE);
}
JIT_OP_IGT_UN:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GT_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LE_UN);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GT_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LE_UN);
}
JIT_OP_IGE:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GE);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LT);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GE);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LT);
}
JIT_OP_IGE_UN:
[reg, immu8] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GE_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LT_UN);
}
[reg, reg] -> {
arm_test_reg_reg(inst, ARM_CMP, $1, $2);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 1, ARM_CC_GE_UN);
arm_alu_reg_imm8_cond(inst, ARM_MOV, $1, 0, 0, ARM_CC_LT_UN);
}
/*
* Pointer check opcodes.
*/
JIT_OP_CHECK_NULL: note
[reg] -> {
arm_test_reg_imm8(inst, ARM_CMP, $1, 0);
throw_builtin(&inst, func, ARM_CC_EQ, JIT_RESULT_NULL_REFERENCE);
}
/*
* Function calls.
*/
JIT_OP_CALL:
[] -> {
jit_function_t func = (jit_function_t)(insn->dest);
arm_call(inst, jit_function_to_closure(func));
}
JIT_OP_CALL_TAIL:
[] -> {
jit_function_t func = (jit_function_t)(insn->dest);
arm_pop_frame_tail(inst, 0);
arm_jump(inst, jit_function_to_closure(func));
}
JIT_OP_CALL_INDIRECT:
[] -> {
arm_mov_reg_reg((inst), ARM_LINK, ARM_PC);
arm_mov_reg_reg((inst), ARM_PC, ARM_WORK);
}
JIT_OP_CALL_VTABLE_PTR:
[] -> {
arm_mov_reg_reg((inst), ARM_LINK, ARM_PC);
arm_mov_reg_reg((inst), ARM_PC, ARM_WORK);
}
JIT_OP_CALL_EXTERNAL:
[] -> {
arm_call(inst, (void *)(insn->dest));
}
JIT_OP_RETURN:
[] -> {
jump_to_epilog(gen, &inst, block);
}
JIT_OP_RETURN_INT: /*unary_branch*/
[reg] -> {
int cpu_reg = $1;
if(cpu_reg != ARM_R0)
{
arm_mov_reg_reg(inst, ARM_R0, cpu_reg);
}
jump_to_epilog(gen, &inst, block);
}
JIT_OP_RETURN_LONG: /*unary_branch*/
[imm] -> {
mov_reg_imm(gen, &inst, ARM_R0, ((jit_int *)($1))[0]);
mov_reg_imm(gen, &inst, ARM_R1, ((jit_int *)($1))[1]);
jump_to_epilog(gen, &inst, block);
}
[local] -> {
arm_load_membase(inst, ARM_R0, ARM_FP, $1);
arm_load_membase(inst, ARM_R1, ARM_FP, $1 + 4);
jump_to_epilog(gen, &inst, block);
}
[lreg] -> {
if($1 != 0)
{
arm_mov_reg_reg(inst, ARM_R0, $1);
arm_mov_reg_reg(inst, ARM_R1, %1);
}
jump_to_epilog(gen, &inst, block);
}
JIT_OP_RETURN_FLOAT32 (JIT_ARM_HAS_VFP): branch
[freg32, clobber("r0")] -> {
arm_mov_reg_float(inst, ARM_R0, $1);
jump_to_epilog(gen, &inst, block);
}
JIT_OP_RETURN_FLOAT32 (JIT_ARM_HAS_FPA): branch
[freg] -> {
if($1 != 0)
{
arm_alu_freg_32(inst, ARM_MVF, ARM_F0, $1);
}
jump_to_epilog(gen, &inst, block);
}
JIT_OP_RETURN_FLOAT32 (!JIT_ARM_HAS_FLOAT_REGS): manual
[] -> {
arm_inst_buf inst;
_jit_regs_spill_all(gen);
_jit_gen_fix_value(insn->value1);
jit_gen_load_inst_ptr(gen, inst);
if(insn->value1->is_constant)
{
mov_reg_imm
(gen, &inst, ARM_R0, ((int *)(insn->value1->address))[0]);
}
else
{
arm_load_membase(inst, ARM_R0, ARM_FP, insn->value1->frame_offset);
}
jump_to_epilog(gen, &inst, block);
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_RETURN_FLOAT64, JIT_OP_RETURN_NFLOAT
(JIT_ARM_HAS_VFP): branch
[freg64, clobber("r0", "r1")] -> {
arm_mov_reg_reg_double(inst,ARM_R0,ARM_R1, $1);
jump_to_epilog(gen, &inst, block);
}
JIT_OP_RETURN_FLOAT64, JIT_OP_RETURN_NFLOAT (JIT_ARM_HAS_FPA): branch
[freg] -> {
if($1 != 0)
{
arm_alu_freg(inst, ARM_MVF, ARM_F0, $1);
}
jump_to_epilog(gen, &inst, block);
}
JIT_OP_RETURN_FLOAT64, JIT_OP_RETURN_NFLOAT (!JIT_ARM_HAS_FLOAT_REGS): manual
[] -> {
arm_inst_buf inst;
_jit_regs_spill_all(gen);
_jit_gen_fix_value(insn->value1);
jit_gen_load_inst_ptr(gen, inst);
if(insn->value1->is_constant)
{
mov_reg_imm
(gen, &inst, ARM_R0, ((int *)(insn->value1->address))[0]);
mov_reg_imm
(gen, &inst, ARM_R1, ((int *)(insn->value1->address))[1]);
}
else
{
arm_load_membase(inst, ARM_R0, ARM_FP, insn->value1->frame_offset);
arm_load_membase(inst, ARM_R1, ARM_FP,
insn->value1->frame_offset + 4);
}
jump_to_epilog(gen, &inst, block);
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_RETURN_SMALL_STRUCT: note
[reg, imm, clobber("r0", "r1")] -> {
//$1: address of the struct to be returned
//$2: size of the struct to be returned
//Prevent the accidental overwriting of the address
int temp_reg = $1;
if(temp_reg < 3)
{
arm_mov_reg_reg(inst, ARM_WORK, temp_reg);
temp_reg = ARM_WORK;
}
//Copy the struct to the return register in a way that's appropriate to its size
switch($2)
{
case 1:
arm_load_membase_byte(inst, ARM_R0, temp_reg, 0);
break;
case 2:
arm_load_membase_ushort(inst, ARM_R0, temp_reg, 0);
break;
case 3:
arm_load_membase_ushort(inst, ARM_R0, temp_reg, 0);
arm_load_membase_byte(inst, ARM_R1, temp_reg, 2);
arm_shift_reg_imm8(inst, ARM_SHL, ARM_R1, ARM_R1, 16);
arm_alu_reg_reg(inst, ARM_ORR, ARM_R0, ARM_R0, ARM_R1);
break;
case 4:
arm_load_membase(inst, ARM_R0, temp_reg, 0);
break;
/*TODO: is this the right way to return a struct > 4 bytes?
* Or should it be returned by address? Look at the Procedure Call Standard!
*/
case 5:
arm_load_membase(inst, ARM_R0, temp_reg, 0);
arm_load_membase_byte(inst, ARM_R1, temp_reg, 4);
break;
case 6:
arm_load_membase(inst, ARM_R0, temp_reg, 0);
arm_load_membase_ushort(inst, ARM_R1, temp_reg, 4);
break;
case 7:
arm_load_membase(inst, ARM_R0, temp_reg, 0);
arm_load_membase_ushort(inst, ARM_R1, temp_reg, 4);
arm_load_membase_byte(inst, ARM_R2, temp_reg, 6);
arm_shift_reg_imm8(inst, ARM_SHL, ARM_R2, ARM_R2, 16);
arm_alu_reg_reg(inst, ARM_ORR, ARM_R1, ARM_R1, ARM_R2);
break;
case 8:
arm_load_membase(inst, ARM_R0, temp_reg, 0);
arm_load_membase(inst, ARM_R1, temp_reg, 4);
break;
}
jump_to_epilog(gen, &inst, block);
}
JIT_OP_SETUP_FOR_NESTED: /*spill_before*/
[] -> {
jit_nint nest_reg = jit_value_get_nint_constant(insn->value1);
if(nest_reg == -1)
{
arm_push_reg(inst, ARM_FP);
}
else
{
arm_mov_reg_reg(inst, _jit_reg_info[nest_reg].cpu_reg, ARM_FP);
}
}
JIT_OP_SETUP_FOR_SIBLING: /*spill_before*/
[] -> {
jit_nint level = jit_value_get_nint_constant(insn->value1);
jit_nint nest_reg = jit_value_get_nint_constant(insn->value2);
int cpu_reg;
if(nest_reg == -1)
{
cpu_reg = ARM_R0;
}
else
{
cpu_reg = _jit_reg_info[nest_reg].cpu_reg;
}
arm_load_membase(inst, cpu_reg, ARM_FP, JIT_APPLY_PARENT_FRAME_OFFSET);
while(level > 0)
{
arm_load_membase(inst, cpu_reg, cpu_reg,
JIT_APPLY_PARENT_FRAME_OFFSET);
--level;
}
if(nest_reg == -1)
{
arm_push_reg(inst, cpu_reg);
}
}
JIT_OP_IMPORT:
[] -> {
/* TODO */
TODO();
}
/*
* Exception handling
*/
JIT_OP_THROW: branch
[reg] -> {
arm_push_reg(inst, $1);
if(func->builder->setjmp_value != 0)
{
/* We have a "setjmp" block in the current function,
so we must record the location of the throw first */
jit_nint pc_offset;
_jit_gen_fix_value(func->builder->setjmp_value);
pc_offset = func->builder->setjmp_value->frame_offset +
jit_jmp_catch_pc_offset;
if(func->builder->position_independent)
{
arm_call_imm(inst, 0);
arm_pop_membase(inst, ARM_FP, pc_offset);
}
else
{
int pc = (int) (unsigned char *) arm_inst_get_posn(inst);
arm_mov_membase_imm(inst, ARM_FP, pc_offset, pc, 4, ARM_WORK);
}
}
arm_call(inst, (void *)jit_exception_throw);
}
JIT_OP_LOAD_PC:
[=reg] -> {
if(func->builder->position_independent)
{
arm_call_imm(inst, 0);
arm_pop_reg(inst, $1);
}
else
{
int pc = inst.current;
mov_reg_imm(gen, &inst, $1, pc);
}
}
JIT_OP_ENTER_FINALLY:
[] -> { /*
* The return address is in the link register
* We must save it on the stack in case it will be overwritten by the content
* of the "finally" block.
* In order to respect the ABI of the ARM architecture, that prescribes an 8-byte
* alignment for the stack at a public interface, we save the value twice,
* in order to move the current SP by 8 bytes
* (we could have just saved the value once and then moved the SP by 4 bytes)
*/
arm_push_reg(inst, ARM_LINK);
arm_push_reg(inst, ARM_LINK);
}
JIT_OP_LEAVE_FINALLY: branch
[] -> {
/* The "finally" return address is on the stack (twice, just for padding)*/
arm_pop_reg(inst, ARM_LINK);
arm_pop_reg(inst, ARM_LINK);
arm_return(inst);
}
JIT_OP_CALL_FINALLY: branch
[] -> {
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_call(inst, 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_call_imm(inst, offset);
block->fixup_list = (void *)(arm_inst_get_posn(inst) - 1);
}
}
JIT_OP_ADDRESS_OF_LABEL:
[=reg] -> {
block = jit_block_from_label(func, (jit_label_t)(insn->value1));
if(func->builder->position_independent)
{
/* TODO */
TODO();
}
else
{
if(block->address)
{
mov_reg_imm(gen, &inst, $1, block->address);
}
else
{
/* Output a placeholder and record on the block's fixup list */
mov_reg_imm(gen, &inst, $1, (int)(block->fixup_absolute_list));
block->fixup_absolute_list = (void *)(inst.current - 1);
}
}
}
/*
* Data manipulation.
*/
JIT_OP_COPY_LOAD_SBYTE:
[reg] -> {}
JIT_OP_COPY_LOAD_UBYTE:
[reg] -> {}
JIT_OP_COPY_LOAD_SHORT:
[reg] -> {}
JIT_OP_COPY_LOAD_USHORT:
[reg] -> {}
JIT_OP_COPY_INT: copy
[=local, imm, scratch reg] -> {
arm_mov_membase_imm(inst, ARM_FP, $1, $2, 4, $3);
}
[reg] -> {}
JIT_OP_COPY_LONG: copy
[lreg] -> {}
JIT_OP_COPY_FLOAT32 (JIT_ARM_HAS_FLOAT_REGS): copy
[freg32] -> {}
JIT_OP_COPY_FLOAT32 (!JIT_ARM_HAS_FLOAT_REGS): manual
[] -> {
arm_inst_buf inst;
_jit_regs_force_out(gen, insn->value1, 0);
_jit_regs_force_out(gen, insn->dest, 1);
_jit_gen_fix_value(insn->value1);
_jit_gen_fix_value(insn->dest);
jit_gen_load_inst_ptr(gen, inst);
if(insn->value1->is_constant)
{
mov_reg_imm
(gen, &inst, ARM_WORK, ((int *)(insn->value1->address))[0]);
}
else
{
arm_load_membase(inst, ARM_WORK, ARM_FP,
insn->value1->frame_offset);
}
arm_store_membase(inst, ARM_WORK, ARM_FP, insn->dest->frame_offset);
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_COPY_FLOAT64, JIT_OP_COPY_NFLOAT (JIT_ARM_HAS_FLOAT_REGS): copy
[freg64] -> {}
JIT_OP_COPY_FLOAT64, JIT_OP_COPY_NFLOAT (!JIT_ARM_HAS_FLOAT_REGS): manual
[] -> {
arm_inst_buf inst;
_jit_regs_force_out(gen, insn->value1, 0);
_jit_regs_force_out(gen, insn->dest, 1);
_jit_gen_fix_value(insn->value1);
_jit_gen_fix_value(insn->dest);
jit_gen_load_inst_ptr(gen, inst);
if(insn->value1->is_constant)
{
mov_reg_imm
(gen, &inst, ARM_WORK, ((int *)(insn->value1->address))[0]);
arm_store_membase(inst, ARM_WORK, ARM_FP,
insn->dest->frame_offset);
mov_reg_imm
(gen, &inst, ARM_WORK, ((int *)(insn->value1->address))[1]);
arm_store_membase(inst, ARM_WORK, ARM_FP,
insn->dest->frame_offset + 4);
}
else
{
arm_load_membase(inst, ARM_WORK, ARM_FP,
insn->value1->frame_offset);
arm_store_membase(inst, ARM_WORK, ARM_FP,
insn->dest->frame_offset);
arm_load_membase(inst, ARM_WORK, ARM_FP,
insn->value1->frame_offset + 4);
arm_store_membase(inst, ARM_WORK, ARM_FP,
insn->dest->frame_offset + 4);
}
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_COPY_STRUCT:
[=frame, frame, scratch reg] -> {
inst = memory_copy(gen, inst, ARM_FP, $1, ARM_FP, $2,
jit_type_get_size(jit_value_get_type(insn->dest)), $3);
}
JIT_OP_COPY_STORE_BYTE: manual
[] -> {
arm_inst_buf inst;
int reg;
_jit_regs_force_out(gen, insn->dest, 1);
_jit_gen_fix_value(insn->dest);
reg = _jit_regs_load_value
(gen, insn->value1, 0,
(insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
JIT_INSN_VALUE1_LIVE)));
jit_gen_load_inst_ptr(gen, inst);
arm_store_membase_byte(inst, _jit_reg_info[reg].cpu_reg,
ARM_FP, insn->dest->frame_offset);
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_COPY_STORE_SHORT: manual
[] -> {
arm_inst_buf inst;
int reg;
_jit_regs_force_out(gen, insn->dest, 1);
_jit_gen_fix_value(insn->dest);
reg = _jit_regs_load_value
(gen, insn->value1, 1,
(insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
JIT_INSN_VALUE1_LIVE)));
jit_gen_load_inst_ptr(gen, inst);
arm_store_membase_short(inst, _jit_reg_info[reg].cpu_reg,
ARM_FP, insn->dest->frame_offset);
jit_gen_save_inst_ptr(gen, inst);
//_jit_regs_free_reg(gen, reg, 1); //TODO: check if it's needed
}
JIT_OP_ADDRESS_OF:
[=reg, frame] -> {
if($2 > 0)
{
arm_alu_reg_imm(inst, ARM_ADD, $1, ARM_FP, $2);
}
else if($2 < 0)
{
arm_alu_reg_imm(inst, ARM_SUB, $1, ARM_FP, -$2);
}
else
{
arm_mov_reg_reg(inst, $1, ARM_FP);
}
}
/*
* Stack pushes and pops.
*/
JIT_OP_INCOMING_REG, JIT_OP_RETURN_REG: note
[reg] -> {
/*
* This rule does nothing itself. Also at this point
* the value is supposed to be already in the register
* so the "reg" pattern does not load it either. But
* it allows the allocator to check the liveness flags
* and free the register if the value is dead.
*/
}
JIT_OP_PUSH_INT: note
[reg] -> {
arm_push_reg(inst, $1);
}
JIT_OP_PUSH_LONG: note
[lreg] -> {
arm_push_reg(inst, %1);
arm_push_reg(inst, $1);
gen->stack_changed=1;
}
JIT_OP_PUSH_FLOAT32 (JIT_ARM_HAS_FLOAT_REGS): note
[freg32] -> {
arm_push_reg_float32(inst, $1);
}
JIT_OP_PUSH_FLOAT32 (!JIT_ARM_HAS_FLOAT_REGS): manual
[] -> {
arm_inst_buf inst;
_jit_regs_force_out(gen, insn->value1, 0);
_jit_gen_fix_value(insn->value1);
jit_gen_load_inst_ptr(gen, inst);
if(insn->value1->is_constant)
{
mov_reg_imm
(gen, &inst, ARM_WORK, ((int *)(insn->value1->address))[0]);
}
else
{
arm_load_membase(inst, ARM_WORK, ARM_FP,
insn->value1->frame_offset);
}
arm_push_reg(inst, ARM_WORK);
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_PUSH_FLOAT64, JIT_OP_PUSH_NFLOAT (JIT_ARM_HAS_FLOAT_REGS): note
[freg64] -> {
arm_push_reg_float64(inst, $1);
}
JIT_OP_PUSH_FLOAT64, JIT_OP_PUSH_NFLOAT (!JIT_ARM_HAS_FLOAT_REGS): manual
[] -> {
arm_inst_buf inst;
_jit_regs_force_out(gen, insn->value1, 0);
_jit_gen_fix_value(insn->value1);
jit_gen_load_inst_ptr(gen, inst);
if(insn->value1->is_constant)
{
mov_reg_imm
(gen, &inst, ARM_WORK, ((int *)(insn->value1->address))[1]);
arm_push_reg(inst, ARM_WORK);
mov_reg_imm
(gen, &inst, ARM_WORK, ((int *)(insn->value1->address))[0]);
arm_push_reg(inst, ARM_WORK);
}
else
{
arm_load_membase(inst, ARM_WORK, ARM_FP,
insn->value1->frame_offset + 4);
arm_push_reg(inst, ARM_WORK);
arm_load_membase(inst, ARM_WORK, ARM_FP,
insn->value1->frame_offset);
arm_push_reg(inst, ARM_WORK);
}
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_PUSH_STRUCT: /*unary_note*/
[reg] -> {
/* TODO */
TODO();
}
JIT_OP_POP_STACK:
[] -> {
arm_alu_reg_imm(inst, ARM_ADD, ARM_SP, ARM_SP, insn->value1->address);
}
JIT_OP_FLUSH_SMALL_STRUCT:
[] -> {
jit_nuint size;
jit_nint offset;
_jit_gen_fix_value(insn->value1);
size = jit_type_get_size(jit_value_get_type(insn->value1));
offset = insn->value1->frame_offset;
switch(size)
{
case 1:
{
arm_store_membase_byte(inst, ARM_R0, ARM_FP, offset);
}
break;
case 2:
{
arm_store_membase_short(inst, ARM_R0, ARM_FP, offset);
}
break;
case 3:
{
arm_mov_reg_reg(inst, ARM_R1, ARM_R0);
arm_store_membase_short(inst, ARM_R0, ARM_FP, offset);
arm_shift_reg_imm8(inst, ARM_SHR, ARM_R0, ARM_R1, 16);
arm_store_membase_byte(inst, ARM_R0, ARM_FP, offset + 2);
}
break;
case 4:
{
arm_store_membase(inst, ARM_R0, ARM_FP, offset);
}
break;
case 5:
{
arm_store_membase(inst, ARM_R0, ARM_FP, offset);
arm_store_membase_byte(inst, ARM_R1, ARM_FP, offset + 4);
}
break;
case 6:
{
arm_store_membase(inst, ARM_R0, ARM_FP, offset);
arm_store_membase_short(inst, ARM_R1, ARM_FP, offset + 4);
}
break;
case 7:
{
arm_store_membase(inst, ARM_R0, ARM_FP, offset);
arm_mov_reg_reg(inst, ARM_R2, ARM_R1);
arm_store_membase_short(inst, ARM_R1, ARM_FP, offset + 4);
arm_shift_reg_imm8(inst, ARM_SHR, ARM_R1, ARM_R2, 16);
arm_store_membase_byte(inst, ARM_R1, ARM_FP, offset + 6);
}
break;
case 8:
{
arm_store_membase(inst, ARM_R0, ARM_FP, offset);
arm_store_membase(inst, ARM_R1, ARM_FP, offset + 4);
}
break;
}
}
JIT_OP_SET_PARAM_INT: note
[imm, imm] -> {
arm_mov_membase_imm(inst, ARM_SP, $2, $1, 4, ARM_WORK);
}
[reg, imm] -> {
arm_mov_membase_reg(inst, ARM_SP, $2, $1, 4);
}
JIT_OP_SET_PARAM_LONG: /*unary_note*/
[lreg] -> {
arm_store_membase(inst, $1, ARM_SP, insn->value2->address);
arm_store_membase(inst, %1, ARM_SP, insn->value2->address + 4);
}
JIT_OP_SET_PARAM_FLOAT32 (JIT_ARM_HAS_FLOAT_REGS): /*unary_note*/
[freg32] -> {
arm_store_membase_float32(inst, $1, ARM_SP, insn->value2->address);
}
JIT_OP_SET_PARAM_FLOAT32 (!JIT_ARM_HAS_FLOAT_REGS): manual
[] -> {
arm_inst_buf inst;
_jit_regs_force_out(gen, insn->value1, 0);
_jit_gen_fix_value(insn->value1);
jit_gen_load_inst_ptr(gen, inst);
if(insn->value1->is_constant)
{
mov_reg_imm
(gen, &inst, ARM_WORK, ((int *)(insn->value1->address))[0]);
arm_store_membase
(inst, ARM_WORK, ARM_SP, insn->value2->address);
}
else
{
arm_load_membase(inst, ARM_WORK, ARM_FP,
insn->value1->frame_offset);
arm_store_membase
(inst, ARM_WORK, ARM_SP, insn->value2->address);
}
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_SET_PARAM_FLOAT64, JIT_OP_SET_PARAM_NFLOAT
(JIT_ARM_HAS_FLOAT_REGS): /*unary_note*/
[freg64] -> {
arm_store_membase_float64(inst, $1, ARM_SP, insn->value2->address);
}
JIT_OP_SET_PARAM_FLOAT64, JIT_OP_SET_PARAM_NFLOAT
(!JIT_ARM_HAS_FLOAT_REGS): manual
[] -> {
arm_inst_buf inst;
_jit_regs_force_out(gen, insn->value1, 0);
_jit_gen_fix_value(insn->value1);
jit_gen_load_inst_ptr(gen, inst);
if(insn->value1->is_constant)
{
mov_reg_imm
(gen, &inst, ARM_WORK, ((int *)(insn->value1->address))[0]);
arm_store_membase
(inst, ARM_WORK, ARM_SP, insn->value2->address);
mov_reg_imm
(gen, &inst, ARM_WORK, ((int *)(insn->value1->address))[1]);
arm_store_membase
(inst, ARM_WORK, ARM_SP, insn->value2->address + 4);
}
else
{
arm_load_membase(inst, ARM_WORK, ARM_FP,
insn->value1->frame_offset);
arm_store_membase
(inst, ARM_WORK, ARM_SP, insn->value2->address);
arm_load_membase(inst, ARM_WORK, ARM_FP,
insn->value1->frame_offset + 4);
arm_store_membase
(inst, ARM_WORK, ARM_SP, insn->value2->address + 4);
}
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_SET_PARAM_STRUCT: note
[reg, imm, scratch reg] -> {
/* Handle arbitrary-sized structures */
jit_nint offset = jit_value_get_nint_constant(insn->dest);
inst = memory_copy(gen, inst, ARM_SP, offset, $1, 0, $2, $3);
}
/*
* Pointer-relative loads and stores.
*/
JIT_OP_LOAD_RELATIVE_SBYTE:
[reg] -> {
arm_load_membase_sbyte(inst, $1, $1, insn->value2->address);
}
JIT_OP_LOAD_RELATIVE_UBYTE:
[reg] -> {
arm_load_membase_byte(inst, $1, $1, insn->value2->address);
}
JIT_OP_LOAD_RELATIVE_SHORT:
[reg] -> {
arm_load_membase_short(inst, $1, $1, insn->value2->address);
}
JIT_OP_LOAD_RELATIVE_USHORT:
[reg] -> {
arm_load_membase_ushort(inst, $1, $1, insn->value2->address);
}
JIT_OP_LOAD_RELATIVE_INT:
[reg] -> {
arm_load_membase(inst, $1, $1, insn->value2->address);
}
JIT_OP_LOAD_RELATIVE_LONG:
[=lreg, reg, imm] -> {
if($1 == $2)
{
arm_mov_reg_membase(inst, %1, $2, $3 + 4, 4);
arm_mov_reg_membase(inst, $1, $2, $3, 4);
}
else
{
arm_mov_reg_membase(inst, $1, $2, $3, 4);
arm_mov_reg_membase(inst, %1, $2, $3 + 4, 4);
}
}
JIT_OP_LOAD_RELATIVE_FLOAT32 (JIT_ARM_HAS_VFP):
[=freg32, reg, imm] -> {
arm_fld_membase(inst, $1, $2, $3, 0);
}
JIT_OP_LOAD_RELATIVE_FLOAT64 (JIT_ARM_HAS_VFP):
[=freg64, reg, imm] -> {
arm_fld_membase(inst, $1, $2, $3, 1);
}
JIT_OP_LOAD_RELATIVE_NFLOAT: manual
[] -> {
/* TODO */
TODO();
abort();
}
JIT_OP_LOAD_RELATIVE_STRUCT: more_space
[=frame, reg, imm, scratch reg] -> {
inst = memory_copy(gen, inst, ARM_FP, $1, $2, $3, jit_type_get_size(jit_value_get_type(insn->dest)), $4);
}
JIT_OP_STORE_RELATIVE_BYTE: ternary
[imm, imm, imm, scratch reg] -> {
arm_mov_mem_imm(inst, $1 + $3, $2, 1, $4);
}
[imm, reg, imm] -> {
arm_mov_mem_reg(inst, $1 + $3, $2, 1);
}
[reg, imm, imm] -> {
arm_mov_membase_imm(inst, $1, $3, $2, 1, ARM_WORK);
}
[reg, reg, imm] -> {
arm_mov_membase_reg(inst, $1, $3, $2, 1);
}
JIT_OP_STORE_RELATIVE_SHORT: ternary
[imm, imm, imm, scratch reg] -> {
arm_mov_mem_imm(inst, $1 + $3, $2, 2, $4);
}
[imm, reg, imm] -> {
arm_mov_mem_reg(inst, $1 + $3, $2, 2);
}
[reg, imm, imm] -> {
arm_mov_membase_imm(inst, $1, $3, $2, 2, ARM_WORK);
}
[reg, reg, imm] -> {
arm_mov_membase_reg(inst, $1, $3, $2, 2);
}
JIT_OP_STORE_RELATIVE_INT: ternary
[imm, imm, imm, scratch reg] -> {
arm_mov_mem_imm(inst, $1 + $3, $2, 4, $4);
}
[imm, reg, imm] -> {
arm_mov_mem_reg(inst, $1 + $3, $2, 4);
}
[reg, imm, imm] -> {
arm_mov_membase_imm(inst, $1, $3, $2, 4, ARM_WORK);
}
[reg, reg, imm] -> {
arm_mov_membase_reg(inst, $1, $3, $2, 4);
}
JIT_OP_STORE_RELATIVE_LONG: ternary
[reg, imm, imm] -> {
arm_mov_membase_imm(inst, $1, $3, *(int *)($2), 4, ARM_WORK);
arm_mov_membase_imm(inst, $1, $3 + 4, *(int *)($2 + 4), 4, ARM_WORK);
}
[reg, local, imm, scratch reg] -> {
arm_mov_reg_membase(inst, $4, ARM_FP, $2, 4);
arm_mov_membase_reg(inst, $1, $3, $4, 4);
arm_mov_reg_membase(inst, $4, ARM_FP, $2 + 4, 4);
arm_mov_membase_reg(inst, $1, $3 + 4, $4, 4);
}
[reg, lreg, imm] -> {
arm_mov_membase_reg(inst, $1, $3, $2, 4);
arm_mov_membase_reg(inst, $1, $3 + 4, %2, 4);
}
JIT_OP_STORE_RELATIVE_FLOAT32 (JIT_ARM_HAS_VFP): ternary
[reg, imm, imm] -> {
arm_mov_membase_imm(inst, $1, $3, ((int *)($2))[0], 4, ARM_WORK);
}
[reg, freg32, imm] -> {
arm_store_membase_float32(inst, $2, $1, $3);
}
JIT_OP_STORE_RELATIVE_FLOAT64 (JIT_ARM_HAS_VFP): ternary
[reg, imm, imm, scratch reg] -> {
arm_mov_membase_imm(inst, $1, $3, ((int *)($2))[0], 4, $4);
arm_mov_membase_imm(inst, $1, $3 + 4, ((int *)($2))[1], 4, $4);
}
[reg, freg64, imm] -> {
arm_store_membase_float64(inst, $2, $1, $3);
}
JIT_OP_STORE_RELATIVE_NFLOAT: manual
[] -> {
/* TODO */
TODO();
abort();
}
JIT_OP_STORE_RELATIVE_STRUCT: manual
[] -> {
arm_inst_buf inst;
int reg = _jit_regs_load_value(gen, insn->dest, 0,
(insn->flags & (JIT_INSN_DEST_NEXT_USE |
JIT_INSN_DEST_LIVE)));
_jit_regs_spill_all(gen);
_jit_gen_fix_value(insn->value1);
jit_gen_load_inst_ptr(gen, inst);
if(!jit_cache_check_for_n(&(gen->posn), 128))
{
jit_cache_mark_full(&(gen->posn));
return;
}
reg = _jit_reg_info[reg].cpu_reg;
inst = memory_copy(gen, inst, reg, (int)(insn->value2->address),
ARM_FP, insn->value1->frame_offset,
jit_type_get_size(jit_value_get_type(insn->value1)), -1);
jit_gen_save_inst_ptr(gen, inst);
}
JIT_OP_ADD_RELATIVE:
[reg] -> {
if(insn->value2->address != 0)
{
arm_alu_reg_imm(inst, ARM_ADD, $1, $1, insn->value2->address);
}
}
/*
* Array element loads and stores.
*/
JIT_OP_LOAD_ELEMENT_UBYTE:
[=reg, reg, reg] -> {
arm_widen_memindex(inst, $1, $2, 0, $3, 0, 0, 0);
}
JIT_OP_LOAD_ELEMENT_USHORT:
[=reg, reg, reg] -> {
arm_widen_memindex(inst, $1, $2, 0, $3, 1, 0, 1);
}
JIT_OP_LOAD_ELEMENT_INT:
[=reg, reg, reg] -> {
/* The last parameter is unimportant: it's not used,
since the displacement (4th param) is 0 */
arm_mov_reg_memindex(inst, $1, $2, 0, $3, 2, 4, 0);
}
JIT_OP_LOAD_ELEMENT_LONG:
[=lreg, reg, reg, scratch reg, scratch reg] -> {
//$1=destination long register (1-st word, LSB)
//%1=destination long register (2-nd word, MSB)
//$2=base register
//$3=index register
//$4=scratch register for arm_mov_reg_memindex
//$5=scratch register for overwriting prevention
assert($2 != $3);
int basereg=$2;
int indexreg=$3;
//Write the 1-st word
if($1 == basereg)
{
//Prevent base reg from being overwritten
arm_mov_reg_reg(inst, $5, basereg);
basereg=$5;
}
else if ($1 == indexreg)
{
//Prevent index reg from being overwritten
arm_mov_reg_reg(inst, $5, indexreg);
indexreg=$5;
}
arm_mov_reg_memindex(inst, $1, basereg, 0, indexreg, 3, 4, $4);
//Write the 2-nd word
arm_mov_reg_memindex(inst, %1, basereg, 4, indexreg, 3, 4, $4);
}
JIT_OP_LOAD_ELEMENT_FLOAT64:
[=freg64, reg, reg, scratch reg] -> {
arm_fld_memindex(inst, $1, $2, 0, $3, 3, 1, $4);
}
JIT_OP_STORE_ELEMENT_BYTE: ternary
[reg, reg, reg, scratch reg] -> {
arm_mov_memindex_reg(inst, $1, 0, $2, 0, $3, 1, $4);
}
JIT_OP_STORE_ELEMENT_SHORT: ternary
[reg, reg, reg, scratch reg] -> {
arm_mov_memindex_reg(inst, $1, 0, $2, 1, $3, 2, $4);
}
JIT_OP_STORE_ELEMENT_INT: ternary
[reg, reg, reg, scratch reg] -> {
arm_mov_memindex_reg(inst, $1, 0, $2, 2, $3, 4, $4);
}
JIT_OP_STORE_ELEMENT_LONG: ternary
[reg, reg, imm] -> {
TODO();
abort();
//x86_mov_memindex_imm(inst, $1, 0, $2, 3, *(int *)($3), 4);
//x86_mov_memindex_imm(inst, $1, 4, $2, 3, *(int *)($3 + 4), 4);
}
[reg, reg, local, scratch reg, scratch reg] -> {
arm_mov_reg_membase(inst, $4, ARM_FP, $3, 4);
arm_mov_memindex_reg(inst, $1, 0, $2, 3, $4, 4, $5);
arm_mov_reg_membase(inst, $4, ARM_FP, $3 + 4, 4);
arm_mov_memindex_reg(inst, $1, 4, $2, 3, $4, 4, $5);
}
[reg, reg, lreg, scratch reg] -> {
arm_mov_memindex_reg(inst, $1, 0, $2, 3, $3, 4, $4);
arm_mov_memindex_reg(inst, $1, 4, $2, 3, %3, 4, $4);
}
JIT_OP_STORE_ELEMENT_FLOAT64: ternary
[reg, reg, freg64, scratch reg] -> {
arm_fst_memindex(inst, $3, $1, 0, $2, 3, 1, $4);
}
/*
* Allocate memory from the stack.
*/
JIT_OP_ALLOCA:
[reg] -> {
//The ARM stack must always be 4-byte aligned and must be 8-byte aligned at a public interface.
//Since we don't know when this function will be called, let's align to 8 bytes.
arm_alu_reg_imm(inst, ARM_ADD, $1, $1, 7);
arm_alu_reg_imm(inst, ARM_AND, $1, $1, ~7);
arm_alu_reg_reg(inst, ARM_SUB, ARM_SP, ARM_SP, $1);
arm_mov_reg_reg(inst, $1, ARM_SP);
gen->stack_changed = 1;
}
/*
* Block operations
*/
JIT_OP_MEMCPY: ternary
[any, any, imm, if("$3 <= 0")] -> { }
[reg, reg, imm, scratch reg, clobber("r0", "r1", "r2")] ->
{
/*
* Call jit_memcpy(dest,src,size).
* $1=dest, $2=src, $3=size
*/
int dest=$1;
int src=$2;
if (dest != ARM_R0) {
if(src==ARM_R0)
{
//Prevent overwriting useful data
arm_mov_reg_reg(inst, $4, src);
src=$4;
}
arm_mov_reg_reg((inst), ARM_R0, dest);
}
if (src != ARM_R1) {
//Move the "src" from wherever it is to where it should be
arm_mov_reg_reg(inst, ARM_R1, src);
}
mov_reg_imm(gen, &(inst), ARM_R2, $3);
//Call the function
arm_call(inst, jit_memcpy);
}
[reg, reg, reg, scratch reg, clobber("r0", "r1", "r2")] -> {
/*
* Call jit_memcpy(dest,src,size).
* $1=dest, $2=src, $3=size
*/
if ($1 != ARM_R0) {
if($2==ARM_R0)
{
//Prevent overwriting useful data
arm_mov_reg_reg(inst, $4, $2);
}
arm_mov_reg_reg((inst), ARM_R0, $1);
}
if ($2 != ARM_R1) {
if ($2==ARM_R0)
{
//Recover previously saved data
arm_mov_reg_reg(inst, ARM_R1, $4);
}
else
{
arm_mov_reg_reg((inst), ARM_R1, $2);
}
}
if ($3 != ARM_R2) {
arm_mov_reg_reg((inst), ARM_R2, $3);
}
//Call the function
arm_call(inst, jit_memcpy);
}
JIT_OP_MEMSET: ternary
[any, any, imm, if("$3 <= 0")] -> { }
[reg, imm, imm, if("$3 <= 32"), space("32 + $3 * 4")] -> {
// $1 = pointer to the initial memory location
// $2 = value to be written in memory
// $3 = length in bytes
int disp;
disp = 0;
while($3 >= (disp + 4))
{
//NB: if 0<A<255, then A*0x01010101 = a 32-bit value where each of its four bytes is A.
arm_mov_membase_imm(inst, $1, disp, $2 * 0x01010101, 4, ARM_WORK);
disp += 4;
}
if($3 >= (disp + 2))
{
arm_mov_membase_imm(inst, $1, disp, $2 * 0x0101, 2, ARM_WORK);
disp += 2;
}
if(insn->value2->address > disp)
{
arm_mov_membase_imm(inst, $1, disp, $2, 1, ARM_WORK);
}
}
[reg, reg, imm, if("$3 < 4")] -> {
TODO();
abort();
}
[reg, +reg, imm, scratch reg, if("$3 <= 32 && ($3 % 2) == 0"), space("32 + $3 * 4")] -> {
// $1 = pointer to the initial memory location
// $2 = value to be written in memory
// $3 = length in bytes
// $4 = scratch register
int disp;
arm_mov_reg_reg(inst, $4, $2);
arm_shift_reg_imm8(inst, ARM_SHL, $2, $2, 8);
arm_alu_reg_reg(inst, ARM_ORR, $2, $2, $4);
arm_mov_reg_reg(inst, $4, $2);
arm_shift_reg_imm8(inst, ARM_SHL, $2, $2, 16);
arm_alu_reg_reg(inst, ARM_ORR, $2, $2, $4);
disp = 0;
while($3 >= (disp + 4))
{
arm_mov_membase_reg(inst, $1, disp, $2, 4);
disp += 4;
}
if($3 > disp)
{
arm_mov_membase_reg(inst, $1, disp, $2, 2);
}
}
[reg, +reg, imm, scratch reg,
if("$3 <= 32 && ($3 % 2) != 0"), space("32 + $3 * 4")] -> {
TODO();
abort();
}
[reg, reg, reg, clobber("r0", "r1", "r2"), scratch reg] -> {
// $1 = pointer to the initial memory location
// $2 = value to be written in memory
// $3 = length in bytes
// $4 = scratch register
int pointer=$1;
int value=$2;
int length=$3;
int scratch=$4;
/* Move the outgoing parameters in the right registers (if they are not already where they should be */
if (pointer != ARM_R0) {
if(value == ARM_R0)
{
//Prevent the value from being overwritten
arm_mov_reg_reg((inst), scratch, ARM_R0);
value=scratch;
}
else if(length==ARM_R0)
{
//Prevent the length from being overwritten
arm_mov_reg_reg((inst), scratch, ARM_R0);
length=scratch;
}
arm_mov_reg_reg((inst), ARM_R0, pointer);
//The register that contained the pointer is now free
scratch=pointer;
}
if (value != ARM_R1)
{
if (length == ARM_R1)
{
//The length is stored in R1. Prevent it from being overwritten
arm_mov_reg_reg(inst, scratch, length);
length=scratch;
}
//Set param 2
arm_mov_reg_reg((inst), ARM_R1, value);
//The register that contained the value is now free
scratch=value;
}
if(length != ARM_R1)
{
//Param 3 still isn't in place: move it!
arm_mov_reg_reg(inst, ARM_R2, length);
}
arm_call(inst, jit_memset);
}