tcg/arm: Restrict constant pool displacement to 12 bits

This will not necessarily restrict the size of the TB, since for v7
the majority of constant pool usage is for calls from the out-of-line
ldst code, which is already at the end of the TB.  But this does
allow us to save one insn per reference on the off-chance.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2019-04-25 10:39:39 -07:00
parent a7cdaf710f
commit b4b82d7e9c

View File

@ -197,6 +197,24 @@ static inline bool reloc_pc24(tcg_insn_unit *code_ptr, tcg_insn_unit *target)
return false; return false;
} }
static inline bool reloc_pc13(tcg_insn_unit *code_ptr, tcg_insn_unit *target)
{
ptrdiff_t offset = tcg_ptr_byte_diff(target, code_ptr) - 8;
if (offset >= -0xfff && offset <= 0xfff) {
tcg_insn_unit insn = *code_ptr;
bool u = (offset >= 0);
if (!u) {
offset = -offset;
}
insn = deposit32(insn, 23, 1, u);
insn = deposit32(insn, 0, 12, offset);
*code_ptr = insn;
return true;
}
return false;
}
static bool patch_reloc(tcg_insn_unit *code_ptr, int type, static bool patch_reloc(tcg_insn_unit *code_ptr, int type,
intptr_t value, intptr_t addend) intptr_t value, intptr_t addend)
{ {
@ -205,39 +223,10 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type,
if (type == R_ARM_PC24) { if (type == R_ARM_PC24) {
return reloc_pc24(code_ptr, (tcg_insn_unit *)value); return reloc_pc24(code_ptr, (tcg_insn_unit *)value);
} else if (type == R_ARM_PC13) { } else if (type == R_ARM_PC13) {
intptr_t diff = value - (uintptr_t)(code_ptr + 2); return reloc_pc13(code_ptr, (tcg_insn_unit *)value);
tcg_insn_unit insn = *code_ptr;
bool u;
if (diff >= -0xfff && diff <= 0xfff) {
u = (diff >= 0);
if (!u) {
diff = -diff;
}
} else {
int rd = extract32(insn, 12, 4);
int rt = rd == TCG_REG_PC ? TCG_REG_TMP : rd;
if (diff < 0x1000 || diff >= 0x100000) {
return false;
}
/* add rt, pc, #high */
*code_ptr++ = ((insn & 0xf0000000) | (1 << 25) | ARITH_ADD
| (TCG_REG_PC << 16) | (rt << 12)
| (20 << 7) | (diff >> 12));
/* ldr rd, [rt, #low] */
insn = deposit32(insn, 12, 4, rt);
diff &= 0xfff;
u = 1;
}
insn = deposit32(insn, 23, 1, u);
insn = deposit32(insn, 0, 12, diff);
*code_ptr = insn;
} else { } else {
g_assert_not_reached(); g_assert_not_reached();
} }
return true;
} }
#define TCG_CT_CONST_ARM 0x100 #define TCG_CT_CONST_ARM 0x100
@ -605,12 +594,8 @@ static inline void tcg_out_ld8s_r(TCGContext *s, int cond, TCGReg rt,
static void tcg_out_movi_pool(TCGContext *s, int cond, int rd, uint32_t arg) static void tcg_out_movi_pool(TCGContext *s, int cond, int rd, uint32_t arg)
{ {
/* The 12-bit range on the ldr insn is sometimes a bit too small.
In order to get around that we require two insns, one of which
will usually be a nop, but may be replaced in patch_reloc. */
new_pool_label(s, arg, R_ARM_PC13, s->code_ptr, 0); new_pool_label(s, arg, R_ARM_PC13, s->code_ptr, 0);
tcg_out_ld32_12(s, cond, rd, TCG_REG_PC, 0); tcg_out_ld32_12(s, cond, rd, TCG_REG_PC, 0);
tcg_out_nop(s);
} }
static void tcg_out_movi32(TCGContext *s, int cond, int rd, uint32_t arg) static void tcg_out_movi32(TCGContext *s, int cond, int rd, uint32_t arg)
@ -1069,8 +1054,8 @@ static void tcg_out_call(TCGContext *s, tcg_insn_unit *addr)
tcg_out_movi32(s, COND_AL, TCG_REG_TMP, addri); tcg_out_movi32(s, COND_AL, TCG_REG_TMP, addri);
tcg_out_blx(s, COND_AL, TCG_REG_TMP); tcg_out_blx(s, COND_AL, TCG_REG_TMP);
} else { } else {
/* ??? Know that movi_pool emits exactly 2 insns. */ /* ??? Know that movi_pool emits exactly 1 insn. */
tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R14, TCG_REG_PC, 4); tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R14, TCG_REG_PC, 0);
tcg_out_movi_pool(s, COND_AL, TCG_REG_PC, addri); tcg_out_movi_pool(s, COND_AL, TCG_REG_PC, addri);
} }
} }