summaryrefslogtreecommitdiff
path: root/lightening/arm-cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lightening/arm-cpu.c')
-rw-r--r--lightening/arm-cpu.c87
1 files changed, 52 insertions, 35 deletions
diff --git a/lightening/arm-cpu.c b/lightening/arm-cpu.c
index d96d57b2d..0e38883ef 100644
--- a/lightening/arm-cpu.c
+++ b/lightening/arm-cpu.c
@@ -186,6 +186,8 @@
#define _NOREG (jit_gpr_regno(_PC))
+#define JIT_RELOC_B JIT_RELOC_FLAG_0
+
static void
emit_wide_thumb(jit_state_t *_jit, uint32_t inst)
{
@@ -265,9 +267,12 @@ write_wide_thumb(uint32_t *loc, uint32_t v)
}
static int
-offset_in_jmp_range(int32_t offset)
+offset_in_jmp_range(int32_t offset, int flags)
{
- return -0x800000 <= offset && offset <= 0x7fffff;
+ if (!(offset & 1) && flags | JIT_RELOC_B)
+ return 0;
+ else
+ return -0x1000000 <= offset && offset <= 0xffffff;
}
static int32_t
@@ -287,7 +292,7 @@ decode_thumb_jump(uint32_t v)
ret |= i2 << 21;
ret |= hi << 11;
ret |= lo;
- return ret;
+ return ret << 1;
}
static const uint32_t thumb_jump_mask = 0xf800d000;
@@ -295,14 +300,15 @@ static const uint32_t thumb_jump_mask = 0xf800d000;
static uint32_t
encode_thumb_jump(int32_t v)
{
- ASSERT(offset_in_jmp_range(v));
+ ASSERT(offset_in_jmp_range(v, 0));
+ v >>= 1;
uint32_t s = !!(v & 0x800000);
uint32_t i1 = !!(v & 0x400000);
uint32_t i2 = !!(v & 0x200000);
uint32_t j1 = s ? i1 : !i1;
uint32_t j2 = s ? i2 : !i2;
uint32_t ret = (s<<26)|((v&0x1ff800)<<5)|(j1<<13)|(j2<<11)|(v&0x7ff);
- ASSERT(decode_thumb_jump(ret) == v);
+ ASSERT(decode_thumb_jump(ret) == v << 1);
ASSERT((ret & thumb_jump_mask) == 0);
return ret;
}
@@ -310,7 +316,13 @@ encode_thumb_jump(int32_t v)
static uint32_t
patch_thumb_jump(uint32_t inst, int32_t v)
{
- return (inst & thumb_jump_mask) | encode_thumb_jump(v);
+ inst &= thumb_jump_mask;
+ if (!(v & 1)) {
+ ASSERT(inst == THUMB2_BLI || inst == THUMB2_BLXI);
+ v = (v + 2) & ~2;
+ inst = THUMB2_BLXI;
+ }
+ return inst | encode_thumb_jump(v);
}
static int32_t
@@ -325,15 +337,23 @@ patch_jmp_offset(uint32_t *loc, int32_t v)
write_wide_thumb(loc, patch_thumb_jump(read_wide_thumb(loc), v));
}
+static void
+patch_veneer_jmp_offset(uint32_t *loc, int32_t v)
+{
+ ASSERT(!(v & 1));
+ patch_jmp_offset(loc, v | 1);
+}
+
static jit_reloc_t
emit_thumb_jump(jit_state_t *_jit, uint32_t inst)
{
while (1) {
uint8_t *pc_base = _jit->pc.uc + 4;
- uint8_t rsh = 1;
- int32_t off = (_jit->pc.uc - pc_base) >> rsh;
- jit_reloc_t ret =
- jit_reloc (_jit, JIT_RELOC_JMP_WITH_VENEER, 0, _jit->pc.uc, pc_base, rsh);
+ int32_t off = (uint8_t*)jit_address(_jit) - pc_base;
+ enum jit_reloc_kind kind = JIT_RELOC_JMP_WITH_VENEER;
+ if (inst == THUMB2_B)
+ kind |= JIT_RELOC_B;
+ jit_reloc_t ret = jit_reloc (_jit, kind, 0, _jit->pc.uc, pc_base, 0);
uint8_t thumb_jump_width = 24;
if (add_pending_literal(_jit, ret, thumb_jump_width - 1)) {
emit_wide_thumb(_jit, patch_thumb_jump(inst, off));
@@ -343,9 +363,12 @@ emit_thumb_jump(jit_state_t *_jit, uint32_t inst)
}
static int
-offset_in_jcc_range(int32_t v)
+offset_in_jcc_range(int32_t v, int flags)
{
- return -0x80000 <= v && v <= 0x7ffff;
+ if (!(v & 1))
+ return 0;
+ else
+ return -0x100000 <= v && v <= 0xfffff;
}
static int32_t
@@ -363,7 +386,7 @@ decode_thumb_cc_jump(uint32_t v)
ret |= j1 << 17;
ret |= hi << 11;
ret |= lo;
- return ret;
+ return ret << 1;
}
static const uint32_t thumb_cc_jump_mask = 0xfbc0d000;
@@ -371,14 +394,15 @@ static const uint32_t thumb_cc_jump_mask = 0xfbc0d000;
static uint32_t
encode_thumb_cc_jump(int32_t v)
{
- ASSERT(offset_in_jcc_range(v));
+ ASSERT(offset_in_jcc_range(v, 0));
+ v >>= 1;
uint32_t s = !!(v & 0x80000);
uint32_t j2 = !!(v & 0x40000);
uint32_t j1 = !!(v & 0x20000);
uint32_t hi = (v >> 11) & 0x3f;
uint32_t lo = v & 0x7ff;
uint32_t ret = (s<<26)|(hi << 16)|(j1<<13)|(j2<<11)|lo;
- ASSERT(decode_thumb_cc_jump(ret) == v);
+ ASSERT(decode_thumb_cc_jump(ret) == v << 1);
ASSERT((ret & thumb_cc_jump_mask) == 0);
return ret;
}
@@ -401,15 +425,21 @@ patch_jcc_offset(uint32_t *loc, int32_t v)
write_wide_thumb(loc, patch_thumb_cc_jump(read_wide_thumb(loc), v));
}
+static void
+patch_veneer_jcc_offset(uint32_t *loc, int32_t v)
+{
+ ASSERT(!(v & 1));
+ patch_jcc_offset(loc, v | 1);
+}
+
static jit_reloc_t
emit_thumb_cc_jump(jit_state_t *_jit, uint32_t inst)
{
while (1) {
uint8_t *pc_base = _jit->pc.uc + 4;
- uint8_t rsh = 1;
- int32_t off = (_jit->pc.uc - pc_base) >> rsh;
+ int32_t off = (uint8_t*)jit_address(_jit) - pc_base;
jit_reloc_t ret =
- jit_reloc (_jit, JIT_RELOC_JCC_WITH_VENEER, 0, _jit->pc.uc, pc_base, rsh);
+ jit_reloc (_jit, JIT_RELOC_JCC_WITH_VENEER, 0, _jit->pc.uc, pc_base, 0);
uint8_t thumb_cc_jump_width = 20;
if (add_pending_literal(_jit, ret, thumb_cc_jump_width - 1)) {
emit_wide_thumb(_jit, patch_thumb_cc_jump(inst, off));
@@ -1028,12 +1058,6 @@ T2_BLI(jit_state_t *_jit)
return tb(_jit, THUMB2_BLI);
}
-static jit_reloc_t
-T2_BLXI(jit_state_t *_jit)
-{
- return tb(_jit, THUMB2_BLXI);
-}
-
enum dmb_option { DMB_ISH = 0xb };
static void
T1_DMB(jit_state_t *_jit, enum dmb_option option)
@@ -2052,7 +2076,7 @@ rshi_u(jit_state_t *_jit, int32_t r0, int32_t r1, jit_word_t i0)
static void
jmpr(jit_state_t *_jit, int32_t r0)
{
- T1_MOV(_jit, jit_gpr_regno(_PC), r0);
+ T1_BX(_jit, r0);
}
static jit_reloc_t
@@ -2064,8 +2088,6 @@ jmp(jit_state_t *_jit)
static void
jmpi(jit_state_t *_jit, jit_word_t i0)
{
- /* Strip thumb bit, if any. */
- i0 &= ~1;
return jit_patch_there(_jit, jmp(_jit), (void*)i0);
}
@@ -2905,10 +2927,7 @@ callr(jit_state_t *_jit, int32_t r0)
static void
calli(jit_state_t *_jit, jit_word_t i0)
{
- if (i0 & 1)
- jit_patch_there(_jit, T2_BLI(_jit), (void*)(i0 & ~1));
- else
- jit_patch_there(_jit, T2_BLXI(_jit), (void*)i0);
+ jit_patch_there(_jit, T2_BLI(_jit), (void*)i0);
}
static void
@@ -2990,8 +3009,7 @@ static void
patch_jmp_without_veneer(jit_state_t *_jit, uint32_t *loc)
{
uint8_t *pc_base = ((uint8_t *)loc) + 4;
- uint8_t rsh = 1;
- int32_t off = (_jit->pc.uc - pc_base) >> rsh;
+ int32_t off = (uint8_t*)jit_address(_jit) - pc_base;
write_wide_thumb(loc, THUMB2_B | encode_thumb_jump(off));
}
@@ -3014,11 +3032,10 @@ emit_veneer(jit_state_t *_jit, jit_pointer_t target)
{
uint16_t thumb1_ldr = 0x4800;
int32_t tmp = jit_gpr_regno(JIT_TMP1);
- int32_t rd = jit_gpr_regno(_PC);
ASSERT(tmp < 8);
// Loaded addr is 4 bytes after the LDR, which is aligned, so offset is 0.
emit_u16(_jit, thumb1_ldr | (tmp << 8));
- emit_u16(_jit, THUMB_MOV|((_u4(rd)&8)<<4)|(_u4(tmp)<<3)|(rd&7));
+ emit_u16(_jit, THUMB_BX|(_u4(tmp)<<3));
emit_u32(_jit, (uint32_t) target);
}