diff mbox

[v3] target-arm: Implement ARMv8 VSEL instruction.

Message ID 524D805F.5080401@linaro.org
State Superseded
Headers show

Commit Message

Will Newton Oct. 3, 2013, 2:34 p.m. UTC
This adds support for the VSEL floating point selection instruction
which was added in ARMv8.

Signed-off-by: Will Newton <will.newton@linaro.org>
---
 target-arm/translate.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 110 insertions(+), 5 deletions(-)

Changes in v3:
 - Move calls to disas_vfp_insn out of disas_coproc_insn
diff mbox

Patch

diff --git a/target-arm/translate.c b/target-arm/translate.c
index 998bde2..10b4fac 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -2880,6 +2880,99 @@  static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
                 rm = VFP_SREG_M(insn);
             }

+            if ((insn & 0x0f800e50) == 0x0e000a00) {
+                /* vsel */
+                uint32_t cc = (insn >> 20) & 3;
+                TCGv_i32 tmp, zero;
+
+                /* ARMv8 VFP.  */
+                if (!arm_feature(env, ARM_FEATURE_V8)) {
+                    return 1;
+                }
+
+                zero = tcg_const_tl(0);
+
+                if (dp) {
+                    TCGv_i64 ftmp1, ftmp2, ftmp3;
+
+                    ftmp1 = tcg_temp_new_i64();
+                    ftmp2 = tcg_temp_new_i64();
+                    ftmp3 = tcg_temp_new_i64();
+                    tcg_gen_ld_f64(ftmp1, cpu_env, vfp_reg_offset(dp, rn));
+                    tcg_gen_ld_f64(ftmp2, cpu_env, vfp_reg_offset(dp, rm));
+                    switch (cc) {
+                    case 0: /* eq: Z */
+                        tcg_gen_movcond_i64(TCG_COND_EQ, ftmp3, cpu_ZF, zero,
+                                            ftmp1, ftmp2);
+                        break;
+                    case 1: /* vs: V */
+                        tcg_gen_movcond_i64(TCG_COND_LT, ftmp3, cpu_VF, zero,
+                                            ftmp1, ftmp2);
+                        break;
+                    case 2: /* ge: N == V -> N ^ V == 0 */
+                        tmp = tcg_temp_new_i32();
+                        tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
+                        tcg_gen_movcond_i64(TCG_COND_GE, ftmp3, tmp, zero,
+                                            ftmp1, ftmp2);
+                        tcg_temp_free_i32(tmp);
+                        break;
+                    case 3: /* gt: !Z && N == V */
+                        tcg_gen_movcond_i64(TCG_COND_NE, ftmp3, cpu_ZF, zero,
+                                            ftmp1, ftmp2);
+                        tmp = tcg_temp_new_i32();
+                        tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
+                        tcg_gen_movcond_i64(TCG_COND_GE, ftmp3, tmp, zero,
+                                            ftmp3, ftmp2);
+                        tcg_temp_free_i32(tmp);
+                        break;
+                    }
+                    tcg_gen_st_f64(ftmp3, cpu_env, vfp_reg_offset(dp, rd));
+                    tcg_temp_free_i64(ftmp1);
+                    tcg_temp_free_i64(ftmp2);
+                    tcg_temp_free_i64(ftmp3);
+                } else {
+                    TCGv_i32 ftmp1, ftmp2, ftmp3;
+
+                    ftmp1 = tcg_temp_new_i32();
+                    ftmp2 = tcg_temp_new_i32();
+                    ftmp3 = tcg_temp_new_i32();
+                    tcg_gen_ld_f32(ftmp1, cpu_env, vfp_reg_offset(dp, rn));
+                    tcg_gen_ld_f32(ftmp2, cpu_env, vfp_reg_offset(dp, rm));
+                    switch (cc) {
+                    case 0: /* eq: Z */
+                        tcg_gen_movcond_i32(TCG_COND_EQ, ftmp3, cpu_ZF, zero,
+                                            ftmp1, ftmp2);
+                        break;
+                    case 1: /* vs: V */
+                        tcg_gen_movcond_i32(TCG_COND_LT, ftmp3, cpu_VF, zero,
+                                            ftmp1, ftmp2);
+                        break;
+                    case 2: /* ge: N == V -> N ^ V == 0 */
+                        tmp = tcg_temp_new_i32();
+                        tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
+                        tcg_gen_movcond_i32(TCG_COND_GE, ftmp3, tmp, zero,
+                                            ftmp1, ftmp2);
+                        tcg_temp_free_i32(tmp);
+                        break;
+                    case 3: /* gt: !Z && N == V */
+                        tcg_gen_movcond_i32(TCG_COND_NE, ftmp3, cpu_ZF, zero,
+                                            ftmp1, ftmp2);
+                        tmp = tcg_temp_new_i32();
+                        tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
+                        tcg_gen_movcond_i32(TCG_COND_GE, ftmp3, tmp, zero,
+                                            ftmp3, ftmp2);
+                        tcg_temp_free_i32(tmp);
+                        break;
+                    }
+                    tcg_gen_st_f32(ftmp3, cpu_env, vfp_reg_offset(dp, rd));
+                    tcg_temp_free_i32(ftmp1);
+                    tcg_temp_free_i32(ftmp2);
+                    tcg_temp_free_i32(ftmp3);
+                }
+
+                return 0;
+            }
+
             veclen = s->vec_len;
             if (op == 15 && rn > 3)
                 veclen = 0;
@@ -6299,9 +6392,6 @@  static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
 	    return disas_dsp_insn(env, s, insn);
 	}
 	return 1;
-    case 10:
-    case 11:
-	return disas_vfp_insn (env, s, insn);
     default:
         break;
     }
@@ -6756,6 +6846,11 @@  static void disas_arm_insn(CPUARMState * env, DisasContext *s)
                 goto illegal_op;
             return;
         }
+        if ((insn & 0x0f000e10) == 0x0e000a00) {
+            /* VFP.  */
+            if (disas_vfp_insn(env, s, insn))
+                goto illegal_op;
+        }
         if (((insn & 0x0f30f000) == 0x0510f000) ||
             ((insn & 0x0f30f010) == 0x0710f000)) {
             if ((insn & (1 << 22)) == 0) {
@@ -8036,9 +8131,15 @@  static void disas_arm_insn(CPUARMState * env, DisasContext *s)
         case 0xc:
         case 0xd:
         case 0xe:
-            /* Coprocessor.  */
-            if (disas_coproc_insn(env, s, insn))
+            if (((insn >> 8) & 0xe) == 10) {
+                /* VFP.  */
+                if (disas_vfp_insn(env, s, insn)) {
+                    goto illegal_op;
+                }
+            } else if (disas_coproc_insn(env, s, insn)) {
+                /* Coprocessor.  */
                 goto illegal_op;
+            }
             break;
         case 0xf:
             /* swi */
@@ -8768,6 +8869,10 @@  static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
             insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28);
             if (disas_neon_data_insn(env, s, insn))
                 goto illegal_op;
+        } else if (((insn >> 8) & 0xe) == 10) {
+            if (disas_vfp_insn(env, s, insn)) {
+                goto illegal_op;
+            }
         } else {
             if (insn & (1 << 28))
                 goto illegal_op;