packages feed

gf-3.5: src/runtime/c/pgf/lightning/arm/core.h

/******************************** -*- C -*- ****************************
 *
 *	Platform-independent layer (arm version)
 *
 ***********************************************************************/

/***********************************************************************
 *
 * Copyright 2011 Free Software Foundation, Inc.
 *
 * This file is part of GNU lightning.
 *
 * GNU lightning 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 3, or (at your option)
 * any later version.
 *
 * GNU lightning 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 GNU lightning; see the file COPYING.LESSER; if not, write to the
 * Free Software Foundation, 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 ***********************************************************************/

#ifndef __lightning_core_arm_h
#define __lightning_core_arm_h

#define JIT_R_NUM			4
static const jit_gpr_t
jit_r_order[JIT_R_NUM] = {
    _R0, _R1, _R2, _R3
};
#define JIT_R(i)			jit_r_order[i]

#define JIT_V_NUM			4
static const jit_gpr_t
jit_v_order[JIT_V_NUM] = {
    _R4, _R5, _R6, _R7
};
#define JIT_V(i)			jit_v_order[i]

#define JIT_FRAMESIZE			48

#define jit_no_set_flags()		jit_flags.no_set_flags
#define jit_armv5_p()			(jit_cpu.version >= 5)
#define jit_armv5e_p()			(jit_cpu.version >= 5 && jit_cpu.extend)
#define jit_armv7r_p()			0
#define jit_swf_p()			(jit_cpu.vfp == 0)
#define jit_hardfp_p()			jit_cpu.abi

extern int	__aeabi_idivmod(int, int);
extern unsigned	__aeabi_uidivmod(unsigned, unsigned);

#define jit_nop(n)			arm_nop(_jit, n)
__jit_inline void
arm_nop(jit_state_t _jit, int n)
{
    assert(n >= 0);
    if (jit_thumb_p()) {
	for (; n > 0; n -= 2)
	    T1_NOP();
    }
    else {
	for (; n > 0; n -= 4)
	    _NOP();
    }
}

#define jit_movr_i(r0, r1)		arm_movr_i(_jit, r0, r1)
__jit_inline void
arm_movr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (r0 != r1) {
	if (jit_thumb_p())
	    T1_MOV(r0, r1);
	else
	    _MOV(r0, r1);
    }
}

#define jit_movi_i(r0, i0)		arm_movi_i(_jit, r0, i0)
__jit_inline void
arm_movi_i(jit_state_t _jit, jit_gpr_t r0, int i0)
{
    int		i;
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && r0 < 8 && !(i0 & 0xffffff80))
	    T1_MOVI(r0, i0);
	else if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_MOVI(r0, i);
	else if ((i = encode_thumb_immediate(~i0)) != -1)
	    T2_MVNI(r0, i);
	else {
	    T2_MOVWI(r0, _jit_US(i0));
	    if (i0 & 0xffff0000)
		T2_MOVTI(r0, _jit_US((unsigned)i0 >> 16));
	}
    }
    else {
	if (jit_armv6_p() && !(i0 & 0xffff0000))
	    _MOVWI(r0, i0);
	else if ((i = encode_arm_immediate(i0)) != -1)
	    _MOVI(r0, i);
	else if ((i = encode_arm_immediate(~i0)) != -1)
	    _MVNI(r0, i);
	else if (jit_armv6_p()) {
	    _MOVWI(r0, _jit_US(i0));
	    if ((i0 & 0xffff0000))
		_MOVTI(r0, _jit_US((unsigned)i0 >> 16));
	}
	else {
	    int     p0, p1, p2, p3, q0, q1, q2, q3;
	    p0 = i0 & 0x000000ff;   p1 = i0 & 0x0000ff00;
	    p2 = i0 & 0x00ff0000;   p3 = i0 & 0xff000000;
	    i0 = ~i0;
	    q0 = i0 & 0x000000ff;   q1 = i0 & 0x0000ff00;
	    q2 = i0 & 0x00ff0000;   q3 = i0 & 0xff000000;
	    if (!!p0 + !!p1 + !!p2 + !!p3 <= !!q0 + !!q1 + !!q2 + !!q3) {
		/* prefer no inversion on tie */
		if (p3) {
		    _MOVI(r0, encode_arm_immediate(p3));
		    if (p2) _ORRI(r0, r0, encode_arm_immediate(p2));
		    if (p1) _ORRI(r0, r0, encode_arm_immediate(p1));
		    if (p0) _ORRI(r0, r0, p0);
		}
		else if (p2) {
		    _MOVI(r0, encode_arm_immediate(p2));
		    if (p1) _ORRI(r0, r0, encode_arm_immediate(p1));
		    if (p0) _ORRI(r0, r0, p0);
		}
		else {
		    _MOVI(r0, encode_arm_immediate(p1));
		    _ORRI(r0, r0, p0);
		}
	    }
	    else {
		if (q3) {
		    _MVNI(r0, encode_arm_immediate(q3));
		    if (q2) _EORI(r0, r0, encode_arm_immediate(q2));
		    if (q1) _EORI(r0, r0, encode_arm_immediate(q1));
		    if (q0) _EORI(r0, r0, q0);
		}
		else if (q2) {
		    _MVNI(r0, encode_arm_immediate(q2));
		    if (q1) _EORI(r0, r0, encode_arm_immediate(q1));
		    if (q0) _EORI(r0, r0, q0);
		}
		else {
		    _MVNI(r0, encode_arm_immediate(q1));
		    _EORI(r0, r0, q0);
		}
	    }
	}
    }
}

#define jit_movi_p(r0, i0)		arm_movi_p(_jit, r0, i0)
__jit_inline jit_insn *
arm_movi_p(jit_state_t _jit, jit_gpr_t r0, void *i0)
{
    jit_insn	*l;
    int		 im, q0, q1, q2, q3;
    im = (int)i0; 		l = _jit->x.pc;
    if (jit_thumb_p()) {
	T2_MOVWI(r0, _jit_US((int)i0));
	T2_MOVTI(r0, _jit_US((int)i0 >> 16));
    }
    else {
	if (jit_armv6_p()) {
	    _MOVWI(r0, _jit_US((unsigned)i0));
	    _MOVTI(r0, _jit_US((unsigned)i0 >> 16));
	}
	else {
	    q0 = im & 0x000000ff;	q1 = im & 0x0000ff00;
	    q2 = im & 0x00ff0000;	q3 = im & 0xff000000;
	    _MOVI(r0, encode_arm_immediate(q3));
	    _ORRI(r0, r0, encode_arm_immediate(q2));
	    _ORRI(r0, r0, encode_arm_immediate(q1));
	    _ORRI(r0, r0, q0);
	}
    }
    return (l);
}

#define jit_patch_movi(i0, i1)		arm_patch_movi(_jit, i0, i1)
__jit_inline void
arm_patch_movi(jit_state_t _jit, jit_insn *i0, void *i1)
{
    union {
	short		*s;
	int		*i;
	void		*v;
    } u;
    jit_thumb_t		 thumb;
    unsigned int	 im;
    int			 q0, q1, q2, q3;
    im = (unsigned int)i1;		u.v = i0;
    if (jit_thumb_p()) {
	q0 = (im & 0xf000) << 4;
	q1 = (im & 0x0800) << 15;
	q2 = (im & 0x0700) << 4;
	q3 =  im & 0x00ff;
	code2thumb(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
	assert(   (thumb.i & 0xfbf00000) == THUMB2_MOVWI);
	thumb.i = (thumb.i & 0xfbf00f00) | q0 | q1 | q2 | q3;
	thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
	im >>= 16;
	q0 = (im & 0xf000) << 4;
	q1 = (im & 0x0800) << 15;
	q2 = (im & 0x0700) << 4;
	q3 =  im & 0x00ff;
	code2thumb(thumb.s[0], thumb.s[1], u.s[2], u.s[3]);
	assert(   (thumb.i & 0xfbf00000) == THUMB2_MOVTI);
	thumb.i = (thumb.i & 0xfbf00f00) | q0 | q1 | q2 | q3;
	thumb2code(thumb.s[0], thumb.s[1], u.s[2], u.s[3]);
    }
    else {
	if (jit_armv6_p()) {
	    q0 =  im &      0xfff;
	    q1 = (im &     0xf000) <<  4;
	    q2 = (im &  0xfff0000) >> 16;
	    q3 = (im & 0xf0000000) >> 12;
	    assert(  (u.i[0] & 0x0ff00000) == (ARM_MOVWI));
	    assert(  (u.i[1] & 0x0ff00000) == (ARM_MOVTI));
	    u.i[0] = (u.i[0] & 0xfff0f000) | q1 | q0;
	    u.i[1] = (u.i[1] & 0xfff0f000) | q3 | q2;
	}
	else {
	    q0 = im & 0x000000ff;	q1 = im & 0x0000ff00;
	    q2 = im & 0x00ff0000;	q3 = im & 0xff000000;
	    assert(  (u.i[0] & 0x0ff00000) == (ARM_MOV|ARM_I));
	    u.i[0] = (u.i[0] & 0xfffff000) | encode_arm_immediate(q3);
	    assert(  (u.i[1] & 0x0ff00000) == (ARM_ORR|ARM_I));
	    u.i[1] = (u.i[1] & 0xfffff000) | encode_arm_immediate(q2);
	    assert(  (u.i[2] & 0x0ff00000) == (ARM_ORR|ARM_I));
	    u.i[2] = (u.i[2] & 0xfffff000) | encode_arm_immediate(q1);
	    assert(  (u.i[3] & 0x0ff00000) == (ARM_ORR|ARM_I));
	    u.i[3] = (u.i[3] & 0xfffff000) | encode_arm_immediate(q0);
	}
    }
}

#define jit_patch_calli(i0, i1)		arm_patch_at(_jit, i0, i1)
#define jit_patch_at(jump, label)	arm_patch_at(_jit, jump, label)
__jit_inline void
arm_patch_at(jit_state_t _jit, jit_insn *jump, jit_insn *label)
{
    long		 d;
    union {
	short		*s;
	int		*i;
	void		*v;
    } u;
    jit_thumb_t		 thumb;
    u.v = jump;
    if (jit_thumb_p() && jump >= _jitl.thumb) {
#if 0
	/* this actually matches other patterns, so cannot patch
	 * automatically short jumps */
	if ((u.s[0] & THUMB_CC_B) == THUMB_CC_B) {
	    assert(_s8P(d));
	    u.s[0] = (u.s[0] & 0xff00) | (d & 0xff);
	}
	else if ((u.s[0] & THUMB_B) == THUMB_B) {
	    assert(_s11P(d));
	    u.s[0] = (u.s[0] & 0xf800) | (d & 0x7ff);
	}
	else
#endif
	{
	    code2thumb(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
	    if ((thumb.i & THUMB2_B) == THUMB2_B) {
		d = (((long)label - (long)jump) >> 1) - 2;
		assert(_s24P(d));
		thumb.i = THUMB2_B | encode_thumb_jump(d);
		thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
	    }
	    else if ((thumb.i & THUMB2_B) == THUMB2_CC_B) {
		d = (((long)label - (long)jump) >> 1) - 2;
		assert(_s20P(d));
		thumb.i = THUMB2_CC_B | (thumb.i & 0x3c00000) |
			  encode_thumb_cc_jump(d);
		thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
	    }
	    else if ((thumb.i & 0xfbf08000) == THUMB2_MOVWI)
		jit_patch_movi(jump, label);
	    else
		assert(!"handled branch opcode");
	}
    }
    else {
	/* 0x0e000000 because 0x01000000 is (branch&) link modifier */
	if ((u.i[0] & 0x0e000000) == ARM_B) {
	    d = (((long)label - (long)jump) >> 2) - 2;
	    assert(_s24P(d));
	    u.i[0] = (u.i[0] & 0xff000000) | (d & 0x00ffffff);
	}
	else if (( jit_armv6_p() && (u.i[0] & 0x0ff00000) == ARM_MOVWI) ||
		 (!jit_armv6_p() && (u.i[0] & 0x0ff00000) == (ARM_MOV|ARM_I)))
	    jit_patch_movi(jump, label);
	else
	    assert(!"handled branch opcode");
    }
}

#define jit_notr_i(r0, r1)		arm_notr_i(_jit, r0, r1)
__jit_inline void
arm_notr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1) < 8)
	    T1_NOT(r0, r1);
	else
	    T2_NOT(r0, r1);
    }
    else
	_NOT(r0, r1);
}

#define jit_negr_i(r0, r1)		arm_negr_i(_jit, r0, r1)
__jit_inline void
arm_negr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1) < 8)
	    T1_RSBI(r0, r1);
	else
	    T2_RSBI(r0, r1, 0);
    }
    else
	_RSBI(r0, r1, 0);
}

#define jit_addr_i(r0, r1, r2)		arm_addr_i(_jit, r0, r1, r2)
__jit_inline void
arm_addr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1|r2) < 8)
	    T1_ADD(r0, r1, r2);
	else if (r0 == r1 || r0 == r2)
	    T1_ADDX(r0, r0 == r1 ? r2 : r1);
	else
	    T2_ADD(r0, r1, r2);
    }
    else
	_ADD(r0, r1, r2);
}

#define jit_addi_i(r0, r1, i0)		arm_addi_i(_jit, r0, r1, i0)
__jit_inline void
arm_addi_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1) < 8 && !(i0 & ~7))
	    T1_ADDI3(r0, r1, i0);
	else if (!jit_no_set_flags() && (r0|r1) < 8 && !(-i0 & ~7))
	    T1_SUBI3(r0, r1, -i0);
	else if (!jit_no_set_flags() && r0 < 8 && r0 == r1 && !(i0 & ~0xff))
	    T1_ADDI8(r0, i0);
	else if (!jit_no_set_flags() && r0 < 8 && r0 == r1 && !(-i0 & ~0xff))
	    T1_SUBI8(r0, -i0);
	else if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_ADDI(r0, r1, i);
	else if ((i = encode_thumb_immediate(-i0)) != -1)
	    T2_SUBI(r0, r1, i);
	else if ((i = encode_thumb_word_immediate(i0)) != -1)
	    T2_ADDWI(r0, r1, i);
	else if ((i = encode_thumb_word_immediate(-i0)) != -1)
	    T2_SUBWI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    T2_ADD(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _ADDI(r0, r1, i);
	else if ((i = encode_arm_immediate(-i0)) != -1)
	    _SUBI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _ADD(r0, r1, reg);
	}
    }
}

#define jit_addcr_ui(r0, r1, r2)	arm_addcr_ui(_jit, r0, r1, r2)
__jit_inline void
arm_addcr_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	/* thumb auto set carry if not inside IT block */
	if ((r0|r1|r2) < 8)
	    T1_ADD(r0, r1, r2);
	else
	    T2_ADDS(r0, r1, r2);
    }
    else
	_ADDS(r0, r1, r2);
}

#define jit_addci_ui(r0, r1, i0)	arm_addci_ui(_jit, r0, r1, i0)
__jit_inline void
arm_addci_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if ((r0|r1) < 8 && !(i0 & ~7))
	    T1_ADDI3(r0, r1, i0);
	else if ((r0|r1) < 8 && !(-i0 & ~7))
	    T1_SUBI3(r0, r1, -i0);
	else if (r0 < 8 && r0 == r1 && !(i0 & ~0xff))
	    T1_ADDI8(r0, i0);
	else if (r0 < 8 && r0 == r1 && !(-i0 & ~0xff))
	    T1_SUBI8(r0, -i0);
	else if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_ADDSI(r0, r1, i);
	else if ((i = encode_thumb_immediate(-i0)) != -1)
	    T2_SUBSI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    T2_ADDS(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _ADDSI(r0, r1, i);
	else if ((i = encode_arm_immediate(-i0)) != -1)
	    _SUBSI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _ADDS(r0, r1, reg);
	}
    }
}

#define jit_addxr_ui(r0, r1, r2)	arm_addxr_ui(_jit, r0, r1, r2)
__jit_inline void
arm_addxr_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    /* keep setting carry because don't know last ADC */
    if (jit_thumb_p()) {
	/* thumb auto set carry if not inside IT block */
	if ((r0|r1|r2) < 8 && (r0 == r1 || r0 == r2))
	    T1_ADC(r0, r0 == r1 ? r2 : r1);
	else
	    T2_ADCS(r0, r1, r2);
    }
    else
	_ADCS(r0, r1, r2);
}

#define jit_addxi_ui(r0, r1, i0)	arm_addxi_ui(_jit, r0, r1, i0)
__jit_inline void
arm_addxi_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_ADCSI(r0, r1, i);
	else if ((i = encode_thumb_immediate(-i0)) != -1)
	    T2_SBCSI(r0, r1, i);
	else {
	    int		no_set_flags = jit_no_set_flags();
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_no_set_flags() = 1;
	    jit_movi_i(reg, i0);
	    jit_no_set_flags() = no_set_flags;
	    T2_ADCS(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _ADCSI(r0, r1, i);
	else if ((i = encode_arm_immediate(-i0)) != -1)
	    _SBCSI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _ADCS(r0, r1, reg);
	}
    }
}

#define jit_subr_i(r0, r1, r2)		arm_subr_i(_jit, r0, r1, r2)
__jit_inline void
arm_subr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1|r2) < 8)
	    T1_SUB(r0, r1, r2);
	else
	    T2_SUB(r0, r1, r2);
    }
    else
	_SUB(r0, r1, r2);
}

#define jit_subi_i(r0, r1, i0)		arm_subi_i(_jit, r0, r1, i0)
__jit_inline void
arm_subi_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1) < 8 && !(i0 & ~7))
	    T1_SUBI3(r0, r1, i0);
	else if (!jit_no_set_flags() && (r0|r1) < 8 && !(-i0 & ~7))
	    T1_ADDI3(r0, r1, -i0);
	else if (!jit_no_set_flags() && r0 < 8 && r0 == r1 && !(i0 & ~0xff))
	    T1_SUBI8(r0, i0);
	else if (!jit_no_set_flags() && r0 < 8 && r0 == r1 && !(-i0 & ~0xff))
	    T1_ADDI8(r0, -i0);
	else if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_SUBI(r0, r1, i);
	else if ((i = encode_thumb_immediate(-i0)) != -1)
	    T2_ADDI(r0, r1, i);
	else if ((i = encode_thumb_word_immediate(i0)) != -1)
	    T2_SUBWI(r0, r1, i);
	else if ((i = encode_thumb_word_immediate(-i0)) != -1)
	    T2_ADDWI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    T2_SUB(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _SUBI(r0, r1, i);
	else if ((i = encode_arm_immediate(-i0)) != -1)
	    _ADDI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _SUB(r0, r1, reg);
	}
    }
}

#define jit_subcr_ui(r0, r1, r2)	arm_subcr_ui(_jit, r0, r1, r2)
__jit_inline void
arm_subcr_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	/* thumb auto set carry if not inside IT block */
	if ((r0|r1|r2) < 8)
	    T1_SUB(r0, r1, r2);
	else
	    T2_SUBS(r0, r1, r2);
    }
    else
	_SUBS(r0, r1, r2);
}

#define jit_subci_ui(r0, r1, i0)	arm_subci_ui(_jit, r0, r1, i0)
__jit_inline void
arm_subci_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if ((r0|r1) < 8 && !(i0 & ~7))
	    T1_SUBI3(r0, r1, i0);
	else if ((r0|r1) < 8 && !(-i0 & ~7))
	    T1_ADDI3(r0, r1, -i0);
	else if (r0 < 8 && r0 == r1 && !(i0 & ~0xff))
	    T1_SUBI8(r0, i0);
	else if (r0 < 8 && r0 == r1 && !(-i0 & ~0xff))
	    T1_ADDI8(r0, -i0);
	else if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_SUBSI(r0, r1, i);
	else if ((i = encode_thumb_immediate(-i0)) != -1)
	    T2_ADDSI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    T2_SUBS(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _SUBSI(r0, r1, i);
	else if ((i = encode_arm_immediate(-i0)) != -1)
	    _ADDSI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _SUBS(r0, r1, reg);
	}
    }
}

#define jit_subxr_ui(r0, r1, r2)	arm_subxr_ui(_jit, r0, r1, r2)
__jit_inline void
arm_subxr_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    /* keep setting carry because don't know last ADC */
    if (jit_thumb_p()) {
	/* thumb auto set carry if not inside IT block */
	if ((r0|r1|r2) < 8 && r0 == r1)
	    T1_SBC(r0, r2);
	else
	    T2_SBCS(r0, r1, r2);
    }
    else
	_SBCS(r0, r1, r2);
}

#define jit_subxi_ui(r0, r1, i0)	arm_subxi_ui(_jit, r0, r1, i0)
__jit_inline void
arm_subxi_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_SBCSI(r0, r1, i);
	else if ((i = encode_thumb_immediate(-i0)) != -1)
	    T2_ADCSI(r0, r1, i);
	else {
	    int		no_set_flags = jit_no_set_flags();
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_no_set_flags() = 1;
	    jit_movi_i(reg, i0);
	    jit_no_set_flags() = no_set_flags;
	    T2_SBCS(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _SBCSI(r0, r1, i);
	else if ((i = encode_arm_immediate(-i0)) != -1)
	    _ADCSI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _SBCS(r0, r1, reg);
	}
    }
}

#define jit_rsbr_i(r0, r1, r2)		arm_rsbr_i(_jit, r0, r1, r2)
__jit_inline void
arm_rsbr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p())
	T2_RSB(r0, r1, r2);
    else
	_RSB(r0, r1, r2);
}

#define jit_rsbi_i(r0, r1, i0)		arm_rsbi_i(_jit, r0, r1, i0)
__jit_inline void
arm_rsbi_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if (i0 == 0)
	    arm_negr_i(_jit, r0, r1);
	else if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_RSBI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    T2_RSB(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _RSBI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _RSB(r0, r1, reg);
	}
    }
}

#define jit_mulr_i(r0, r1, r2)		arm_mulr_i(_jit, r0, r1, r2)
#define jit_mulr_ui(r0, r1, r2)		arm_mulr_i(_jit, r0, r1, r2)
__jit_inline void
arm_mulr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && r0 == r2 && (r0|r1) < 8)
	    T1_MUL(r0, r1);
	else if (!jit_no_set_flags() && r0 == r1 && (r0|r2) < 8)
	    T1_MUL(r0, r2);
	else
	    T2_MUL(r0, r1, r2);
    }
    else {
	if (r0 == r1 && !jit_armv6_p()) {
	    if (r0 != r2)
		_MUL(r0, r2, r1);
	    else {
		_MOV(JIT_TMP, r1);
		_MUL(r0, JIT_TMP, r2);
	    }
	}
	else
	    _MUL(r0, r1, r2);
    }
}

#define jit_muli_i(r0, r1, i0)		arm_muli_i(_jit, r0, r1, i0)
#define jit_muli_ui(r0, r1, i0)		arm_muli_i(_jit, r0, r1, i0)
__jit_inline void
arm_muli_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_gpr_t	reg;
    reg = r0 != r1 ? r0 : JIT_TMP;
    jit_movi_i(reg, i0);
    arm_mulr_i(_jit, r0, r1, reg);
}

#define jit_hmulr_i(r0, r1, r2)		arm_hmulr_i(_jit, r0, r1, r2)
__jit_inline void
arm_hmulr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p())
	T2_SMULL(JIT_TMP, r0, r1, r2);
    else {
	if (r0 == r1 && !jit_armv6_p()) {
	    assert(r2 != JIT_TMP);
	    _SMULL(JIT_TMP, r0, r2, r1);
	}
	else
	    _SMULL(JIT_TMP, r0, r1, r2);
    }
}

#define jit_hmuli_i(r0, r1, i0)		arm_hmuli_i(_jit, r0, r1, i0)
__jit_inline void
arm_hmuli_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	assert(r0 != JIT_TMP);
	jit_movi_i(JIT_TMP, i0);
	T2_SMULL(JIT_TMP, r0, r1, JIT_TMP);
    }
    else {
	if (r0 != r1 || jit_armv6_p()) {
	    jit_movi_i(JIT_TMP, i0);
	    _SMULL(JIT_TMP, r0, r1, JIT_TMP);
	}
	else {
	    if (r0 != _R0)	    reg = _R0;
	    else if (r0 != _R1)     reg = _R1;
	    else if (r0 != _R2)     reg = _R2;
	    else		    reg = _R3;
	    _PUSH(1<<reg);
	    jit_movi_i(reg, i0);
	    _SMULL(JIT_TMP, r0, r1, reg);
	    _POP(1<<reg);
	}
    }
}

#define jit_hmulr_ui(r0, r1, r2)	arm_hmulr_ui(_jit, r0, r1, r2)
__jit_inline void
arm_hmulr_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p())
	T2_UMULL(JIT_TMP, r0, r1, r2);
    else {
	if (r0 == r1 && !jit_armv6_p()) {
	    assert(r2 != JIT_TMP);
	    _UMULL(JIT_TMP, r0, r2, r1);
	}
	else
	    _UMULL(JIT_TMP, r0, r1, r2);
    }
}

#define jit_hmuli_ui(r0, r1, i0)	arm_hmuli_ui(_jit, r0, r1, i0)
__jit_inline void
arm_hmuli_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	assert(r0 != JIT_TMP);
	jit_movi_i(JIT_TMP, i0);
	T2_UMULL(JIT_TMP, r0, r1, JIT_TMP);
    }
    else {
	if (r0 != r1 || jit_armv6_p()) {
	    jit_movi_i(JIT_TMP, i0);
	    _UMULL(JIT_TMP, r0, r1, JIT_TMP);
	}
	else {
	    if (r0 != _R0)	    reg = _R0;
	    else if (r0 != _R1)     reg = _R1;
	    else if (r0 != _R2)     reg = _R2;
	    else		    reg = _R3;
	    _PUSH(1<<reg);
	    jit_movi_i(reg, i0);
	    _UMULL(JIT_TMP, r0, r1, reg);
	    _POP(1<<reg);
	}
    }
}

static void
arm_divmod(jit_state_t _jit, int div, int sign,
	   jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    int			 d;
    int			 l;
    void		*p;
    l = 0xf;
    if ((int)r0 < 4)
	/* bogus extra push to align at 8 bytes */
	l = (l & ~(1 << r0)) | 0x10;
    if (jit_thumb_p())
	T1_PUSH(l);
    else
	_PUSH(l);
    if (r1 == _R1 && r2 == _R0) {
	jit_movr_i(JIT_FTMP, _R0);
	jit_movr_i(_R0, _R1);
	jit_movr_i(_R1, JIT_FTMP);
    }
    else if (r2 == _R0) {
	jit_movr_i(_R1, r2);
	jit_movr_i(_R0, r1);
    }
    else {
	jit_movr_i(_R0, r1);
	jit_movr_i(_R1, r2);
    }
    if (sign)		p = __aeabi_idivmod;
    else		p = __aeabi_uidivmod;
    if (!jit_exchange_p()) {
	if (jit_thumb_p())
	    d = (((int)p - (int)_jit->x.pc) >> 1) - 2;
	else
	    d = (((int)p - (int)_jit->x.pc) >> 2) - 2;
	if (_s24P(d)) {
	    if (jit_thumb_p())
		T2_BLI(encode_thumb_jump(d));
	    else
		_BLI(d & 0x00ffffff);
	}
	else
	    goto fallback;
    }
    else {
    fallback:
	jit_movi_i(JIT_FTMP, (int)p);
	if (jit_thumb_p())
	    T1_BLX(JIT_FTMP);
	else
	    _BLX(JIT_FTMP);
    }
    if (div)
	jit_movr_i(r0, _R0);
    else
	jit_movr_i(r0, _R1);
    if (jit_thumb_p())
	T1_POP(l);
    else
	_POP(l);
}

#define jit_divr_i(r0, r1, r2)		arm_divr_i(_jit, r0, r1, r2)
__jit_inline void
arm_divr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_armv7r_p() && jit_thumb_p())
	T2_SDIV(r0, r1, r2);
    else
	arm_divmod(_jit, 1, 1, r0, r1, r2);
}

#define jit_divi_i(r0, r1, i0)		arm_divi_i(_jit, r0, r1, i0)
__jit_inline void
arm_divi_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_movi_i(JIT_TMP, i0);
    arm_divr_i(_jit, r0, r1, JIT_TMP);
}

#define jit_divr_ui(r0, r1, r2)		arm_divr_ui(_jit, r0, r1, r2)
__jit_inline void
arm_divr_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_armv7r_p() && jit_thumb_p())
	T2_UDIV(r0, r1, r2);
    else
	arm_divmod(_jit, 1, 0, r0, r1, r2);
}

#define jit_divi_ui(r0, r1, i0)		arm_divi_ui(_jit, r0, r1, i0)
__jit_inline void
arm_divi_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, unsigned int i0)
{
    jit_movi_i(JIT_TMP, i0);
    arm_divr_ui(_jit, r0, r1, JIT_TMP);
}

#define jit_modr_i(r0, r1, r2)		arm_modr_i(_jit, r0, r1, r2)
__jit_inline void
arm_modr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    arm_divmod(_jit, 0, 1, r0, r1, r2);
}

#define jit_modi_i(r0, r1, i0)		arm_modi_i(_jit, r0, r1, i0)
__jit_inline void
arm_modi_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_movi_i(JIT_TMP, i0);
    arm_modr_i(_jit, r0, r1, JIT_TMP);
}

#define jit_modr_ui(r0, r1, r2)		arm_modr_ui(_jit, r0, r1, r2)
__jit_inline void
arm_modr_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    arm_divmod(_jit, 0, 0, r0, r1, r2);
}

#define jit_modi_ui(r0, r1, i0)		arm_modi_ui(_jit, r0, r1, i0)
__jit_inline void
arm_modi_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_movi_i(JIT_TMP, i0);
    arm_modr_ui(_jit, r0, r1, JIT_TMP);
}

#define jit_andr_i(r0, r1, r2)		arm_andr_i(_jit, r0, r1, r2)
__jit_inline void
arm_andr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1|r2) < 8 && (r0 == r1 || r0 == r2))
	    T1_AND(r0, r0 == r1 ? r2 : r1);
	else
	    T2_AND(r0, r1, r2);
    }
    else
	_AND(r0, r1, r2);
}

#define jit_andi_i(r0, r1, i0)		arm_andi_i(_jit, r0, r1, i0)
__jit_inline void
arm_andi_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_ANDI(r0, r1, i);
	else if ((i = encode_thumb_immediate(~i0)) != -1)
	    T2_BICI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    T2_AND(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _ANDI(r0, r1, i);
	else if ((i = encode_arm_immediate(~i0)) != -1)
	    _BICI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _AND(r0, r1, reg);
	}
    }
}

#define jit_orr_i(r0, r1, r2)		arm_orr_i(_jit, r0, r1, r2)
__jit_inline void
arm_orr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1|r2) < 8 && (r0 == r1 || r0 == r2))
	    T1_ORR(r0, r0 == r1 ? r2 : r1);
	else
	    T2_ORR(r0, r1, r2);
    }
    else
	_ORR(r0, r1, r2);
}

#define jit_ori_i(r0, r1, i0)		arm_ori_i(_jit, r0, r1, i0)
__jit_inline void
arm_ori_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_ORRI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    T2_ORR(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _ORRI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _ORR(r0, r1, reg);
	}
    }
}

#define jit_xorr_i(r0, r1, r2)		arm_xorr_i(_jit, r0, r1, r2)
__jit_inline void
arm_xorr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1|r2) < 8 && (r0 == r1 || r0 == r2))
	    T1_EOR(r0, r0 == r1 ? r2 : r1);
	else
	    T2_EOR(r0, r1, r2);
    }
    else
	_EOR(r0, r1, r2);
}

#define jit_xori_i(r0, r1, i0)		arm_xori_i(_jit, r0, r1, i0)
__jit_inline void
arm_xori_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_EORI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    T2_EOR(r0, r1, reg);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _EORI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _EOR(r0, r1, reg);
	}
    }
}

#define jit_lshr_i(r0, r1, r2)		arm_lshr_i(_jit, r0, r1, r2)
__jit_inline void
arm_lshr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1|r2) < 8 && r0 == r1)
	    T1_LSL(r0, r2);
	else
	    T2_LSL(r0, r1, r2);
    }
    else
	_LSL(r0, r1, r2);
}

#define jit_lshi_i(r0, r1, i0)		arm_lshi_i(_jit, r0, r1, i0)
__jit_inline void
arm_lshi_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    assert(i0 >= 0 && i0 <= 31);
    if (i0 == 0)
	jit_movr_i(r0, r1);
    else if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1) < 8)
	    T1_LSLI(r0, r1, i0);
	else
	    T2_LSLI(r0, r1, i0);
    }
    else
	_LSLI(r0, r1, i0);
}

#define jit_rshr_i(r0, r1, r2)		arm_rshr_i(_jit, r0, r1, r2)
__jit_inline void
arm_rshr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1|r2) < 8 && r0 == r1)
	    T1_ASR(r0, r2);
	else
	    T2_ASR(r0, r1, r2);
    }
    else
	_ASR(r0, r1, r2);
}

#define jit_rshi_i(r0, r1, i0)		arm_rshi_i(_jit, r0, r1, i0)
__jit_inline void
arm_rshi_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    assert(i0 >= 0 && i0 <= 31);
    if (i0 == 0)
	jit_movr_i(r0, r1);
    else if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1) < 8)
	    T1_ASRI(r0, r1, i0);
	else
	    T2_ASRI(r0, r1, i0);
    }
    else
	_ASRI(r0, r1, i0);
}

#define jit_rshr_ui(r0, r1, r2)		arm_rshr_ui(_jit, r0, r1, r2)
__jit_inline void
arm_rshr_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1|r2) < 8 && r0 == r1)
	    T1_LSR(r0, r2);
	else
	    T2_LSR(r0, r1, r2);
    }
    else
	_LSR(r0, r1, r2);
}

#define jit_rshi_ui(r0, r1, i0)		arm_rshi_ui(_jit, r0, r1, i0)
__jit_inline void
arm_rshi_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    assert(i0 >= 0 && i0 <= 31);
    if (i0 == 0)
	jit_movr_i(r0, r1);
    else if (jit_thumb_p()) {
	if (!jit_no_set_flags() && (r0|r1) < 8)
	    T1_LSRI(r0, r1, i0);
	else
	    T2_LSRI(r0, r1, i0);
    }
    else
	_LSRI(r0, r1, i0);
}

__jit_inline void
arm_ccr(jit_state_t _jit, int ct, int cf,
	jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	assert((ct ^ cf) >> 28 == 1);
	if ((r1|r2) < 8)
	    T1_CMP(r1, r2);
	else if ((r1&r2) & 8)
	    T1_CMPX(r1, r2);
	else
	    T2_CMP(r1, r2);
	_ITE(ct);
	if (r0 < 8) {
	    T1_MOVI(r0, 1);
	    T1_MOVI(r0, 0);
	}
	else {
	    T2_MOVI(r0, 1);
	    T2_MOVI(r0, 0);
	}
    }
    else {
	_CMP(r1, r2);
	_CC_MOVI(ct, r0, 1);
	_CC_MOVI(cf, r0, 0);
    }
}
__jit_inline void
arm_cci(jit_state_t _jit, int ct, int cf,
	jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p()) {
	if (r1 < 7 && !(i0 & 0xffffff00))
	    T1_CMPI(r1, i0);
	else if ((i = encode_thumb_immediate(i0)) != -1)
	    T2_CMPI(r1, i);
	else if ((i = encode_thumb_immediate(-i0)) != -1)
	    T2_CMNI(r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    arm_ccr(_jit, ct, cf, r0, r1, reg);
	    return;
	}
	_ITE(ct);
	if (r0 < 8) {
	    T1_MOVI(r0, 1);
	    T1_MOVI(r0, 0);
	}
	else {
	    T2_MOVI(r0, 1);
	    T2_MOVI(r0, 0);
	}
    }
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _CMPI(r1, i);
	else if ((i = encode_arm_immediate(-i0)) != -1)
	    _CMNI(r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _CMP(r1, reg);
	}
	_CC_MOVI(ct, r0, 1);
	_CC_MOVI(cf, r0, 0);
    }
}
#define jit_ltr_i(r0, r1, r2)	arm_ccr(_jit,ARM_CC_LT,ARM_CC_GE,r0,r1,r2)
#define jit_lti_i(r0, r1, i0)	arm_cci(_jit,ARM_CC_LT,ARM_CC_GE,r0,r1,i0)
#define jit_ltr_ui(r0, r1, r2)	arm_ccr(_jit,ARM_CC_LO,ARM_CC_HS,r0,r1,r2)
#define jit_lti_ui(r0, r1, i0)	arm_cci(_jit,ARM_CC_LO,ARM_CC_HS,r0,r1,i0)
#define jit_ler_i(r0, r1, r2)	arm_ccr(_jit,ARM_CC_LE,ARM_CC_GT,r0,r1,r2)
#define jit_lei_i(r0, r1, i0)	arm_cci(_jit,ARM_CC_LE,ARM_CC_GT,r0,r1,i0)
#define jit_ler_ui(r0, r1, r2)	arm_ccr(_jit,ARM_CC_LS,ARM_CC_HI,r0,r1,r2)
#define jit_lei_ui(r0, r1, i0)	arm_cci(_jit,ARM_CC_LS,ARM_CC_HI,r0,r1,i0)
#define jit_eqr_i(r0, r1, r2)	arm_ccr(_jit,ARM_CC_EQ,ARM_CC_NE,r0,r1,r2)
#define jit_eqi_i(r0, r1, i0)	arm_cci(_jit,ARM_CC_EQ,ARM_CC_NE,r0,r1,i0)
#define jit_ger_i(r0, r1, r2)	arm_ccr(_jit,ARM_CC_GE,ARM_CC_LT,r0,r1,r2)
#define jit_gei_i(r0, r1, i0)	arm_cci(_jit,ARM_CC_GE,ARM_CC_LT,r0,r1,i0)
#define jit_ger_ui(r0, r1, r2)	arm_ccr(_jit,ARM_CC_HS,ARM_CC_LO,r0,r1,r2)
#define jit_gei_ui(r0, r1, i0)	arm_cci(_jit,ARM_CC_HS,ARM_CC_LO,r0,r1,i0)
#define jit_gtr_i(r0, r1, r2)	arm_ccr(_jit,ARM_CC_GT,ARM_CC_LE,r0,r1,r2)
#define jit_gti_i(r0, r1, i0)	arm_cci(_jit,ARM_CC_GT,ARM_CC_LE,r0,r1,i0)
#define jit_gtr_ui(r0, r1, r2)	arm_ccr(_jit,ARM_CC_HI,ARM_CC_LS,r0,r1,r2)
#define jit_gti_ui(r0, r1, i0)	arm_cci(_jit,ARM_CC_HI,ARM_CC_LS,r0,r1,i0)

#define jit_ner_i(r0, r1, r2)		arm_ner_i(_jit, r0, r1, r2)
__jit_inline void
arm_ner_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p())
	arm_ccr(_jit, ARM_CC_NE, ARM_CC_EQ, r0, r1, r2);
    else {
	_SUBS(r0, r1, r2);
	_CC_MOVI(ARM_CC_NE, r0, 1);
    }
}

#define jit_nei_i(r0, r1, i0)		arm_nei_i(_jit, r0, r1, i0)
__jit_inline void
arm_nei_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    int		i;
    jit_gpr_t	reg;
    if (jit_thumb_p())
	arm_cci(_jit, ARM_CC_NE, ARM_CC_EQ, r0, r1, i0);
    else {
	if ((i = encode_arm_immediate(i0)) != -1)
	    _SUBSI(r0, r1, i);
	else if ((i = encode_arm_immediate(-i0)) != -1)
	    _ADDSI(r0, r1, i);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _SUBS(r0, r1, reg);
	}
	_CC_MOVI(ARM_CC_NE, r0, 1);
    }
}

#define jit_jmpr(r0)			arm_jmpr(_jit, r0)
__jit_inline void
arm_jmpr(jit_state_t _jit, jit_gpr_t r0)
{
    if (jit_thumb_p())
	T1_MOV(_R15, r0);
    else
	_MOV(_R15, r0);
}

#define jit_jmpi(i0)			arm_jmpi(_jit, i0)
__jit_inline jit_insn *
arm_jmpi(jit_state_t _jit, void *i0)
{
    jit_insn	*l;
    long	 d;
    l = _jit->x.pc;
    if (jit_thumb_p() && _jitl.after_prolog) {
	d = (((long)i0 - (long)l) >> 1) - 2;
	if (_s20P(d))
	    T2_B(encode_thumb_jump(d));
	else {
	    jit_movi_p(JIT_TMP, i0);
	    jit_jmpr(JIT_TMP);
	}
    }
    else {
	d = (((long)i0 - (long)l) >> 2) - 2;
	assert(_s24P(d));
	_CC_B(ARM_CC_AL, d & 0x00ffffff);
    }
    return (l);
}

__jit_inline jit_insn *
arm_bccr(jit_state_t _jit, int cc, jit_insn *i0, jit_gpr_t r0, jit_gpr_t r1)
{
    jit_insn	*l;
    long	 d;
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_CMP(r0, r1);
	else if ((r0&r1) & 8)
	    T1_CMPX(r0, r1);
	else
	    T2_CMP(r0, r1);
	/* use only thumb2 conditional as does not know if will be patched */
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 1) - 2;
	assert(_s20P(d));
	T2_CC_B(cc, encode_thumb_cc_jump(d));
    }
    else {
	_CMP(r0, r1);
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 2) - 2;
	assert(_s24P(d));
	_CC_B(cc, d & 0x00ffffff);
    }
    return (l);
}
__jit_inline jit_insn *
arm_bcci(jit_state_t _jit, int cc, jit_insn *i0, jit_gpr_t r0, int i1)
{
    jit_insn	*l;
    long	 d;
    int		 i;
    if (jit_thumb_p()) {
	if (r0 < 7 && !(i1 & 0xffffff00))
	    T1_CMPI(r0, i1);
	else if ((i = encode_thumb_immediate(i1)) != -1)
	    T2_CMPI(r0, i);
	else if ((i = encode_thumb_immediate(-i1)) != -1)
	    T2_CMNI(r0, i);
	else {
	    jit_movi_i(JIT_TMP, i1);
	    T2_CMP(r0, JIT_TMP);
	}
	/* use only thumb2 conditional as does not know if will be patched */
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 1) - 2;
	assert(_s20P(d));
	T2_CC_B(cc, encode_thumb_cc_jump(d));
    }
    else {
	if ((i = encode_arm_immediate(i1)) != -1)
	    _CMPI(r0, i);
	else if ((i = encode_arm_immediate(-i1)) != -1)
	    _CMNI(r0, i);
	else {
	    jit_movi_i(JIT_TMP, i1);
	    _CMP(r0, JIT_TMP);
	}
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 2) - 2;
	assert(_s24P(d));
	_CC_B(cc, d & 0x00ffffff);
    }
    return (l);
}
#define jit_bltr_i(i0, r0, r1)		arm_bccr(_jit, ARM_CC_LT, i0, r0, r1)
#define jit_blti_i(i0, r0, i1)		arm_bcci(_jit, ARM_CC_LT, i0, r0, i1)
#define jit_bltr_ui(i0, r0, r1) 	arm_bccr(_jit, ARM_CC_LO, i0, r0, r1)
#define jit_blti_ui(i0, r0, i1) 	arm_bcci(_jit, ARM_CC_LO, i0, r0, i1)
#define jit_bler_i(i0, r0, r1)		arm_bccr(_jit, ARM_CC_LE, i0, r0, r1)
#define jit_blei_i(i0, r0, i1)		arm_bcci(_jit, ARM_CC_LE, i0, r0, i1)
#define jit_bler_ui(i0, r0, r1) 	arm_bccr(_jit, ARM_CC_LS, i0, r0, r1)
#define jit_blei_ui(i0, r0, i1) 	arm_bcci(_jit, ARM_CC_LS, i0, r0, i1)
#define jit_beqr_i(i0, r0, r1)		arm_bccr(_jit, ARM_CC_EQ, i0, r0, r1)
#define jit_beqi_i(i0, r0, i1)		arm_bcci(_jit, ARM_CC_EQ, i0, r0, i1)
#define jit_bger_i(i0, r0, r1)		arm_bccr(_jit, ARM_CC_GE, i0, r0, r1)
#define jit_bgei_i(i0, r0, i1)		arm_bcci(_jit, ARM_CC_GE, i0, r0, i1)
#define jit_bger_ui(i0, r0, r1) 	arm_bccr(_jit, ARM_CC_HS, i0, r0, r1)
#define jit_bgei_ui(i0, r0, i1) 	arm_bcci(_jit, ARM_CC_HS, i0, r0, i1)
#define jit_bgtr_i(i0, r0, r1)		arm_bccr(_jit, ARM_CC_GT, i0, r0, r1)
#define jit_bgti_i(i0, r0, i1)		arm_bcci(_jit, ARM_CC_GT, i0, r0, i1)
#define jit_bgtr_ui(i0, r0, r1) 	arm_bccr(_jit, ARM_CC_HI, i0, r0, r1)
#define jit_bgti_ui(i0, r0, i1) 	arm_bcci(_jit, ARM_CC_HI, i0, r0, i1)
#define jit_bner_i(i0, r0, r1)		arm_bccr(_jit, ARM_CC_NE, i0, r0, r1)
#define jit_bnei_i(i0, r0, i1)		arm_bcci(_jit, ARM_CC_NE, i0, r0, i1)

__jit_inline jit_insn *
arm_baddr(jit_state_t _jit, int cc, jit_insn *i0, jit_gpr_t r0, jit_gpr_t r1)
{
    jit_insn	*l;
    long	 d;
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_ADD(r0, r0, r1);
	else
	    T2_ADDS(r0, r0, r1);
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 1) - 2;
	assert(_s20P(d));
	T2_CC_B(cc, encode_thumb_cc_jump(d));
    }
    else {
	_ADDS(r0, r0, r1);
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 2) - 2;
	assert(_s24P(d));
	_CC_B(cc, d & 0x00ffffff);
    }
    return (l);
}
__jit_inline jit_insn *
arm_baddi(jit_state_t _jit, int cc, jit_insn *i0, jit_gpr_t r0, int i1)
{
    jit_insn	*l;
    long	 d;
    int		 i;
    if (jit_thumb_p()) {
	if (r0 < 8 && !(i1 & ~7))
	    T1_ADDI3(r0, r0, i1);
	else if (r0 < 8 && !(-i1 & ~7))
	    T1_SUBI3(r0, r0, -i1);
	else if (r0 < 8 && !(i1 & ~0xff))
	    T1_ADDI8(r0, i1);
	else if (r0 < 8 && !(-i1 & ~0xff))
	    T1_SUBI8(r0, -i1);
	else if ((i = encode_thumb_immediate(i1)) != -1)
	    T2_ADDSI(r0, r0, i);
	else if ((i = encode_thumb_immediate(-i1)) != -1)
	    T2_SUBSI(r0, r0, i);
	else {
	    jit_movi_i(JIT_TMP, i1);
	    T2_ADDS(r0, r0, JIT_TMP);
	}
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 1) - 2;
	assert(_s20P(d));
	T2_CC_B(cc, encode_thumb_cc_jump(d));
    }
    else {
	if ((i = encode_arm_immediate(i1)) != -1)
	    _ADDSI(r0, r0, i);
	else if ((i = encode_arm_immediate(-i1)) != -1)
	    _SUBSI(r0, r0, i);
	else {
	    jit_movi_i(JIT_TMP, i1);
	    _ADDS(r0, r0, JIT_TMP);
	}
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 2) - 2;
	assert(_s24P(d));
	_CC_B(cc, d & 0x00ffffff);
    }
    return (l);
}
#define jit_boaddr_i(i0, r0, r1)	arm_baddr(_jit, ARM_CC_VS, i0, r0, r1)
#define jit_boaddi_i(i0, r0, i1)	arm_baddi(_jit, ARM_CC_VS, i0, r0, i1)
#define jit_boaddr_ui(i0, r0, r1)	arm_baddr(_jit, ARM_CC_HS, i0, r0, r1)
#define jit_boaddi_ui(i0, r0, i1)	arm_baddi(_jit, ARM_CC_HS, i0, r0, i1)

__jit_inline jit_insn *
arm_bsubr(jit_state_t _jit, int cc, jit_insn *i0, jit_gpr_t r0, jit_gpr_t r1)
{
    jit_insn	*l;
    long	 d;
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_SUB(r0, r0, r1);
	else
	    T2_SUBS(r0, r0, r1);
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 1) - 2;
	assert(_s20P(d));
	T2_CC_B(cc, encode_thumb_cc_jump(d));
    }
    else {
	_SUBS(r0, r0, r1);
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 2) - 2;
	assert(_s24P(d));
	_CC_B(cc, d & 0x00ffffff);
    }
    return (l);
}

__jit_inline jit_insn *
arm_bsubi(jit_state_t _jit, int cc, jit_insn *i0, jit_gpr_t r0, int i1)
{
    jit_insn	*l;
    long	 d;
    int		 i;
    if (jit_thumb_p()) {
	if (r0 < 8 && !(i1 & ~7))
	    T1_SUBI3(r0, r0, i1);
	else if (r0 < 8 && !(-i1 & ~7))
	    T1_ADDI3(r0, r0, -i1);
	else if (r0 < 8 && !(i1 & ~0xff))
	    T1_SUBI8(r0, i1);
	else if (r0 < 8 && !(-i1 & ~0xff))
	    T1_ADDI8(r0, -i1);
	else if ((i = encode_thumb_immediate(i1)) != -1)
	    T2_SUBSI(r0, r0, i);
	else if ((i = encode_thumb_immediate(-i1)) != -1)
	    T2_ADDSI(r0, r0, i);
	else {
	    jit_movi_i(JIT_TMP, i1);
	    T2_SUBS(r0, r0, JIT_TMP);
	}
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 1) - 2;
	assert(_s20P(d));
	T2_CC_B(cc, encode_thumb_cc_jump(d));
    }
    else {
	if ((i = encode_arm_immediate(i1)) != -1)
	    _SUBSI(r0, r0, i);
	else if ((i = encode_arm_immediate(-i1)) != -1)
	    _ADDSI(r0, r0, i);
	else {
	    jit_movi_i(JIT_TMP, i1);
	    _SUBS(r0, r0, JIT_TMP);
	}
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 2) - 2;
	assert(_s24P(d));
	_CC_B(cc, d & 0x00ffffff);
    }
    return (l);
}
#define jit_bosubr_i(i0, r0, r1)	arm_bsubr(_jit, ARM_CC_VS, i0, r0, r1)
#define jit_bosubi_i(i0, r0, i1)	arm_bsubi(_jit, ARM_CC_VS, i0, r0, i1)
#define jit_bosubr_ui(i0, r0, r1)	arm_bsubr(_jit, ARM_CC_LO, i0, r0, r1)
#define jit_bosubi_ui(i0, r0, i1)	arm_bsubi(_jit, ARM_CC_LO, i0, r0, i1)

__jit_inline jit_insn *
arm_bmxr(jit_state_t _jit, int cc, jit_insn *i0, jit_gpr_t r0, jit_gpr_t r1)
{
    jit_insn	*l;
    long	 d;
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_TST(r0, r1);
	else
	    T2_TST(r0, r1);
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 1) - 2;
	assert(_s20P(d));
	T2_CC_B(cc, encode_thumb_cc_jump(d));
    }
    else {
	if (jit_armv5_p())
	    _TST(r0, r1);
	else
	    _ANDS(JIT_TMP, r0, r1);
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 2) - 2;
	assert(_s24P(d));
	_CC_B(cc, d & 0x00ffffff);
    }
    return (l);
}
__jit_inline jit_insn *
arm_bmxi(jit_state_t _jit, int cc, jit_insn *i0, jit_gpr_t r0, int i1)
{
    jit_insn	*l;
    long	 d;
    int		 i;
    if (jit_thumb_p()) {
	if ((i = encode_thumb_immediate(i1)) != -1)
	    T2_TSTI(r0, i);
	else {
	    jit_movi_i(JIT_TMP, i1);
	    T2_TST(r0, JIT_TMP);
	}
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 1) - 2;
	assert(_s20P(d));
	T2_CC_B(cc, encode_thumb_cc_jump(d));
    }
    else {
	if (jit_armv5_p()) {
	    if ((i = encode_arm_immediate(i1)) != -1)
		_TSTI(r0, i);
	    else {
		jit_movi_i(JIT_TMP, i1);
		_TST(r0, JIT_TMP);
	    }
	}
	else {
	    if ((i = encode_arm_immediate(i1)) != -1)
		_ANDSI(JIT_TMP, r0, i);
	    else if ((i = encode_arm_immediate(~i1)) != -1)
		_BICSI(JIT_TMP, r0, i);
	    else {
		jit_movi_i(JIT_TMP, i1);
		_ANDS(JIT_TMP, r0, JIT_TMP);
	    }
	}
	l = _jit->x.pc;
	d = (((long)i0 - (long)l) >> 2) - 2;
	assert(_s24P(d));
	_CC_B(cc, d & 0x00ffffff);
    }
    return (l);
}
#define jit_bmsr_i(i0, r0, r1)		arm_bmxr(_jit, ARM_CC_NE, i0, r0, r1)
#define jit_bmsi_i(i0, r0, i1)		arm_bmxi(_jit, ARM_CC_NE, i0, r0, i1)
#define jit_bmcr_i(i0, r0, r1)		arm_bmxr(_jit, ARM_CC_EQ, i0, r0, r1)
#define jit_bmci_i(i0, r0, i1)		arm_bmxi(_jit, ARM_CC_EQ, i0, r0, i1)

#define jit_ldr_c(r0, r1)		arm_ldr_c(_jit, r0, r1)
__jit_inline void
arm_ldr_c(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p())
	T2_LDRSBI(r0, r1, 0);
    else
	_LDRSBI(r0, r1, 0);
}

#define jit_ldi_c(r0, i0)		arm_ldi_c(_jit, r0, i0)
__jit_inline void
arm_ldi_c(jit_state_t _jit, jit_gpr_t r0, void *i0)
{
    jit_movi_i(JIT_TMP, (int)i0);
    if (jit_thumb_p())
	T2_LDRSBI(r0, JIT_TMP, 0);
    else
	_LDRSBI(r0, JIT_TMP, 0);
}

#define jit_ldxr_c(r0, r1, r2)		arm_ldxr_c(_jit, r0, r1, r2)
__jit_inline void
arm_ldxr_c(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if ((r0|r1|r2) < 8)
	    T1_LDRSB(r0, r1, r2);
	else
	    T2_LDRSB(r0, r1, r2);
    }
    else
	_LDRSB(r0, r1, r2);
}

#define jit_ldxi_c(r0, r1, i0)		arm_ldxi_c(_jit, r0, r1, i0)
__jit_inline void
arm_ldxi_c(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_gpr_t		reg;
    if (jit_thumb_p()) {
	if (i0 >= 0 && i0 <= 255)
	    T2_LDRSBI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -255)
	    T2_LDRSBIN(r0, r1, -i0);
	else if (i0 >= 0 && i0 <= 4095)
	    T2_LDRSBWI(r0, r1, i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    if ((r0|r1|reg) < 8)
		T1_LDRSB(r0, r1, reg);
	    else
		T2_LDRSB(r0, r1, reg);
	}
    }
    else {
	if (i0 >= 0 && i0 <= 255)
	    _LDRSBI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -255)
	    _LDRSBIN(r0, r1, -i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _LDRSB(r0, r1, reg);
	}
    }
}

#define jit_ldr_uc(r0, r1)		arm_ldr_uc(_jit, r0, r1)
__jit_inline void
arm_ldr_uc(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p())
	T2_LDRBI(r0, r1, 0);
    else
	_LDRBI(r0, r1, 0);
}

#define jit_ldi_uc(r0, i0)		arm_ldi_uc(_jit, r0, i0)
__jit_inline void
arm_ldi_uc(jit_state_t _jit, jit_gpr_t r0, void *i0)
{
    jit_movi_i(JIT_TMP, (int)i0);
    if (jit_thumb_p())
	T2_LDRBI(r0, JIT_TMP, 0);
    else
	_LDRBI(r0, JIT_TMP, 0);
}

#define jit_ldxr_uc(r0, r1, r2)		arm_ldxr_uc(_jit, r0, r1, r2)
__jit_inline void
arm_ldxr_uc(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if ((r0|r1|r2) < 8)
	    T1_LDRB(r0, r1, r2);
	else
	    T2_LDRB(r0, r1, r2);
    }
    else
	_LDRB(r0, r1, r2);
}

#define jit_ldxi_uc(r0, r1, i0)		arm_ldxi_uc(_jit, r0, r1, i0)
__jit_inline void
arm_ldxi_uc(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_gpr_t		reg;
    if (jit_thumb_p()) {
	if ((r0|r1) < 8 && i0 >= 0 && i0 < 0x20)
	    T1_LDRBI(r0, r1, i0);
	else if (i0 >= 0 && i0 <= 255)
	    T2_LDRBI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -255)
	    T2_LDRBIN(r0, r1, -i0);
	else if (i0 >= 0 && i0 <= 4095)
	    T2_LDRBWI(r0, r1, i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    if ((r0|r1|reg) < 8)
		T1_LDRB(r0, r1, reg);
	    else
		T2_LDRB(r0, r1, reg);
	}
    }
    else {
	if (i0 >= 0 && i0 <= 4095)
	    _LDRBI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -4095)
	    _LDRBIN(r0, r1, -i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _LDRB(r0, r1, reg);
	}
    }
}

#define jit_ldr_s(r0, r1)		arm_ldr_s(_jit, r0, r1)
__jit_inline void
arm_ldr_s(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p())
	T2_LDRSHI(r0, r1, 0);
    else
	_LDRSHI(r0, r1, 0);
}

#define jit_ldi_s(r0, i0)		arm_ldi_s(_jit, r0, i0)
__jit_inline void
arm_ldi_s(jit_state_t _jit, jit_gpr_t r0, void *i0)
{
    jit_movi_i(JIT_TMP, (int)i0);
    if (jit_thumb_p())
	T2_LDRSHI(r0, JIT_TMP, 0);
    else
	_LDRSHI(r0, JIT_TMP, 0);
}

#define jit_ldxr_s(r0, r1, r2)		arm_ldxr_s(_jit, r0, r1, r2)
__jit_inline void
arm_ldxr_s(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if ((r0|r1|r2) < 8)
	    T1_LDRSH(r0, r1, r2);
	else
	    T2_LDRSH(r0, r1, r2);
    }
    else
	_LDRSH(r0, r1, r2);
}

#define jit_ldxi_s(r0, r1, i0)		arm_ldxi_s(_jit, r0, r1, i0)
__jit_inline void
arm_ldxi_s(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_gpr_t		reg;
    if (jit_thumb_p()) {
	if (i0 >= 0 && i0 <= 255)
	    T2_LDRSHI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -255)
	    T2_LDRSHIN(r0, r1, -i0);
	else if (i0 >= 0 && i0 <= 4095)
	    T2_LDRSHWI(r0, r1, i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    if ((r0|r1|reg) < 8)
		T1_LDRSH(r0, r1, reg);
	    else
		T2_LDRSH(r0, r1, reg);
	}
    }
    else {
	if (i0 >= 0 && i0 <= 255)
	    _LDRSHI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -255)
	    _LDRSHIN(r0, r1, -i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _LDRSH(r0, r1, reg);
	}
    }

}
#define jit_ldr_us(r0, r1)		arm_ldr_us(_jit, r0, r1)
__jit_inline void
arm_ldr_us(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p())
	T2_LDRHI(r0, r1, 0);
    else
	_LDRHI(r0, r1, 0);
}

#define jit_ldi_us(r0, i0)		arm_ldi_us(_jit, r0, i0)
__jit_inline void
arm_ldi_us(jit_state_t _jit, jit_gpr_t r0, void *i0)
{
    jit_movi_i(JIT_TMP, (int)i0);
    if (jit_thumb_p())
	T2_LDRHI(r0, JIT_TMP, 0);
    else
	_LDRHI(r0, JIT_TMP, 0);
}

#define jit_ldxr_us(r0, r1, r2)		arm_ldxr_us(_jit, r0, r1, r2)
__jit_inline void
arm_ldxr_us(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if ((r0|r1|r2) < 8)
	    T1_LDRH(r0, r1, r2);
	else
	    T2_LDRH(r0, r1, r2);
    }
    else
	_LDRH(r0, r1, r2);
}

#define jit_ldxi_us(r0, r1, i0)		arm_ldxi_us(_jit, r0, r1, i0)
__jit_inline void
arm_ldxi_us(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_gpr_t		reg;
    if (jit_thumb_p()) {
	if ((r0|r1) < 8 && i0 >= 0 && !(i0 & 1) && (i0 >> 1) < 0x20)
	    T1_LDRHI(r0, r1, i0 >> 1);
	else if (i0 >= 0 && i0 <= 255)
	    T2_LDRHI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -255)
	    T2_LDRHIN(r0, r1, -i0);
	else if (i0 >= 0 && i0 <= 4095)
	    T2_LDRHWI(r0, r1, i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    if ((r0|r1|reg) < 8)
		T1_LDRH(r0, r1, reg);
	    else
		T2_LDRH(r0, r1, reg);
	}
    }
    else {
	if (i0 >= 0 && i0 <= 255)
	    _LDRHI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -255)
	    _LDRHIN(r0, r1, -i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _LDRH(r0, r1, reg);
	}
    }
}

#define jit_ldr_i(r0, r1)		arm_ldr_i(_jit, r0, r1)
__jit_inline void
arm_ldr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p())
	T2_LDRI(r0, r1, 0);
    else
	_LDRI(r0, r1, 0);
}

#define jit_ldi_i(r0, i0)		arm_ldi_i(_jit, r0, i0)
__jit_inline void
arm_ldi_i(jit_state_t _jit, jit_gpr_t r0, void *i0)
{
    jit_movi_i(JIT_TMP, (int)i0);
    if (jit_thumb_p())
	T2_LDRI(r0, JIT_TMP, 0);
    else
	_LDRI(r0, JIT_TMP, 0);
}

#define jit_ldxr_i(r0, r1, r2)		arm_ldxr_i(_jit, r0, r1, r2)
__jit_inline void
arm_ldxr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if ((r0|r1|r2) < 8)
	    T1_LDR(r0, r1, r2);
	else
	    T2_LDR(r0, r1, r2);
    }
    else
	_LDR(r0, r1, r2);
}

#define jit_ldxi_i(r0, r1, i0)		arm_ldxi_i(_jit, r0, r1, i0)
__jit_inline void
arm_ldxi_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, int i0)
{
    jit_gpr_t		reg;
    if (jit_thumb_p()) {
	if ((r0|r1) < 8 && i0 >= 0 && !(i0 & 3) && (i0 >> 2) < 0x20)
	    T1_LDRI(r0, r1, i0 >> 2);
	else if (r1 == _R13 && r0 < 8 &&
		 i0 >= 0 && !(i0 & 3) && (i0 >> 2) <= 255)
	    T1_LDRISP(r0, i0 >> 2);
	else if (i0 >= 0 && i0 <= 255)
	    T2_LDRI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -255)
	    T2_LDRIN(r0, r1, -i0);
	else if (i0 >= 0 && i0 <= 4095)
	    T2_LDRWI(r0, r1, i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    if ((r0|r1|reg) < 8)
		T1_LDR(r0, r1, reg);
	    else
		T2_LDR(r0, r1, reg);
	}
    }
    else {
	if (i0 >= 0 && i0 <= 4095)
	    _LDRI(r0, r1, i0);
	else if (i0 < 0 && i0 >= -4095)
	    _LDRIN(r0, r1, -i0);
	else {
	    reg = r0 != r1 ? r0 : JIT_TMP;
	    jit_movi_i(reg, i0);
	    _LDR(r0, r1, reg);
	}
    }
}

#define jit_str_c(r0, r1)		arm_str_c(_jit, r0, r1)
__jit_inline void
arm_str_c(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p())
	T2_STRBI(r1, r0, 0);
    else
	_STRBI(r1, r0, 0);
}

#define jit_sti_c(r0, i0)		arm_sti_c(_jit, r0, i0)
__jit_inline void
arm_sti_c(jit_state_t _jit, void *i0, jit_gpr_t r0)
{
    jit_movi_i(JIT_TMP, (int)i0);
    if (jit_thumb_p())
	T2_STRBI(r0, JIT_TMP, 0);
    else
	_STRBI(r0, JIT_TMP, 0);
}

#define jit_stxr_c(r0, r1, r2)		arm_stxr_c(_jit, r0, r1, r2)
__jit_inline void
arm_stxr_c(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if ((r0|r1|r2) < 8)
	    T1_STRB(r2, r1, r0);
	else
	    T2_STRB(r2, r1, r0);
    }
    else
	_STRB(r2, r1, r0);
}

#define jit_stxi_c(r0, r1, i0)		arm_stxi_c(_jit, r0, r1, i0)
__jit_inline void
arm_stxi_c(jit_state_t _jit, int i0, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if ((r0|r1) < 8 && i0 >= 0 && i0 < 0x20)
	    T1_STRBI(r1, r0, i0);
	else if (i0 >= 0 && i0 <= 255)
	    T2_STRBI(r1, r0, i0);
	else if (i0 < 0 && i0 >= -255)
	    T2_STRBIN(r1, r0, -i0);
	else if (i0 >= 0 && i0 <= 4095)
	    T2_STRBWI(r1, r0, i0);
	else {
	    jit_movi_i(JIT_TMP, (int)i0);
	    if ((r0|r1|JIT_TMP) < 8)
		T1_STRB(r1, r0, JIT_TMP);
	    else
		T2_STRB(r1, r0, JIT_TMP);
	}
    }
    else {
	if (i0 >= 0 && i0 <= 4095)
	    _STRBI(r1, r0, i0);
	else if (i0 < 0 && i0 >= -4095)
	    _STRBIN(r1, r0, -i0);
	else {
	    jit_movi_i(JIT_TMP, i0);
	    _STRB(r1, r0, JIT_TMP);
	}
    }
}

#define jit_str_s(r0, r1)		arm_str_s(_jit, r0, r1)
__jit_inline void
arm_str_s(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p())
	T2_STRHI(r1, r0, 0);
    else
	_STRHI(r1, r0, 0);
}

#define jit_sti_s(r0, i0)		arm_sti_s(_jit, r0, i0)
__jit_inline void
arm_sti_s(jit_state_t _jit, void *i0, jit_gpr_t r0)
{
    jit_movi_i(JIT_TMP, (int)i0);
    if (jit_thumb_p())
	T2_STRHI(r0, JIT_TMP, 0);
    else
	_STRHI(r0, JIT_TMP, 0);
}

#define jit_stxr_s(r0, r1, r2)		arm_stxr_s(_jit, r0, r1, r2)
__jit_inline void
arm_stxr_s(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if ((r0|r1|r2) < 8)
	    T1_STRH(r2, r1, r0);
	else
	    T2_STRH(r2, r1, r0);
    }
    else
	_STRH(r2, r1, r0);
}

#define jit_stxi_s(r0, r1, i0)		arm_stxi_s(_jit, r0, r1, i0)
__jit_inline void
arm_stxi_s(jit_state_t _jit, int i0, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if ((r0|r1) < 8 && i0 >= 0 && !(i0 & 1) && (i0 >> 1) < 0x20)
	    T1_STRHI(r1, r0, i0 >> 1);
	else if (i0 >= 0 && i0 <= 255)
	    T2_STRHI(r1, r0, i0);
	else if (i0 < 0 && i0 >= -255)
	    T2_STRHIN(r1, r0, -i0);
	else if (i0 >= 0 && i0 <= 4095)
	    T2_STRHWI(r1, r0, i0);
	else {
	    jit_movi_i(JIT_TMP, (int)i0);
	    if ((r0|r1|JIT_TMP) < 8)
		T1_STRH(r1, r0, JIT_TMP);
	    else
		T2_STRH(r1, r0, JIT_TMP);
	}
    }
    else {
	if (i0 >= 0 && i0 <= 255)
	    _STRHI(r1, r0, i0);
	else if (i0 < 0 && i0 >= -255)
	    _STRHIN(r1, r0, -i0);
	else {
	    jit_movi_i(JIT_TMP, i0);
	    _STRH(r1, r0, JIT_TMP);
	}
    }
}

#define jit_str_i(r0, r1)		arm_str_i(_jit, r0, r1)
__jit_inline void
arm_str_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p())
	T2_STRI(r1, r0, 0);
    else
	_STRI(r1, r0, 0);
}

#define jit_sti_i(r0, i0)		arm_sti_i(_jit, r0, i0)
__jit_inline void
arm_sti_i(jit_state_t _jit, void *i0, jit_gpr_t r0)
{
    jit_movi_i(JIT_TMP, (int)i0);
    if (jit_thumb_p())
	T2_STRI(r0, JIT_TMP, 0);
    else
	_STRI(r0, JIT_TMP, 0);
}

#define jit_stxr_i(r0, r1, r2)		arm_stxr_i(_jit, r0, r1, r2)
__jit_inline void
arm_stxr_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1, jit_gpr_t r2)
{
    if (jit_thumb_p()) {
	if ((r0|r1|r2) < 8)
	    T1_STR(r2, r1, r0);
	else
	    T2_STR(r2, r1, r0);
    }
    else
	_STR(r2, r1, r0);
}

#define jit_stxi_i(r0, r1, i0)		arm_stxi_i(_jit, r0, r1, i0)
__jit_inline void
arm_stxi_i(jit_state_t _jit, int i0, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if ((r0|r1) < 8 && i0 >= 0 && !(i0 & 3) && (i0 >> 2) < 0x20)
	    T1_STRI(r1, r0, i0 >> 2);
	else if (r0 == _R13 && r1 < 8 &&
		 i0 >= 0 && !(i0 & 3) && (i0 >> 2) <= 255)
	    T1_STRISP(r1, i0 >> 2);
	else if (i0 >= 0 && i0 <= 255)
	    T2_STRI(r1, r0, i0);
	else if (i0 < 0 && i0 >= -255)
	    T2_STRIN(r1, r0, -i0);
	else if (i0 >= 0 && i0 <= 4095)
	    T2_STRWI(r1, r0, i0);
	else {
	    jit_movi_i(JIT_TMP, (int)i0);
	    if ((r0|r1|JIT_TMP) < 8)
		T1_STR(r1, r0, JIT_TMP);
	    else
		T2_STR(r1, r0, JIT_TMP);
	}
    }
    else {
	if (i0 >= 0 && i0 <= 4095)
	    _STRI(r1, r0, i0);
	else if (i0 < 0 && i0 >= -4095)
	    _STRIN(r1, r0, -i0);
	else {
	    jit_movi_i(JIT_TMP, i0);
	    _STR(r1, r0, JIT_TMP);
	}
    }
}

#if __BYTE_ORDER == __LITTLE_ENDIAN
/* inline glibc htons (without register clobber) */
#define jit_ntoh_us(r0, r1)		arm_ntoh_us(_jit, r0, r1)
__jit_inline void
arm_ntoh_us(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_REV16(r0, r1);
	else
	    T2_REV16(r0, r1);
    }
    else {
	if (jit_armv6_p())
	    _REV16(r0, r1);
	else {
	    _LSLI(JIT_TMP, r1, 24);
	    _LSRI(r0, r1, 8);
	    _ORR_SI(r0, r0, JIT_TMP, ARM_LSR, 16);
	}
    }
}

/* inline glibc htonl (without register clobber) */
#define jit_ntoh_ui(r0, r1)		arm_ntoh_ui(_jit, r0, r1)
__jit_inline void
arm_ntoh_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_REV(r0, r1);
	else
	    T2_REV(r0, r1);
    }
    else {
	if (jit_armv6_p())
	    _REV(r0, r1);
	else {
	    _EOR_SI(JIT_TMP, r1, r1, ARM_ROR, 16);
	    _LSRI(JIT_TMP, JIT_TMP, 8);
	    _BICI(JIT_TMP, JIT_TMP, encode_arm_immediate(0xff00));
	    _EOR_SI(r0, JIT_TMP, r1, ARM_ROR, 8);
	}
    }
}
#endif

#define jit_extr_c_i(r0, r1)		arm_extr_c_i(_jit, r0, r1)
__jit_inline void
arm_extr_c_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_SXTB(r0, r1);
	else
	    T2_SXTB(r0, r1);
    }
    else {
	if (jit_armv6_p())
	    _SXTB(r0, r1);
	else {
	    _LSLI(r0, r1, 24);
	    _ASRI(r0, r0, 24);
	}
    }
}

#define jit_extr_c_ui(r0, r1)		arm_extr_c_ui(_jit, r0, r1)
__jit_inline void
arm_extr_c_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_UXTB(r0, r1);
	else
	    T2_UXTB(r0, r1);
    }
    else {
	if (jit_armv6_p())
	    _UXTB(r0, r1);
	else
	    _ANDI(r0, r1, 0xff);
    }
}

#define jit_extr_s_i(r0, r1)		arm_extr_s_i(_jit, r0, r1)
__jit_inline void
arm_extr_s_i(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_SXTH(r0, r1);
	else
	    T2_SXTH(r0, r1);
    }
    else {
	if (jit_armv6_p())
	    _SXTH(r0, r1);
	else {
	    _LSLI(r0, r1, 16);
	    _ASRI(r0, r0, 16);
	}
    }
}

#define jit_extr_s_ui(r0, r1)		arm_extr_s_ui(_jit, r0, r1)
__jit_inline void
arm_extr_s_ui(jit_state_t _jit, jit_gpr_t r0, jit_gpr_t r1)
{
    if (jit_thumb_p()) {
	if ((r0|r1) < 8)
	    T1_UXTH(r0, r1);
	else
	    T2_UXTH(r0, r1);
    }
    else {
	if (jit_armv6_p())
	    _UXTH(r0, r1);
	else {
	    /* _ANDI(r0, r1, 0xffff) needs more instructions */
	    _LSLI(r0, r1, 16);
	    _LSRI(r0, r0, 16);
	}
    }
}

#define jit_allocai(i0)			arm_allocai(_jit, i0)
__jit_inline int
arm_allocai(jit_state_t _jit, int i0)
{
    assert(i0 >= 0);
    _jitl.alloca_offset += i0;
    jit_patch_movi(_jitl.stack, (void *)
		   ((_jitl.alloca_offset + _jitl.stack_length + 7) & -8));
    return (-_jitl.alloca_offset);
}

#define jit_prolog(n)			arm_prolog(_jit, n)
__jit_inline void
arm_prolog(jit_state_t _jit, int i0)
{
    if (jit_thumb_p()) {
	/*  switch to thumb mode (better approach would be to
	 * or 1 address being called, but no clear distinction
	 * of what is a pointer to a jit function, or if patching
	 * a pointer to a jit function) */
	_ADDI(_R12, _R15, 1);
	_BX(_R12);
	if (!_jitl.after_prolog) {
	    _jitl.after_prolog = 1;
	    _jitl.thumb = _jit->x.pc;
	}  
	if (jit_hardfp_p()) {
	    T2_PUSH((1<<_R4)|(1<<_R5)|(1<<_R6)|(1<<_R7)|(1<<_R8)|(1<<_R9)|
		    /* previous fp and return address */
		    (1<<JIT_FP)|(1<<JIT_LR));
	    _VPUSH_F64(_D8, 8);
	    T2_PUSH(/* arguments (should keep state and only save "i0" registers) */
		    (1<<_R0)|(1<<_R1)|(1<<_R2)|(1<<_R3));
	}
	else
	    T2_PUSH(/* arguments (should keep state and only save "i0" registers) */
		    (1<<_R0)|(1<<_R1)|(1<<_R2)|(1<<_R3)|
		    (1<<_R4)|(1<<_R5)|(1<<_R6)|(1<<_R7)|(1<<_R8)|(1<<_R9)|
		    /* previous fp and return address */
		    (1<<JIT_FP)|(1<<JIT_LR));
	T2_MOV(JIT_FP, JIT_SP);
    }
    else {
	if (jit_hardfp_p()) {
	    _PUSH((1<<_R4)|(1<<_R5)|(1<<_R6)|(1<<_R7)|(1<<_R8)|(1<<_R9)|
		  /* previous fp and return address */
		  (1<<JIT_FP)|(1<<JIT_LR));
	    _VPUSH_F64(_D8, 8);
	    _PUSH(/* arguments (should keep state and only save "i0" registers) */
		  (1<<_R0)|(1<<_R1)|(1<<_R2)|(1<<_R3));
	}
	else
	    _PUSH(/* arguments (should keep state and only save "i0" registers) */
		  (1<<_R0)|(1<<_R1)|(1<<_R2)|(1<<_R3)|
		  (1<<_R4)|(1<<_R5)|(1<<_R6)|(1<<_R7)|(1<<_R8)|(1<<_R9)|
		  /* previous fp and return address */
		  (1<<JIT_FP)|(1<<JIT_LR));
	_MOV(JIT_FP, JIT_SP);
    }

    _jitl.nextarg_get = _jitl.nextarg_getf = 0;
    _jitl.framesize = JIT_FRAMESIZE;
    if (jit_hardfp_p())
	_jitl.framesize += 64;

    /* patch alloca and stack adjustment */
    _jitl.stack = (int *)_jit->x.pc;

    if (jit_swf_p())
	/* 6 soft double precision float registers */
	_jitl.alloca_offset = 48;
    else
	_jitl.alloca_offset = 0;

    jit_movi_p(JIT_TMP, (void *)_jitl.alloca_offset);
    jit_subr_i(JIT_SP, JIT_SP, JIT_TMP);
    _jitl.stack_length = _jitl.stack_offset = 0;
}

#define jit_callr(r0)			arm_callr(_jit, r0)
static void
arm_callr(jit_state_t _jit, jit_gpr_t r0)
{
    if (jit_thumb_p())
	T1_BLX(r0);
    else
	_BLX(r0);
}

#define jit_calli(i0)			arm_calli(_jit, i0)
__jit_inline jit_insn *
arm_calli(jit_state_t _jit, void *i0)
{
    jit_insn	*l;
    l = _jit->x.pc;
    jit_movi_p(JIT_TMP, i0);
    if (jit_thumb_p())
	T1_BLX(JIT_TMP);
    else
	_BLX(JIT_TMP);
    return (l);
}

#define jit_prepare_i(i0)		arm_prepare_i(_jit, i0)
__jit_inline void
arm_prepare_i(jit_state_t _jit, int i0)
{
    assert(i0 >= 0 && !_jitl.stack_offset && !_jitl.nextarg_put);
    _jitl.stack_offset = i0 << 2;
}

#define jit_arg_c()			arm_arg_i(_jit)
#define jit_arg_uc()			arm_arg_i(_jit)
#define jit_arg_s()			arm_arg_i(_jit)
#define jit_arg_us()			arm_arg_i(_jit)
#define jit_arg_i()			arm_arg_i(_jit)
#define jit_arg_ui()			arm_arg_i(_jit)
#define jit_arg_l()			arm_arg_i(_jit)
#define jit_arg_ul()			arm_arg_i(_jit)
#define jit_arg_p()			arm_arg_i(_jit)
__jit_inline int
arm_arg_i(jit_state_t _jit)
{
    int		ofs = _jitl.nextarg_get++;
    if (ofs > 3) {
	ofs = _jitl.framesize;
	_jitl.framesize += sizeof(int);
    }
    return (ofs);
}

#define jit_getarg_c(r0, i0)		arm_getarg_c(_jit, r0, i0)
__jit_inline void
arm_getarg_c(jit_state_t _jit, jit_gpr_t r0, int i0)
{
    if (i0 < 4)
	i0 <<= 2;
#if __BYTE_ORDER == __BIG_ENDIAN
    i0 += sizeof(int) - sizeof(char);
#endif
    jit_ldxi_c(r0, JIT_FP, i0);
}

#define jit_getarg_uc(r0, i0)		arm_getarg_uc(_jit, r0, i0)
__jit_inline void
arm_getarg_uc(jit_state_t _jit, jit_gpr_t r0, int i0)
{
    if (i0 < 4)
	i0 <<= 2;
#if __BYTE_ORDER == __BIG_ENDIAN
    i0 += sizeof(int) - sizeof(char);
#endif
    jit_ldxi_uc(r0, JIT_FP, i0);
}

#define jit_getarg_s(r0, i0)		arm_getarg_s(_jit, r0, i0)
__jit_inline void
arm_getarg_s(jit_state_t _jit, jit_gpr_t r0, int i0)
{
    if (i0 < 4)
	i0 <<= 2;
#if __BYTE_ORDER == __BIG_ENDIAN
    i0 += sizeof(int) - sizeof(short);
#endif
    jit_ldxi_s(r0, JIT_FP, i0);
}

#define jit_getarg_us(r0, i0)		arm_getarg_us(_jit, r0, i0)
__jit_inline void
arm_getarg_us(jit_state_t _jit, jit_gpr_t r0, int i0)
{
    if (i0 < 4)
	i0 <<= 2;
#if __BYTE_ORDER == __BIG_ENDIAN
    i0 += sizeof(int) - sizeof(short);
#endif
    jit_ldxi_us(r0, JIT_FP, i0);
}

#define jit_getarg_i(r0, i0)		arm_getarg_i(_jit, r0, i0)
#define jit_getarg_ui(r0, i0)		arm_getarg_i(_jit, r0, i0)
#define jit_getarg_l(r0, i0)		arm_getarg_i(_jit, r0, i0)
#define jit_getarg_ul(r0, i0)		arm_getarg_i(_jit, r0, i0)
#define jit_getarg_p(r0, i0)		arm_getarg_i(_jit, r0, i0)
__jit_inline void
arm_getarg_i(jit_state_t _jit, jit_gpr_t r0, int i0)
{
    /* arguments are saved in prolog */
    if (i0 < 4)
	i0 <<= 2;
    jit_ldxi_i(r0, JIT_FP, i0);
}

#define jit_pusharg_i(r0)		arm_pusharg_i(_jit, r0)
__jit_inline void
arm_pusharg_i(jit_state_t _jit, jit_gpr_t r0)
{
    int		ofs = _jitl.nextarg_put++;
    assert(ofs < 256);
    _jitl.stack_offset -= sizeof(int);
    _jitl.arguments[ofs] = (int *)_jit->x.pc;
    _jitl.types[ofs >> 5] &= ~(1 << (ofs & 31));
    /* force 32 bit instruction due to possibly needing to trasform it */
    if (jit_thumb_p())
	T2_STRWI(r0, JIT_SP, 0);
    else
	_STRI(r0, JIT_SP, 0);
}

static void
arm_patch_arguments(jit_state_t _jit)
{
    int		 reg;
    int		 ioff;
    int		 foff;
    int		 size;
    int		 index;
    jit_thumb_t	 thumb;
    int		 offset;
    union {
	_ui	*i;
	_us	*s;
    } u;

    ioff = foff = 0;
    for (index = _jitl.nextarg_put - 1, offset = 0; index >= 0; index--) {
	if (_jitl.types[index >> 5] & (1 << (index & 31)))
	    size = sizeof(double);
	else
	    size = sizeof(int);
	u.i = _jitl.arguments[index];
	if (jit_thumb_p()) {
	    code2thumb(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
	    switch (thumb.i & 0xfff00f00) {
		case ARM_CC_AL|ARM_VSTR|ARM_P:
		    if (jit_hardfp_p()) {
			if (foff < 16) {
			    reg = (thumb.i >> 12) & 0xf;
			    thumb.i = (ARM_CC_AL|ARM_VMOV_F |
				       ((foff >> 1) << 12) | reg);
			    if (foff & 1)
				thumb.i |= ARM_V_D;
			    thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
			    ++foff;
			    continue;
			}
		    }
		    else {
			if (ioff < 4) {
			    thumb.i = ((thumb.i & 0xfff0ff00) |
				       (JIT_FP << 16) | ioff);
			    thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
			    ++ioff;
			    continue;
			}
		    }
		    thumb.i = (thumb.i & 0xffffff00) | (offset >> 2);
		    thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
		    break;
		case ARM_CC_AL|ARM_VSTR|ARM_V_F64|ARM_P:
		    if (jit_hardfp_p()) {
			if (foff & 1)
			    ++foff;
			if (foff < 16) {
			    reg = (thumb.i >> 12) & 0xf;
			    thumb.i = (ARM_CC_AL|ARM_VMOV_F|ARM_V_F64 |
				       ((foff >> 1) << 12) | reg);
			    thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
			    foff += 2;
			    continue;
			}
		    }
		    else {
			if (ioff & 1)
			    ++ioff;
			if (ioff < 4) {
			    thumb.i = ((thumb.i & 0xfff0ff00) |
				       (JIT_FP << 16) | ioff);
			    thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
			    ioff += 2;
			    continue;
			}
		    }
		    if (offset & 7)
			offset += sizeof(int);
		    thumb.i = (thumb.i & 0xffffff00) | (offset >> 2);
		    thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
		    break;
		case THUMB2_STRWI:
		thumb_stri:
		    if (size == 8 && (ioff & 1))
			++ioff;
		    if (ioff < 4) {
			thumb.i = ((thumb.i & 0xfff0f000) |
				   (JIT_FP << 16) | (ioff << 2));
			thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
			++ioff;
			if (size == 8) {
			    code2thumb(thumb.s[0], thumb.s[1], u.s[2], u.s[3]);
			    thumb.i = ((thumb.i & 0xfff0f000) |
				       (JIT_FP << 16) | (ioff << 2));
			    thumb2code(thumb.s[0], thumb.s[1], u.s[2], u.s[3]);
			    ++ioff;
			}
			continue;
		    }
		    if (size == 8 && (offset & 7))
			offset += sizeof(int);
		    thumb.i = (thumb.i & 0xfffff000) | offset;
		    thumb2code(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
		    if (size == 8) {
			code2thumb(thumb.s[0], thumb.s[1], u.s[2], u.s[3]);
			thumb.i = (thumb.i & 0xfffff000) | (offset + 4);
			thumb2code(thumb.s[0], thumb.s[1], u.s[2], u.s[3]);
		    }
		    break;
		default:
		    /* offset too large */
		    if ((thumb.i & 0xfff00000) == THUMB2_STRWI)
			goto thumb_stri;
		    abort();
	    }
	}
	else {
	    switch (u.i[0] & 0xfff00f00) {
		case ARM_CC_AL|ARM_VSTR|ARM_P:
		    if (jit_hardfp_p()) {
			if (foff < 16) {
			    reg = (u.i[0] >> 12) & 0xf;
			    u.i[0] = (ARM_CC_AL|ARM_VMOV_F |
				      ((foff >> 1) << 12) | reg);
			    if (foff & 1)
				u.i[0] |= ARM_V_D;
			    ++foff;
			    continue;
			}
		    }
		    else {
			if (ioff < 4) {
			    u.i[0] = ((u.i[0] & 0xfff0ff00) |
				      (JIT_FP << 16) | ioff);
			    ++ioff;
			    continue;
			}
		    }
		    u.i[0] = (u.i[0] & 0xffffff00) | (offset >> 2);
		    break;
		case ARM_CC_AL|ARM_VSTR|ARM_V_F64|ARM_P:
		    if (jit_hardfp_p()) {
			if (foff & 1)
			    ++foff;
			if (foff < 16) {
			    reg = (u.i[0] >> 12) & 0xf;
			    u.i[0] = (ARM_CC_AL|ARM_VMOV_F|ARM_V_F64 |
				      ((foff >> 1) << 12) | reg);
			    foff += 2;
			    continue;
			}
		    }
		    else {
			if (ioff & 1)
			    ++ioff;
			if (ioff < 4) {
			    u.i[0] = ((u.i[0] & 0xfff0ff00) |
				      (JIT_FP << 16) | ioff);
			    ioff += 2;
			    continue;
			}
		    }
		    if (offset & 7)
			offset += sizeof(int);
		    u.i[0] = (u.i[0] & 0xffffff00) | (offset >> 2);
		    break;
		case ARM_CC_AL|ARM_STRI|ARM_P:
		arm_stri:
		    if (size == 8 && (ioff & 1))
			++ioff;
		    if (ioff < 4) {
			u.i[0] = ((u.i[0] & 0xfff0f000) |
				  (JIT_FP << 16) | (ioff << 2));
			++ioff;
			if (size == 8) {
			    u.i[1] = ((u.i[1] & 0xfff0f000) |
				      (JIT_FP << 16) | (ioff << 2));
			    ++ioff;
			}
			continue;
		    }
		    if (size == 8 && (offset & 7))
			offset += sizeof(int);
		    u.i[0] = (u.i[0] & 0xfffff000) | offset;
		    if (size == 8)
			u.i[1] = (u.i[1] & 0xfffff000) | (offset + 4);
		    break;
		default:
		    /* offset too large */
		    if ((u.i[0] & 0xfff00000) == (ARM_CC_AL|ARM_STRI|ARM_P))
			goto arm_stri;
		    abort();
	    }
	}
	offset += size;
    }
    _jitl.reglist = ((1 << ioff) - 1) & 0xf;
    if (_jitl.stack_length < offset) {
	_jitl.stack_length = offset;
	jit_patch_movi(_jitl.stack, (void *)
		       ((_jitl.alloca_offset +
			 _jitl.stack_length + 7) & -8));
    }
}

#define jit_finishr(rs)			arm_finishr(_jit, rs)
__jit_inline void
arm_finishr(jit_state_t _jit, jit_gpr_t r0)
{
    assert(!_jitl.stack_offset);
    if (r0 < 4) {
	jit_movr_i(JIT_TMP, r0);
	r0 = JIT_TMP;
    }
    arm_patch_arguments(_jit);
    _jitl.nextarg_put = 0;
    if (_jitl.reglist) {
	if (jit_thumb_p())
	    T2_LDMIA(JIT_FP, _jitl.reglist);
	else
	    _LDMIA(JIT_FP, _jitl.reglist);
	_jitl.reglist = 0;
    }
    jit_callr(r0);
}

#define jit_finish(i0)			arm_finishi(_jit, i0)
__jit_inline jit_insn *
arm_finishi(jit_state_t _jit, void *i0)
{
    assert(!_jitl.stack_offset);
    arm_patch_arguments(_jit);
    _jitl.nextarg_put = 0;
    if (_jitl.reglist) {
	if (jit_thumb_p())
	    T2_LDMIA(JIT_FP, _jitl.reglist);
	else
	    _LDMIA(JIT_FP, _jitl.reglist);
	_jitl.reglist = 0;
    }
    return (jit_calli(i0));
}

#define jit_retval_i(r0)		jit_movr_i(r0, JIT_RET)
#define jit_ret()			arm_ret(_jit)
__jit_inline void
arm_ret(jit_state_t _jit)
{
    /* do not restore arguments */
    jit_addi_i(JIT_SP, JIT_FP, 16);
    if (jit_hardfp_p())
	_VPOP_F64(_D8, 8);
    if (jit_thumb_p())
	T2_POP(/* callee save */
	       (1<<_R4)|(1<<_R5)|(1<<_R6)|(1<<_R7)|(1<<_R8)|(1<<_R9)|
	       /* previous fp and return address */
	       (1<<JIT_FP)|(1<<JIT_PC));
    else
	_POP(/* callee save */
	     (1<<_R4)|(1<<_R5)|(1<<_R6)|(1<<_R7)|(1<<_R8)|(1<<_R9)|
	     /* previous fp and return address */
	     (1<<JIT_FP)|(1<<JIT_PC));
    if (jit_thumb_p() && ((int)_jit->x.pc & 2))
	T1_NOP();
}

/* just to pass make check... */
#ifdef JIT_NEED_PUSH_POP
# define jit_pushr_i(r0)		arm_pushr_i(_jit, r0)
__jit_inline int
arm_pushr_i(jit_state_t _jit, jit_gpr_t r0)
{
    int		offset;
    assert(_jitl.pop < sizeof(_jitl.push) / sizeof(_jitl.push[0]));
    offset = jit_allocai(4);
    _jitl.push[_jitl.pop++] = offset;
    jit_stxi_i(offset, JIT_FP, r0);
}

# define jit_popr_i(r0)			arm_popr_i(_jit, r0)
__jit_inline int
arm_popr_i(jit_state_t _jit, jit_gpr_t r0)
{
    int		offset;
    assert(_jitl.pop > 0);
    offset = _jitl.push[--_jitl.pop];
    jit_ldxi_i(r0, JIT_FP, offset);
}
#endif

#endif /* __lightning_core_arm_h */