target/arm: Introduce add_reg_for_lit

Provide a common routine for the places that require ALIGN(PC, 4)
as the base address as opposed to plain PC. The two are always
the same for A32, but the difference is meaningful for thumb mode.

Backports commit 16e0d8234ef9291747332d2c431e46808a060472 from qemu
This commit is contained in:
Richard Henderson 2019-11-18 17:07:18 -05:00 committed by Lioncash
parent 1c0914e58c
commit a2e60445de
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
2 changed files with 83 additions and 120 deletions

View file

@ -958,13 +958,8 @@ static bool trans_VLDR_VSTR_sp(DisasContext *s, arg_VLDR_VSTR_sp *a)
offset = -offset; offset = -offset;
} }
if (s->thumb && a->rn == 15) { /* For thumb, use of PC is UNPREDICTABLE. */
/* This is actually UNPREDICTABLE */ addr = add_reg_for_lit(s, a->rn, offset);
addr = tcg_temp_new_i32(tcg_ctx);
tcg_gen_movi_i32(tcg_ctx, addr, s->pc & ~2);
} else {
addr = load_reg(s, a->rn);
}
tcg_gen_addi_i32(tcg_ctx, addr, addr, offset); tcg_gen_addi_i32(tcg_ctx, addr, addr, offset);
tmp = tcg_temp_new_i32(tcg_ctx); tmp = tcg_temp_new_i32(tcg_ctx);
if (a->l) { if (a->l) {
@ -1001,13 +996,8 @@ static bool trans_VLDR_VSTR_dp(DisasContext *s, arg_VLDR_VSTR_dp *a)
offset = -offset; offset = -offset;
} }
if (s->thumb && a->rn == 15) { /* For thumb, use of PC is UNPREDICTABLE. */
/* This is actually UNPREDICTABLE */ addr = add_reg_for_lit(s, a->rn, offset);
addr = tcg_temp_new_i32(tcg_ctx);
tcg_gen_movi_i32(tcg_ctx, addr, s->pc & ~2);
} else {
addr = load_reg(s, a->rn);
}
tcg_gen_addi_i32(tcg_ctx, addr, addr, offset); tcg_gen_addi_i32(tcg_ctx, addr, addr, offset);
tmp = tcg_temp_new_i64(tcg_ctx); tmp = tcg_temp_new_i64(tcg_ctx);
if (a->l) { if (a->l) {
@ -1048,13 +1038,8 @@ static bool trans_VLDM_VSTM_sp(DisasContext *s, arg_VLDM_VSTM_sp *a)
return true; return true;
} }
if (s->thumb && a->rn == 15) { /* For thumb, use of PC is UNPREDICTABLE. */
/* This is actually UNPREDICTABLE */ addr = add_reg_for_lit(s, a->rn, 0);
addr = tcg_temp_new_i32(tcg_ctx);
tcg_gen_movi_i32(tcg_ctx, addr, s->pc & ~2);
} else {
addr = load_reg(s, a->rn);
}
if (a->p) { if (a->p) {
/* pre-decrement */ /* pre-decrement */
tcg_gen_addi_i32(tcg_ctx, addr, addr, -(a->imm << 2)); tcg_gen_addi_i32(tcg_ctx, addr, addr, -(a->imm << 2));
@ -1132,13 +1117,8 @@ static bool trans_VLDM_VSTM_dp(DisasContext *s, arg_VLDM_VSTM_dp *a)
return true; return true;
} }
if (s->thumb && a->rn == 15) { /* For thumb, use of PC is UNPREDICTABLE. */
/* This is actually UNPREDICTABLE */ addr = add_reg_for_lit(s, a->rn, 0);
addr = tcg_temp_new_i32(tcg_ctx);
tcg_gen_movi_i32(tcg_ctx, addr, s->pc & ~2);
} else {
addr = load_reg(s, a->rn);
}
if (a->p) { if (a->p) {
/* pre-decrement */ /* pre-decrement */
tcg_gen_addi_i32(tcg_ctx, addr, addr, -(a->imm << 2)); tcg_gen_addi_i32(tcg_ctx, addr, addr, -(a->imm << 2));

View file

@ -219,6 +219,24 @@ static inline TCGv_i32 load_reg(DisasContext *s, int reg)
return tmp; return tmp;
} }
/*
* Create a new temp, REG + OFS, except PC is ALIGN(PC, 4).
* This is used for load/store for which use of PC implies (literal),
* or ADD that implies ADR.
*/
static TCGv_i32 add_reg_for_lit(DisasContext *s, int reg, int ofs)
{
TCGContext *tcg_ctx = s->uc->tcg_ctx;
TCGv_i32 tmp = tcg_temp_new_i32(tcg_ctx);
if (reg == 15) {
tcg_gen_movi_i32(tcg_ctx, tmp, (read_pc(s) & ~3) + ofs);
} else {
tcg_gen_addi_i32(tcg_ctx, tmp, tcg_ctx->cpu_R[reg], ofs);
}
return tmp;
}
/* Set a CPU register. The source must be a temporary and will be /* Set a CPU register. The source must be a temporary and will be
marked as dead. */ marked as dead. */
static void store_reg(DisasContext *s, int reg, TCGv_i32 var) static void store_reg(DisasContext *s, int reg, TCGv_i32 var)
@ -9636,16 +9654,12 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
*/ */
bool wback = extract32(insn, 21, 1); bool wback = extract32(insn, 21, 1);
if (rn == 15) { if (rn == 15 && (insn & (1 << 21))) {
if (insn & (1 << 21)) {
/* UNPREDICTABLE */ /* UNPREDICTABLE */
goto illegal_op; goto illegal_op;
} }
addr = tcg_temp_new_i32(tcg_ctx);
tcg_gen_movi_i32(tcg_ctx, addr, s->pc & ~3); addr = add_reg_for_lit(s, rn, 0);
} else {
addr = load_reg(s, rn);
}
offset = (insn & 0xff) * 4; offset = (insn & 0xff) * 4;
if ((insn & (1 << 23)) == 0) { if ((insn & (1 << 23)) == 0) {
offset = 0-offset; offset = 0-offset;
@ -10846,21 +10860,10 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
store_reg(s, rd, tmp); store_reg(s, rd, tmp);
} else { } else {
/* Add/sub 12-bit immediate. */ /* Add/sub 12-bit immediate. */
if (rn == 15) { if (insn & (1 << 23)) {
offset = s->pc & ~(uint32_t)3; imm = -imm;
if (insn & (1 << 23)) }
offset -= imm; tmp = add_reg_for_lit(s, rn, imm);
else
offset += imm;
tmp = tcg_temp_new_i32(tcg_ctx);
tcg_gen_movi_i32(tcg_ctx, tmp, offset);
store_reg(s, rd, tmp);
} else {
tmp = load_reg(s, rn);
if (insn & (1 << 23))
tcg_gen_subi_i32(tcg_ctx, tmp, tmp, imm);
else
tcg_gen_addi_i32(tcg_ctx, tmp, tmp, imm);
if (rn == 13 && rd == 13) { if (rn == 13 && rd == 13) {
/* ADD SP, SP, imm or SUB SP, SP, imm */ /* ADD SP, SP, imm or SUB SP, SP, imm */
store_sp_checked(s, tmp); store_sp_checked(s, tmp);
@ -10869,7 +10872,6 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
} }
} }
} }
}
} else { } else {
/* /*
* 0b1111_0x0x_xxxx_0xxx_xxxx_xxxx * 0b1111_0x0x_xxxx_0xxx_xxxx_xxxx
@ -10980,23 +10982,15 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
} }
} }
memidx = get_mem_index(s); memidx = get_mem_index(s);
if (rn == 15) { imm = insn & 0xfff;
addr = tcg_temp_new_i32(tcg_ctx); if (insn & (1 << 23)) {
/* PC relative. */ /* PC relative or Positive offset. */
/* s->pc has already been incremented by 4. */ addr = add_reg_for_lit(s, rn, imm);
imm = s->pc & 0xfffffffc; } else if (rn == 15) {
if (insn & (1 << 23)) /* PC relative with negative offset. */
imm += insn & 0xfff; addr = add_reg_for_lit(s, rn, -imm);
else
imm -= insn & 0xfff;
tcg_gen_movi_i32(tcg_ctx, addr, imm);
} else { } else {
addr = load_reg(s, rn); addr = load_reg(s, rn);
if (insn & (1 << 23)) {
/* Positive offset. */
imm = insn & 0xfff;
tcg_gen_addi_i32(tcg_ctx, addr, addr, imm);
} else {
imm = insn & 0xff; imm = insn & 0xff;
switch ((insn >> 8) & 0xf) { switch ((insn >> 8) & 0xf) {
case 0x0: /* Shifted Register. */ case 0x0: /* Shifted Register. */
@ -11006,27 +11000,28 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
goto illegal_op; goto illegal_op;
} }
tmp = load_reg(s, rm); tmp = load_reg(s, rm);
if (shift) if (shift) {
tcg_gen_shli_i32(tcg_ctx, tmp, tmp, shift); tcg_gen_shli_i32(tcg_ctx, tmp, tmp, shift);
}
tcg_gen_add_i32(tcg_ctx, addr, addr, tmp); tcg_gen_add_i32(tcg_ctx, addr, addr, tmp);
tcg_temp_free_i32(tcg_ctx, tmp); tcg_temp_free_i32(tcg_ctx, tmp);
break; break;
case 0xc: /* Negative offset. */ case 0xc: /* Negative offset. */
tcg_gen_addi_i32(tcg_ctx, addr, addr, 0-imm); tcg_gen_addi_i32(tcg_ctx, addr, addr, -imm);
break; break;
case 0xe: /* User privilege. */ case 0xe: /* User privilege. */
tcg_gen_addi_i32(tcg_ctx, addr, addr, imm); tcg_gen_addi_i32(tcg_ctx, addr, addr, imm);
memidx = get_a32_user_mem_index(s); memidx = get_a32_user_mem_index(s);
break; break;
case 0x9: /* Post-decrement. */ case 0x9: /* Post-decrement. */
imm = 0-imm; imm = -imm;
/* Fall through. */ /* Fall through. */
case 0xb: /* Post-increment. */ case 0xb: /* Post-increment. */
postinc = 1; postinc = 1;
writeback = 1; writeback = 1;
break; break;
case 0xd: /* Pre-decrement. */ case 0xd: /* Pre-decrement. */
imm = 0-imm; imm = -imm;
/* Fall through. */ /* Fall through. */
case 0xf: /* Pre-increment. */ case 0xf: /* Pre-increment. */
writeback = 1; writeback = 1;
@ -11036,7 +11031,6 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
goto illegal_op; goto illegal_op;
} }
} }
}
issinfo = writeback ? ISSInvalid : rs; issinfo = writeback ? ISSInvalid : rs;
@ -11231,10 +11225,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn)
if (insn & (1 << 11)) { if (insn & (1 << 11)) {
rd = (insn >> 8) & 7; rd = (insn >> 8) & 7;
/* load pc-relative. Bit 1 of PC is ignored. */ /* load pc-relative. Bit 1 of PC is ignored. */
val = read_pc(s) + 2 + ((insn & 0xff) * 4); addr = add_reg_for_lit(s, 15, (insn & 0xff) * 4);
val &= ~(uint32_t)2;
addr = tcg_temp_new_i32(tcg_ctx);
tcg_gen_movi_i32(tcg_ctx, addr, val);
tmp = tcg_temp_new_i32(tcg_ctx); tmp = tcg_temp_new_i32(tcg_ctx);
gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s),
rd | ISSIs16Bit); rd | ISSIs16Bit);
@ -11612,16 +11603,8 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn)
* - Add PC/SP (immediate) * - Add PC/SP (immediate)
*/ */
rd = (insn >> 8) & 7; rd = (insn >> 8) & 7;
if (insn & (1 << 11)) {
/* SP */
tmp = load_reg(s, 13);
} else {
/* PC. bit 1 is ignored. */
tmp = tcg_temp_new_i32(tcg_ctx);
tcg_gen_movi_i32(tcg_ctx, tmp, read_pc(s) & ~(uint32_t)2);
}
val = (insn & 0xff) * 4; val = (insn & 0xff) * 4;
tcg_gen_addi_i32(tcg_ctx, tmp, tmp, val); tmp = add_reg_for_lit(s, insn & (1 << 11) ? 13 : 15, val);
store_reg(s, rd, tmp); store_reg(s, rd, tmp);
break; break;