@@ -34,4 +34,6 @@
#define NB_MMU_MODES 15
+#define TARGET_TB_PCREL 1
+
#endif
@@ -12,6 +12,12 @@ typedef struct DisasContext {
/* The address of the current instruction being translated. */
target_ulong pc_curr;
+ /*
+ * For TARGET_TB_PCREL, the value relative to pc_curr against which
+ * offsets must be computed for cpu_pc. -1 if unknown due to jump.
+ */
+ target_ulong pc_save;
+ target_ulong pc_cond_save;
target_ulong page_start;
uint32_t insn;
/* Nonzero if this instruction has been conditionally skipped. */
@@ -64,17 +64,18 @@ static void arm_cpu_set_pc(CPUState *cs, vaddr value)
void arm_cpu_synchronize_from_tb(CPUState *cs,
const TranslationBlock *tb)
{
- ARMCPU *cpu = ARM_CPU(cs);
- CPUARMState *env = &cpu->env;
-
- /*
- * It's OK to look at env for the current mode here, because it's
- * never possible for an AArch64 TB to chain to an AArch32 TB.
- */
- if (is_a64(env)) {
- env->pc = tb_pc(tb);
- } else {
- env->regs[15] = tb_pc(tb);
+ /* The program counter is always up to date with TARGET_TB_PCREL. */
+ if (!TARGET_TB_PCREL) {
+ CPUARMState *env = cs->env_ptr;
+ /*
+ * It's OK to look at env for the current mode here, because it's
+ * never possible for an AArch64 TB to chain to an AArch32 TB.
+ */
+ if (is_a64(env)) {
+ env->pc = tb_pc(tb);
+ } else {
+ env->regs[15] = tb_pc(tb);
+ }
}
}
#endif /* CONFIG_TCG */
@@ -150,12 +150,18 @@ static void reset_btype(DisasContext *s)
static void gen_pc_plus_diff(DisasContext *s, TCGv_i64 dest, int diff)
{
- tcg_gen_movi_i64(dest, s->pc_curr + diff);
+ assert(s->pc_save != -1);
+ if (TARGET_TB_PCREL) {
+ tcg_gen_addi_i64(dest, cpu_pc, (s->pc_curr - s->pc_save) + diff);
+ } else {
+ tcg_gen_movi_i64(dest, s->pc_curr + diff);
+ }
}
void gen_a64_update_pc(DisasContext *s, int diff)
{
gen_pc_plus_diff(s, cpu_pc, diff);
+ s->pc_save = s->pc_curr + diff;
}
/*
@@ -209,6 +215,7 @@ static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src)
* then loading an address into the PC will clear out any tag.
*/
gen_top_byte_ignore(s, cpu_pc, src, s->tbii);
+ s->pc_save = -1;
}
/*
@@ -347,16 +354,22 @@ static void gen_exception_internal(int excp)
static void gen_exception_internal_insn(DisasContext *s, int pc_diff, int excp)
{
+ target_ulong pc_save = s->pc_save;
+
gen_a64_update_pc(s, pc_diff);
gen_exception_internal(excp);
s->base.is_jmp = DISAS_NORETURN;
+ s->pc_save = pc_save;
}
static void gen_exception_bkpt_insn(DisasContext *s, uint32_t syndrome)
{
+ target_ulong pc_save = s->pc_save;
+
gen_a64_update_pc(s, 0);
gen_helper_exception_bkpt_insn(cpu_env, tcg_constant_i32(syndrome));
s->base.is_jmp = DISAS_NORETURN;
+ s->pc_save = pc_save;
}
static void gen_step_complete_exception(DisasContext *s)
@@ -385,11 +398,16 @@ static inline bool use_goto_tb(DisasContext *s, uint64_t dest)
static void gen_goto_tb(DisasContext *s, int n, int diff)
{
- uint64_t dest = s->pc_curr + diff;
+ target_ulong pc_save = s->pc_save;
- if (use_goto_tb(s, dest)) {
- tcg_gen_goto_tb(n);
- gen_a64_update_pc(s, diff);
+ if (use_goto_tb(s, s->pc_curr + diff)) {
+ if (TARGET_TB_PCREL) {
+ gen_a64_update_pc(s, diff);
+ tcg_gen_goto_tb(n);
+ } else {
+ tcg_gen_goto_tb(n);
+ gen_a64_update_pc(s, diff);
+ }
tcg_gen_exit_tb(s->base.tb, n);
s->base.is_jmp = DISAS_NORETURN;
} else {
@@ -401,6 +419,7 @@ static void gen_goto_tb(DisasContext *s, int n, int diff)
s->base.is_jmp = DISAS_NORETURN;
}
}
+ s->pc_save = pc_save;
}
static void init_tmp_a64_array(DisasContext *s)
@@ -14717,7 +14736,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->isar = &arm_cpu->isar;
dc->condjmp = 0;
-
+ dc->pc_save = dc->base.pc_first;
dc->aarch64 = true;
dc->thumb = false;
dc->sctlr_b = 0;
@@ -14799,8 +14818,12 @@ static void aarch64_tr_tb_start(DisasContextBase *db, CPUState *cpu)
static void aarch64_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
+ target_ulong pc_arg = dc->base.pc_next;
- tcg_gen_insn_start(dc->base.pc_next, 0, 0);
+ if (TARGET_TB_PCREL) {
+ pc_arg &= ~TARGET_PAGE_MASK;
+ }
+ tcg_gen_insn_start(pc_arg, 0, 0);
dc->insn_start = tcg_last_op();
}
@@ -164,6 +164,7 @@ void arm_gen_condlabel(DisasContext *s)
if (!s->condjmp) {
s->condlabel = gen_new_label();
s->condjmp = 1;
+ s->pc_cond_save = s->pc_save;
}
}
@@ -278,7 +279,12 @@ static int jmp_diff(DisasContext *s, int diff)
static void gen_pc_plus_diff(DisasContext *s, TCGv_i32 var, int diff)
{
- tcg_gen_movi_i32(var, s->pc_curr + diff);
+ assert(s->pc_save != -1);
+ if (TARGET_TB_PCREL) {
+ tcg_gen_addi_i32(var, cpu_R[15], (s->pc_curr - s->pc_save) + diff);
+ } else {
+ tcg_gen_movi_i32(var, s->pc_curr + diff);
+ }
}
/* Set a variable to the value of a CPU register. */
@@ -321,6 +327,7 @@ void store_reg(DisasContext *s, int reg, TCGv_i32 var)
*/
tcg_gen_andi_i32(var, var, s->thumb ? ~1 : ~3);
s->base.is_jmp = DISAS_JUMP;
+ s->pc_save = -1;
} else if (reg == 13 && arm_dc_feature(s, ARM_FEATURE_M)) {
/* For M-profile SP bits [1:0] are always zero */
tcg_gen_andi_i32(var, var, ~3);
@@ -786,7 +793,8 @@ void gen_set_condexec(DisasContext *s)
void gen_update_pc(DisasContext *s, int diff)
{
- tcg_gen_movi_i32(cpu_R[15], s->pc_curr + diff);
+ gen_pc_plus_diff(s, cpu_R[15], diff);
+ s->pc_save = s->pc_curr + diff;
}
/* Set PC and Thumb state from var. var is marked as dead. */
@@ -796,6 +804,7 @@ static inline void gen_bx(DisasContext *s, TCGv_i32 var)
tcg_gen_andi_i32(cpu_R[15], var, ~1);
tcg_gen_andi_i32(var, var, 1);
store_cpu_field(var, thumb);
+ s->pc_save = -1;
}
/*
@@ -1118,6 +1127,8 @@ static void gen_exception(int excp, uint32_t syndrome)
static void gen_exception_insn_el_v(DisasContext *s, int pc_diff, int excp,
uint32_t syn, TCGv_i32 tcg_el)
{
+ target_ulong pc_save = s->pc_save;
+
if (s->aarch64) {
gen_a64_update_pc(s, pc_diff);
} else {
@@ -1126,6 +1137,7 @@ static void gen_exception_insn_el_v(DisasContext *s, int pc_diff, int excp,
}
gen_exception_el_v(excp, syn, tcg_el);
s->base.is_jmp = DISAS_NORETURN;
+ s->pc_save = pc_save;
}
void gen_exception_insn_el(DisasContext *s, int pc_diff, int excp,
@@ -1137,6 +1149,8 @@ void gen_exception_insn_el(DisasContext *s, int pc_diff, int excp,
void gen_exception_insn(DisasContext *s, int pc_diff, int excp, uint32_t syn)
{
+ target_ulong pc_save = s->pc_save;
+
if (s->aarch64) {
gen_a64_update_pc(s, pc_diff);
} else {
@@ -1145,6 +1159,7 @@ void gen_exception_insn(DisasContext *s, int pc_diff, int excp, uint32_t syn)
}
gen_exception(excp, syn);
s->base.is_jmp = DISAS_NORETURN;
+ s->pc_save = pc_save;
}
static void gen_exception_bkpt_insn(DisasContext *s, uint32_t syn)
@@ -2612,11 +2627,14 @@ static void gen_goto_ptr(void)
*/
static void gen_goto_tb(DisasContext *s, int n, int diff)
{
- target_ulong dest = s->pc_curr + diff;
-
- if (translator_use_goto_tb(&s->base, dest)) {
- tcg_gen_goto_tb(n);
- gen_update_pc(s, diff);
+ if (translator_use_goto_tb(&s->base, s->pc_curr + diff)) {
+ if (TARGET_TB_PCREL) {
+ gen_update_pc(s, diff);
+ tcg_gen_goto_tb(n);
+ } else {
+ tcg_gen_goto_tb(n);
+ gen_update_pc(s, diff);
+ }
tcg_gen_exit_tb(s->base.tb, n);
} else {
gen_update_pc(s, diff);
@@ -2628,10 +2646,13 @@ static void gen_goto_tb(DisasContext *s, int n, int diff)
/* Jump, specifying which TB number to use if we gen_goto_tb() */
static void gen_jmp_tb(DisasContext *s, int diff, int tbno)
{
+ target_ulong pc_save = s->pc_save;
+
if (unlikely(s->ss_active)) {
/* An indirect jump so that we still trigger the debug exception. */
gen_update_pc(s, diff);
s->base.is_jmp = DISAS_JUMP;
+ s->pc_save = pc_save;
return;
}
switch (s->base.is_jmp) {
@@ -2667,6 +2688,7 @@ static void gen_jmp_tb(DisasContext *s, int diff, int tbno)
*/
g_assert_not_reached();
}
+ s->pc_save = pc_save;
}
static inline void gen_jmp(DisasContext *s, int diff)
@@ -9333,7 +9355,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
dc->isar = &cpu->isar;
dc->condjmp = 0;
-
+ dc->pc_save = dc->base.pc_first;
dc->aarch64 = false;
dc->thumb = EX_TBFLAG_AM32(tb_flags, THUMB);
dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE;
@@ -9488,13 +9510,17 @@ static void arm_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
* fields here.
*/
uint32_t condexec_bits;
+ target_ulong pc_arg = dc->base.pc_next;
+ if (TARGET_TB_PCREL) {
+ pc_arg &= ~TARGET_PAGE_MASK;
+ }
if (dc->eci) {
condexec_bits = dc->eci << 4;
} else {
condexec_bits = (dc->condexec_cond << 4) | (dc->condexec_mask >> 1);
}
- tcg_gen_insn_start(dc->base.pc_next, condexec_bits, 0);
+ tcg_gen_insn_start(pc_arg, condexec_bits, 0);
dc->insn_start = tcg_last_op();
}
@@ -9537,7 +9563,10 @@ static bool arm_check_ss_active(DisasContext *dc)
static void arm_post_translate_insn(DisasContext *dc)
{
- if (dc->condjmp && !dc->base.is_jmp) {
+ if (dc->condjmp && dc->base.is_jmp == DISAS_NEXT) {
+ if (dc->pc_save != dc->pc_cond_save) {
+ gen_update_pc(dc, dc->pc_cond_save - dc->pc_save);
+ }
gen_set_label(dc->condlabel);
dc->condjmp = 0;
}
@@ -9867,6 +9896,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
if (dc->condjmp) {
/* "Condition failed" instruction codepath for the branch/trap insn */
+ dc->pc_save = dc->pc_cond_save;
gen_set_label(dc->condlabel);
gen_set_condexec(dc);
if (unlikely(dc->ss_active)) {
@@ -9929,11 +9959,19 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb,
target_ulong *data)
{
if (is_a64(env)) {
- env->pc = data[0];
+ if (TARGET_TB_PCREL) {
+ env->pc = (env->pc & TARGET_PAGE_MASK) | data[0];
+ } else {
+ env->pc = data[0];
+ }
env->condexec_bits = 0;
env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT;
} else {
- env->regs[15] = data[0];
+ if (TARGET_TB_PCREL) {
+ env->regs[15] = (env->regs[15] & TARGET_PAGE_MASK) | data[0];
+ } else {
+ env->regs[15] = data[0];
+ }
env->condexec_bits = data[1];
env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT;
}
Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/arm/cpu-param.h | 2 ++ target/arm/translate.h | 6 ++++ target/arm/cpu.c | 23 +++++++------- target/arm/translate-a64.c | 37 ++++++++++++++++++----- target/arm/translate.c | 62 ++++++++++++++++++++++++++++++-------- 5 files changed, 100 insertions(+), 30 deletions(-)