]> git.proxmox.com Git - qemu.git/blobdiff - target-arm/op_helper.c
user_only: compile everything with -fpie
[qemu.git] / target-arm / op_helper.c
index d7541d6a6546d86071d838cb690549c2298c6873..9b1a0143d24e2ca5e0822d039939f80da7e30df6 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 #include "exec.h"
 #include "helpers.h"
 
+#define SIGNBIT (uint32_t)0x80000000
+#define SIGNBIT64 ((uint64_t)1 << 63)
+
 void raise_exception(int tt)
 {
     env->exception_index = tt;
@@ -28,7 +30,7 @@ void raise_exception(int tt)
 
 /* thread support */
 
-spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
 
 void cpu_lock(void)
 {
@@ -40,225 +42,31 @@ void cpu_unlock(void)
     spin_unlock(&global_cpu_lock);
 }
 
-/* VFP support.  */
-
-void do_vfp_abss(void)
-{
-    FT0s = float32_abs(FT0s);
-}
-
-void do_vfp_absd(void)
-{
-    FT0d = float64_abs(FT0d);
-}
-
-void do_vfp_sqrts(void)
-{
-    FT0s = float32_sqrt(FT0s, &env->vfp.fp_status);
-}
-
-void do_vfp_sqrtd(void)
-{
-    FT0d = float64_sqrt(FT0d, &env->vfp.fp_status);
-}
-
-/* XXX: check quiet/signaling case */
-#define DO_VFP_cmp(p, size)               \
-void do_vfp_cmp##p(void)                  \
-{                                         \
-    uint32_t flags;                       \
-    switch(float ## size ## _compare_quiet(FT0##p, FT1##p, &env->vfp.fp_status)) {\
-    case 0: flags = 0x6; break;\
-    case -1: flags = 0x8; break;\
-    case 1: flags = 0x2; break;\
-    default: case 2: flags = 0x3; break;\
-    }\
-    env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28)\
-        | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
-    FORCE_RET();                          \
-}\
-\
-void do_vfp_cmpe##p(void)                   \
-{                                           \
-    uint32_t flags;                       \
-    switch(float ## size ## _compare(FT0##p, FT1##p, &env->vfp.fp_status)) {\
-    case 0: flags = 0x6; break;\
-    case -1: flags = 0x8; break;\
-    case 1: flags = 0x2; break;\
-    default: case 2: flags = 0x3; break;\
-    }\
-    env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28)\
-        | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
-    FORCE_RET();                          \
-}
-DO_VFP_cmp(s, 32)
-DO_VFP_cmp(d, 64)
-#undef DO_VFP_cmp
-
-/* Convert host exception flags to vfp form.  */
-static inline int vfp_exceptbits_from_host(int host_bits)
-{
-    int target_bits = 0;
-
-    if (host_bits & float_flag_invalid)
-        target_bits |= 1;
-    if (host_bits & float_flag_divbyzero)
-        target_bits |= 2;
-    if (host_bits & float_flag_overflow)
-        target_bits |= 4;
-    if (host_bits & float_flag_underflow)
-        target_bits |= 8;
-    if (host_bits & float_flag_inexact)
-        target_bits |= 0x10;
-    return target_bits;
-}
-
-/* Convert vfp exception flags to target form.  */
-static inline int vfp_exceptbits_to_host(int target_bits)
-{
-    int host_bits = 0;
-
-    if (target_bits & 1)
-        host_bits |= float_flag_invalid;
-    if (target_bits & 2)
-        host_bits |= float_flag_divbyzero;
-    if (target_bits & 4)
-        host_bits |= float_flag_overflow;
-    if (target_bits & 8)
-        host_bits |= float_flag_underflow;
-    if (target_bits & 0x10)
-        host_bits |= float_flag_inexact;
-    return host_bits;
-}
-
-void do_vfp_set_fpscr(void)
-{
-    int i;
-    uint32_t changed;
-
-    changed = env->vfp.xregs[ARM_VFP_FPSCR];
-    env->vfp.xregs[ARM_VFP_FPSCR] = (T0 & 0xffc8ffff);
-    env->vfp.vec_len = (T0 >> 16) & 7;
-    env->vfp.vec_stride = (T0 >> 20) & 3;
-
-    changed ^= T0;
-    if (changed & (3 << 22)) {
-        i = (T0 >> 22) & 3;
-        switch (i) {
-        case 0:
-            i = float_round_nearest_even;
-            break;
-        case 1:
-            i = float_round_up;
-            break;
-        case 2:
-            i = float_round_down;
-            break;
-        case 3:
-            i = float_round_to_zero;
-            break;
-        }
-        set_float_rounding_mode(i, &env->vfp.fp_status);
-    }
-
-    i = vfp_exceptbits_to_host((T0 >> 8) & 0x1f);
-    set_float_exception_flags(i, &env->vfp.fp_status);
-    /* XXX: FZ and DN are not implemented.  */
-}
-
-void do_vfp_get_fpscr(void)
-{
-    int i;
-
-    T0 = (env->vfp.xregs[ARM_VFP_FPSCR] & 0xffc8ffff) | (env->vfp.vec_len << 16)
-          | (env->vfp.vec_stride << 20);
-    i = get_float_exception_flags(&env->vfp.fp_status);
-    T0 |= vfp_exceptbits_from_host(i);
-}
-
-float32 helper_recps_f32(float32 a, float32 b)
-{
-    float_status *s = &env->vfp.fp_status;
-    float32 two = int32_to_float32(2, s);
-    return float32_sub(two, float32_mul(a, b, s), s);
-}
-
-float32 helper_rsqrts_f32(float32 a, float32 b)
-{
-    float_status *s = &env->vfp.fp_status;
-    float32 three = int32_to_float32(3, s);
-    return float32_sub(three, float32_mul(a, b, s), s);
-}
-
-/* TODO: The architecture specifies the value that the estimate functions
-   should return.  We return the exact reciprocal/root instead.  */
-float32 helper_recpe_f32(float32 a)
-{
-    float_status *s = &env->vfp.fp_status;
-    float32 one = int32_to_float32(1, s);
-    return float32_div(one, a, s);
-}
-
-float32 helper_rsqrte_f32(float32 a)
-{
-    float_status *s = &env->vfp.fp_status;
-    float32 one = int32_to_float32(1, s);
-    return float32_div(one, float32_sqrt(a, s), s);
-}
-
-uint32_t helper_recpe_u32(uint32_t a)
-{
-    float_status *s = &env->vfp.fp_status;
-    float32 tmp;
-    tmp = int32_to_float32(a, s);
-    tmp = float32_scalbn(tmp, -32, s);
-    tmp = helper_recpe_f32(tmp);
-    tmp = float32_scalbn(tmp, 31, s);
-    return float32_to_int32(tmp, s);
-}
-
-uint32_t helper_rsqrte_u32(uint32_t a)
-{
-    float_status *s = &env->vfp.fp_status;
-    float32 tmp;
-    tmp = int32_to_float32(a, s);
-    tmp = float32_scalbn(tmp, -32, s);
-    tmp = helper_rsqrte_f32(tmp);
-    tmp = float32_scalbn(tmp, 31, s);
-    return float32_to_int32(tmp, s);
-}
-
-void helper_neon_tbl(int rn, int maxindex)
+uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def,
+                          uint32_t rn, uint32_t maxindex)
 {
     uint32_t val;
-    uint32_t mask;
     uint32_t tmp;
     int index;
     int shift;
     uint64_t *table;
     table = (uint64_t *)&env->vfp.regs[rn];
     val = 0;
-    mask = 0;
     for (shift = 0; shift < 32; shift += 8) {
-        index = (T1 >> shift) & 0xff;
-        if (index <= maxindex) {
-            tmp = (table[index >> 3] >> (index & 7)) & 0xff;
+        index = (ireg >> shift) & 0xff;
+        if (index < maxindex) {
+            tmp = (table[index >> 3] >> ((index & 7) << 3)) & 0xff;
             val |= tmp << shift;
         } else {
-            val |= T0 & (0xff << shift);
+            val |= def & (0xff << shift);
         }
     }
-    T0 = val;
+    return val;
 }
 
 #if !defined(CONFIG_USER_ONLY)
 
 #define MMUSUFFIX _mmu
-#ifdef __s390__
-# define GETPC() ((void*)((unsigned long)__builtin_return_address(0) & 0x7fffffffUL))
-#else
-# define GETPC() (__builtin_return_address(0))
-#endif
 
 #define SHIFT 0
 #include "softmmu_template.h"
@@ -288,7 +96,7 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
     saved_env = env;
     env = cpu_single_env;
     ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
-    if (__builtin_expect(ret, 0)) {
+    if (unlikely(ret)) {
         if (retaddr) {
             /* now we have a real cpu fault */
             pc = (unsigned long)retaddr;
@@ -305,7 +113,8 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
 }
 #endif
 
-#define SIGNBIT (uint32_t)0x80000000
+/* FIXME: Pass an axplicit pointer to QF to CPUState, and move saturating
+   instructions into helper.c  */
 uint32_t HELPER(add_setq)(uint32_t a, uint32_t b)
 {
     uint32_t res = a + b;
@@ -375,7 +184,6 @@ static inline uint32_t do_ssat(int32_t val, int shift)
     int32_t top;
     uint32_t mask;
 
-    shift = PARAM1;
     top = val >> shift;
     mask = (1u << shift) - 1;
     if (top > 0) {
@@ -393,7 +201,6 @@ static inline uint32_t do_usat(int32_t val, int shift)
 {
     uint32_t max;
 
-    shift = PARAM1;
     max = (1u << shift) - 1;
     if (val < 0) {
         env->QF = 1;
@@ -459,3 +266,224 @@ void HELPER(cpsr_write)(uint32_t val, uint32_t mask)
 {
     cpsr_write(env, val, mask);
 }
+
+/* Access to user mode registers from privileged modes.  */
+uint32_t HELPER(get_user_reg)(uint32_t regno)
+{
+    uint32_t val;
+
+    if (regno == 13) {
+        val = env->banked_r13[0];
+    } else if (regno == 14) {
+        val = env->banked_r14[0];
+    } else if (regno >= 8
+               && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
+        val = env->usr_regs[regno - 8];
+    } else {
+        val = env->regs[regno];
+    }
+    return val;
+}
+
+void HELPER(set_user_reg)(uint32_t regno, uint32_t val)
+{
+    if (regno == 13) {
+        env->banked_r13[0] = val;
+    } else if (regno == 14) {
+        env->banked_r14[0] = val;
+    } else if (regno >= 8
+               && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
+        env->usr_regs[regno - 8] = val;
+    } else {
+        env->regs[regno] = val;
+    }
+}
+
+/* ??? Flag setting arithmetic is awkward because we need to do comparisons.
+   The only way to do that in TCG is a conditional branch, which clobbers
+   all our temporaries.  For now implement these as helper functions.  */
+
+uint32_t HELPER (add_cc)(uint32_t a, uint32_t b)
+{
+    uint32_t result;
+    result = a + b;
+    env->NF = env->ZF = result;
+    env->CF = result < a;
+    env->VF = (a ^ b ^ -1) & (a ^ result);
+    return result;
+}
+
+uint32_t HELPER(adc_cc)(uint32_t a, uint32_t b)
+{
+    uint32_t result;
+    if (!env->CF) {
+        result = a + b;
+        env->CF = result < a;
+    } else {
+        result = a + b + 1;
+        env->CF = result <= a;
+    }
+    env->VF = (a ^ b ^ -1) & (a ^ result);
+    env->NF = env->ZF = result;
+    return result;
+}
+
+uint32_t HELPER(sub_cc)(uint32_t a, uint32_t b)
+{
+    uint32_t result;
+    result = a - b;
+    env->NF = env->ZF = result;
+    env->CF = a >= b;
+    env->VF = (a ^ b) & (a ^ result);
+    return result;
+}
+
+uint32_t HELPER(sbc_cc)(uint32_t a, uint32_t b)
+{
+    uint32_t result;
+    if (!env->CF) {
+        result = a - b - 1;
+        env->CF = a > b;
+    } else {
+        result = a - b;
+        env->CF = a >= b;
+    }
+    env->VF = (a ^ b) & (a ^ result);
+    env->NF = env->ZF = result;
+    return result;
+}
+
+/* Similarly for variable shift instructions.  */
+
+uint32_t HELPER(shl)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32)
+        return 0;
+    return x << shift;
+}
+
+uint32_t HELPER(shr)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32)
+        return 0;
+    return (uint32_t)x >> shift;
+}
+
+uint32_t HELPER(sar)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32)
+        shift = 31;
+    return (int32_t)x >> shift;
+}
+
+uint32_t HELPER(shl_cc)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32) {
+        if (shift == 32)
+            env->CF = x & 1;
+        else
+            env->CF = 0;
+        return 0;
+    } else if (shift != 0) {
+        env->CF = (x >> (32 - shift)) & 1;
+        return x << shift;
+    }
+    return x;
+}
+
+uint32_t HELPER(shr_cc)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32) {
+        if (shift == 32)
+            env->CF = (x >> 31) & 1;
+        else
+            env->CF = 0;
+        return 0;
+    } else if (shift != 0) {
+        env->CF = (x >> (shift - 1)) & 1;
+        return x >> shift;
+    }
+    return x;
+}
+
+uint32_t HELPER(sar_cc)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32) {
+        env->CF = (x >> 31) & 1;
+        return (int32_t)x >> 31;
+    } else if (shift != 0) {
+        env->CF = (x >> (shift - 1)) & 1;
+        return (int32_t)x >> shift;
+    }
+    return x;
+}
+
+uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i)
+{
+    int shift1, shift;
+    shift1 = i & 0xff;
+    shift = shift1 & 0x1f;
+    if (shift == 0) {
+        if (shift1 != 0)
+            env->CF = (x >> 31) & 1;
+        return x;
+    } else {
+        env->CF = (x >> (shift - 1)) & 1;
+        return ((uint32_t)x >> shift) | (x << (32 - shift));
+    }
+}
+
+uint64_t HELPER(neon_add_saturate_s64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    res = src1 + src2;
+    if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) {
+        env->QF = 1;
+        res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
+    }
+    return res;
+}
+
+uint64_t HELPER(neon_add_saturate_u64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    res = src1 + src2;
+    if (res < src1) {
+        env->QF = 1;
+        res = ~(uint64_t)0;
+    }
+    return res;
+}
+
+uint64_t HELPER(neon_sub_saturate_s64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    res = src1 - src2;
+    if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) {
+        env->QF = 1;
+        res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
+    }
+    return res;
+}
+
+uint64_t HELPER(neon_sub_saturate_u64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    if (src1 < src2) {
+        env->QF = 1;
+        res = 0;
+    } else {
+        res = src1 - src2;
+    }
+    return res;
+}