aboutsummaryrefslogtreecommitdiff
path: root/target-arm/translate.c
diff options
context:
space:
mode:
authorPaul Brook <paul@codesourcery.com>2009-11-22 21:35:13 +0000
committerPaul Brook <paul@codesourcery.com>2009-11-22 21:35:13 +0000
commit426f5abcaa15d0e59475fdb33b2a4a5fdf492d05 (patch)
treeef9eb63ac19b7ba19fe533d514d3571f21de2b3d /target-arm/translate.c
parenteee4850422d1d18276c1f4dbb6153b568efcb8b9 (diff)
ARM atomic ops rewrite
Implement ARMv6 atomic ops (ldrex/strex) using the same trick as PPC. Signed-off-by: Paul Brook <paul@codesourcery.com>
Diffstat (limited to 'target-arm/translate.c')
-rw-r--r--target-arm/translate.c250
1 files changed, 165 insertions, 85 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c
index a002f7e029..45bf77256f 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -76,6 +76,13 @@ static TCGv_ptr cpu_env;
/* We reuse the same 64-bit temporaries for efficiency. */
static TCGv_i64 cpu_V0, cpu_V1, cpu_M0;
static TCGv_i32 cpu_R[16];
+static TCGv_i32 cpu_exclusive_addr;
+static TCGv_i32 cpu_exclusive_val;
+static TCGv_i32 cpu_exclusive_high;
+#ifdef CONFIG_USER_ONLY
+static TCGv_i32 cpu_exclusive_test;
+static TCGv_i32 cpu_exclusive_info;
+#endif
/* FIXME: These should be removed. */
static TCGv cpu_F0s, cpu_F1s;
@@ -99,6 +106,18 @@ void arm_translate_init(void)
offsetof(CPUState, regs[i]),
regnames[i]);
}
+ cpu_exclusive_addr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_addr), "exclusive_addr");
+ cpu_exclusive_val = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_val), "exclusive_val");
+ cpu_exclusive_high = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_high), "exclusive_high");
+#ifdef CONFIG_USER_ONLY
+ cpu_exclusive_test = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_test), "exclusive_test");
+ cpu_exclusive_info = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_info), "exclusive_info");
+#endif
#define GEN_HELPER 2
#include "helpers.h"
@@ -5819,6 +5838,132 @@ static void gen_logicq_cc(TCGv_i64 val)
dead_tmp(tmp);
}
+/* Load/Store exclusive instructions are implemented by remembering
+ the value/address loaded, and seeing if these are the same
+ when the store is performed. This should be is sufficient to implement
+ the architecturally mandated semantics, and avoids having to monitor
+ regular stores.
+
+ In system emulation mode only one CPU will be running at once, so
+ this sequence is effectively atomic. In user emulation mode we
+ throw an exception and handle the atomic operation elsewhere. */
+static void gen_load_exclusive(DisasContext *s, int rt, int rt2,
+ TCGv addr, int size)
+{
+ TCGv tmp;
+
+ switch (size) {
+ case 0:
+ tmp = gen_ld8u(addr, IS_USER(s));
+ break;
+ case 1:
+ tmp = gen_ld16u(addr, IS_USER(s));
+ break;
+ case 2:
+ case 3:
+ tmp = gen_ld32(addr, IS_USER(s));
+ break;
+ default:
+ abort();
+ }
+ tcg_gen_mov_i32(cpu_exclusive_val, tmp);
+ store_reg(s, rt, tmp);
+ if (size == 3) {
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp = gen_ld32(addr, IS_USER(s));
+ tcg_gen_mov_i32(cpu_exclusive_high, tmp);
+ store_reg(s, rt2, tmp);
+ }
+ tcg_gen_mov_i32(cpu_exclusive_addr, addr);
+}
+
+static void gen_clrex(DisasContext *s)
+{
+ tcg_gen_movi_i32(cpu_exclusive_addr, -1);
+}
+
+#ifdef CONFIG_USER_ONLY
+static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
+ TCGv addr, int size)
+{
+ tcg_gen_mov_i32(cpu_exclusive_test, addr);
+ tcg_gen_movi_i32(cpu_exclusive_info,
+ size | (rd << 4) | (rt << 8) | (rt2 << 12));
+ gen_set_condexec(s);
+ gen_set_pc_im(s->pc - 4);
+ gen_exception(EXCP_STREX);
+ s->is_jmp = DISAS_JUMP;
+}
+#else
+static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
+ TCGv addr, int size)
+{
+ TCGv tmp;
+ int done_label;
+ int fail_label;
+
+ /* if (env->exclusive_addr == addr && env->exclusive_val == [addr]) {
+ [addr] = {Rt};
+ {Rd} = 0;
+ } else {
+ {Rd} = 1;
+ } */
+ fail_label = gen_new_label();
+ done_label = gen_new_label();
+ tcg_gen_brcond_i32(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label);
+ switch (size) {
+ case 0:
+ tmp = gen_ld8u(addr, IS_USER(s));
+ break;
+ case 1:
+ tmp = gen_ld16u(addr, IS_USER(s));
+ break;
+ case 2:
+ case 3:
+ tmp = gen_ld32(addr, IS_USER(s));
+ break;
+ default:
+ abort();
+ }
+ tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_val, fail_label);
+ dead_tmp(tmp);
+ if (size == 3) {
+ TCGv tmp2 = new_tmp();
+ tcg_gen_addi_i32(tmp2, addr, 4);
+ tmp = gen_ld32(addr, IS_USER(s));
+ dead_tmp(tmp2);
+ tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_high, fail_label);
+ dead_tmp(tmp);
+ }
+ tmp = load_reg(s, rt);
+ switch (size) {
+ case 0:
+ gen_st8(tmp, addr, IS_USER(s));
+ break;
+ case 1:
+ gen_st16(tmp, addr, IS_USER(s));
+ break;
+ case 2:
+ case 3:
+ gen_st32(tmp, addr, IS_USER(s));
+ break;
+ default:
+ abort();
+ }
+ if (size == 3) {
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp = load_reg(s, rt2);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ tcg_gen_movi_i32(cpu_R[rd], 0);
+ tcg_gen_br(done_label);
+ gen_set_label(fail_label);
+ tcg_gen_movi_i32(cpu_R[rd], 1);
+ gen_set_label(done_label);
+ tcg_gen_movi_i32(cpu_exclusive_addr, -1);
+}
+#endif
+
static void disas_arm_insn(CPUState * env, DisasContext *s)
{
unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh;
@@ -5869,7 +6014,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
switch ((insn >> 4) & 0xf) {
case 1: /* clrex */
ARCH(6K);
- gen_helper_clrex(cpu_env);
+ gen_clrex(s);
return;
case 4: /* dsb */
case 5: /* dmb */
@@ -6454,57 +6599,40 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
addr = tcg_temp_local_new_i32();
load_reg_var(s, addr, rn);
if (insn & (1 << 20)) {
- gen_helper_mark_exclusive(cpu_env, addr);
switch (op1) {
case 0: /* ldrex */
- tmp = gen_ld32(addr, IS_USER(s));
+ gen_load_exclusive(s, rd, 15, addr, 2);
break;
case 1: /* ldrexd */
- tmp = gen_ld32(addr, IS_USER(s));
- store_reg(s, rd, tmp);
- tcg_gen_addi_i32(addr, addr, 4);
- tmp = gen_ld32(addr, IS_USER(s));
- rd++;
+ gen_load_exclusive(s, rd, rd + 1, addr, 3);
break;
case 2: /* ldrexb */
- tmp = gen_ld8u(addr, IS_USER(s));
+ gen_load_exclusive(s, rd, 15, addr, 0);
break;
case 3: /* ldrexh */
- tmp = gen_ld16u(addr, IS_USER(s));
+ gen_load_exclusive(s, rd, 15, addr, 1);
break;
default:
abort();
}
- store_reg(s, rd, tmp);
} else {
- int label = gen_new_label();
rm = insn & 0xf;
- tmp2 = tcg_temp_local_new_i32();
- gen_helper_test_exclusive(tmp2, cpu_env, addr);
- tcg_gen_brcondi_i32(TCG_COND_NE, tmp2, 0, label);
- tmp = load_reg(s,rm);
switch (op1) {
case 0: /* strex */
- gen_st32(tmp, addr, IS_USER(s));
+ gen_store_exclusive(s, rd, rm, 15, addr, 2);
break;
case 1: /* strexd */
- gen_st32(tmp, addr, IS_USER(s));
- tcg_gen_addi_i32(addr, addr, 4);
- tmp = load_reg(s, rm + 1);
- gen_st32(tmp, addr, IS_USER(s));
+ gen_store_exclusive(s, rd, rm, rm + 1, addr, 2);
break;
case 2: /* strexb */
- gen_st8(tmp, addr, IS_USER(s));
+ gen_store_exclusive(s, rd, rm, 15, addr, 0);
break;
case 3: /* strexh */
- gen_st16(tmp, addr, IS_USER(s));
+ gen_store_exclusive(s, rd, rm, 15, addr, 1);
break;
default:
abort();
}
- gen_set_label(label);
- tcg_gen_mov_i32(cpu_R[rd], tmp2);
- tcg_temp_free(tmp2);
}
tcg_temp_free(addr);
} else {
@@ -7259,20 +7387,11 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
/* Load/store exclusive word. */
addr = tcg_temp_local_new();
load_reg_var(s, addr, rn);
+ tcg_gen_addi_i32(addr, addr, (insn & 0xff) << 2);
if (insn & (1 << 20)) {
- gen_helper_mark_exclusive(cpu_env, addr);
- tmp = gen_ld32(addr, IS_USER(s));
- store_reg(s, rd, tmp);
+ gen_load_exclusive(s, rs, 15, addr, 2);
} else {
- int label = gen_new_label();
- tmp2 = tcg_temp_local_new();
- gen_helper_test_exclusive(tmp2, cpu_env, addr);
- tcg_gen_brcondi_i32(TCG_COND_NE, tmp2, 0, label);
- tmp = load_reg(s, rs);
- gen_st32(tmp, addr, IS_USER(s));
- gen_set_label(label);
- tcg_gen_mov_i32(cpu_R[rd], tmp2);
- tcg_temp_free(tmp2);
+ gen_store_exclusive(s, rd, rs, 15, addr, 2);
}
tcg_temp_free(addr);
} else if ((insn & (1 << 6)) == 0) {
@@ -7300,56 +7419,17 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
store_reg(s, 15, tmp);
} else {
/* Load/store exclusive byte/halfword/doubleword. */
- /* ??? These are not really atomic. However we know
- we never have multiple CPUs running in parallel,
- so it is good enough. */
+ ARCH(7);
op = (insn >> 4) & 0x3;
+ if (op == 2) {
+ goto illegal_op;
+ }
addr = tcg_temp_local_new();
load_reg_var(s, addr, rn);
if (insn & (1 << 20)) {
- gen_helper_mark_exclusive(cpu_env, addr);
- switch (op) {
- case 0:
- tmp = gen_ld8u(addr, IS_USER(s));
- break;
- case 1:
- tmp = gen_ld16u(addr, IS_USER(s));
- break;
- case 3:
- tmp = gen_ld32(addr, IS_USER(s));
- tcg_gen_addi_i32(addr, addr, 4);
- tmp2 = gen_ld32(addr, IS_USER(s));
- store_reg(s, rd, tmp2);
- break;
- default:
- goto illegal_op;
- }
- store_reg(s, rs, tmp);
+ gen_load_exclusive(s, rs, rd, addr, op);
} else {
- int label = gen_new_label();
- tmp2 = tcg_temp_local_new();
- gen_helper_test_exclusive(tmp2, cpu_env, addr);
- tcg_gen_brcondi_i32(TCG_COND_NE, tmp2, 0, label);
- tmp = load_reg(s, rs);
- switch (op) {
- case 0:
- gen_st8(tmp, addr, IS_USER(s));
- break;
- case 1:
- gen_st16(tmp, addr, IS_USER(s));
- break;
- case 3:
- gen_st32(tmp, addr, IS_USER(s));
- tcg_gen_addi_i32(addr, addr, 4);
- tmp = load_reg(s, rd);
- gen_st32(tmp, addr, IS_USER(s));
- break;
- default:
- goto illegal_op;
- }
- gen_set_label(label);
- tcg_gen_mov_i32(cpu_R[rm], tmp2);
- tcg_temp_free(tmp2);
+ gen_store_exclusive(s, rm, rs, rd, addr, op);
}
tcg_temp_free(addr);
}
@@ -7845,16 +7925,16 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
}
break;
case 3: /* Special control operations. */
+ ARCH(7);
op = (insn >> 4) & 0xf;
switch (op) {
case 2: /* clrex */
- gen_helper_clrex(cpu_env);
+ gen_clrex(s);
break;
case 4: /* dsb */
case 5: /* dmb */
case 6: /* isb */
/* These execute as NOPs. */
- ARCH(7);
break;
default:
goto illegal_op;