aboutsummaryrefslogtreecommitdiff
path: root/target-ppc
diff options
context:
space:
mode:
authorRichard Henderson <rth@twiddle.net>2013-03-21 10:01:45 +0000
committerAlexander Graf <agraf@suse.de>2013-03-22 15:28:54 +0100
commit79482e5ab38a05ca8869040b0d8b8f451f16ff62 (patch)
treeb9be9189d4cf7d4a8376a037df3d0ae576768403 /target-ppc
parentb632a148b677b773ff155f9de840b37a653567b9 (diff)
target-ppc: Fix add and subf carry generation in narrow mode
The set of computations used in b5a73f8d8a57e940f9bbeb399a9e47897522ee9a are only valid if the current word size == target_long size. This failed to take ppc64 in 32-bit (narrow) mode into account. Add a NARROW_MODE macro to avoid conditional compilation. Signed-off-by: Richard Henderson <rth@twiddle.net> Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'target-ppc')
-rw-r--r--target-ppc/translate.c64
1 files changed, 48 insertions, 16 deletions
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index bb690f817a..47efcd7091 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -204,6 +204,13 @@ typedef struct DisasContext {
int singlestep_enabled;
} DisasContext;
+/* True when active word size < size of target_long. */
+#ifdef TARGET_PPC64
+# define NARROW_MODE(C) (!(C)->sf_mode)
+#else
+# define NARROW_MODE(C) 0
+#endif
+
struct opc_handler_t {
/* invalid bits for instruction 1 (Rc(opcode) == 0) */
uint32_t inval1;
@@ -778,14 +785,26 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1,
}
if (compute_ca) {
- TCGv zero = tcg_const_tl(0);
- if (add_ca) {
- tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero);
- tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero);
+ if (NARROW_MODE(ctx)) {
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_ext32u_tl(t1, arg2);
+ tcg_gen_ext32u_tl(t0, arg1);
+ tcg_gen_add_tl(t0, t0, t1);
+ tcg_temp_free(t1);
+ if (add_ca) {
+ tcg_gen_add_tl(t0, t0, cpu_ca);
+ }
+ tcg_gen_shri_tl(cpu_ca, t0, 32);
} else {
- tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero);
+ TCGv zero = tcg_const_tl(0);
+ if (add_ca) {
+ tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero);
+ tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero);
+ } else {
+ tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero);
+ }
+ tcg_temp_free(zero);
}
- tcg_temp_free(zero);
} else {
tcg_gen_add_tl(t0, arg1, arg2);
if (add_ca) {
@@ -1114,14 +1133,25 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1,
{
TCGv t0 = ret;
- if (((add_ca && compute_ca) || compute_ov)
- && (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2))) {
+ if (compute_ov && (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2))) {
t0 = tcg_temp_new();
}
- if (add_ca) {
- /* dest = ~arg1 + arg2 + ca. */
- if (compute_ca) {
+ if (compute_ca) {
+ /* dest = ~arg1 + arg2 [+ ca]. */
+ if (NARROW_MODE(ctx)) {
+ TCGv inv1 = tcg_temp_new();
+ tcg_gen_not_tl(inv1, arg1);
+ tcg_gen_ext32u_tl(t0, arg2);
+ tcg_gen_ext32u_tl(inv1, inv1);
+ if (add_ca) {
+ tcg_gen_add_tl(t0, t0, cpu_ca);
+ } else {
+ tcg_gen_addi_tl(t0, t0, 1);
+ }
+ tcg_gen_add_tl(t0, t0, inv1);
+ tcg_gen_shri_tl(cpu_ca, t0, 32);
+ } else if (add_ca) {
TCGv zero, inv1 = tcg_temp_new();
tcg_gen_not_tl(inv1, arg1);
zero = tcg_const_tl(0);
@@ -1130,14 +1160,16 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1,
tcg_temp_free(zero);
tcg_temp_free(inv1);
} else {
+ tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1);
tcg_gen_sub_tl(t0, arg2, arg1);
- tcg_gen_add_tl(t0, t0, cpu_ca);
- tcg_gen_subi_tl(t0, t0, 1);
}
+ } else if (add_ca) {
+ /* Since we're ignoring carry-out, we can simplify the
+ standard ~arg1 + arg2 + ca to arg2 - arg1 + ca - 1. */
+ tcg_gen_sub_tl(t0, arg2, arg1);
+ tcg_gen_add_tl(t0, t0, cpu_ca);
+ tcg_gen_subi_tl(t0, t0, 1);
} else {
- if (compute_ca) {
- tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1);
- }
tcg_gen_sub_tl(t0, arg2, arg1);
}