| /* |
| * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/log.h" |
| #include "exec/exec-all.h" |
| #include "exec/cpu_ldst.h" |
| #include "exec/helper-proto.h" |
| #include "fpu/softfloat.h" |
| #include "cpu.h" |
| #include "internal.h" |
| #include "macros.h" |
| #include "arch.h" |
| #include "hex_arch_types.h" |
| #include "fma_emu.h" |
| #include "mmvec/mmvec.h" |
| #include "mmvec/macros.h" |
| #include "op_helper.h" |
| #include "translate.h" |
| |
| #define SF_BIAS 127 |
| #define SF_MANTBITS 23 |
| |
| /* Exceptions processing helpers */ |
| G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, |
| uint32_t exception, |
| uintptr_t pc) |
| { |
| CPUState *cs = env_cpu(env); |
| qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); |
| cs->exception_index = exception; |
| cpu_loop_exit_restore(cs, pc); |
| } |
| |
| G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) |
| { |
| hexagon_raise_exception_err(env, excp, 0); |
| } |
| |
| void log_store32(CPUHexagonState *env, target_ulong addr, |
| target_ulong val, int width, int slot) |
| { |
| HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx |
| ", %" PRId32 " [0x08%" PRIx32 "])\n", |
| width, addr, val, val); |
| env->mem_log_stores[slot].va = addr; |
| env->mem_log_stores[slot].width = width; |
| env->mem_log_stores[slot].data32 = val; |
| } |
| |
| void log_store64(CPUHexagonState *env, target_ulong addr, |
| int64_t val, int width, int slot) |
| { |
| HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx |
| ", %" PRId64 " [0x016%" PRIx64 "])\n", |
| width, addr, val, val); |
| env->mem_log_stores[slot].va = addr; |
| env->mem_log_stores[slot].width = width; |
| env->mem_log_stores[slot].data64 = val; |
| } |
| |
| /* Handy place to set a breakpoint */ |
| void HELPER(debug_start_packet)(CPUHexagonState *env) |
| { |
| HEX_DEBUG_LOG("Start packet: pc = 0x" TARGET_FMT_lx "\n", |
| env->gpr[HEX_REG_PC]); |
| |
| for (int i = 0; i < TOTAL_PER_THREAD_REGS; i++) { |
| env->reg_written[i] = 0; |
| } |
| } |
| |
| /* Checks for bookkeeping errors between disassembly context and runtime */ |
| void HELPER(debug_check_store_width)(CPUHexagonState *env, int slot, int check) |
| { |
| if (env->mem_log_stores[slot].width != check) { |
| HEX_DEBUG_LOG("ERROR: %d != %d\n", |
| env->mem_log_stores[slot].width, check); |
| g_assert_not_reached(); |
| } |
| } |
| |
| static void commit_store(CPUHexagonState *env, int slot_num, uintptr_t ra) |
| { |
| uint8_t width = env->mem_log_stores[slot_num].width; |
| target_ulong va = env->mem_log_stores[slot_num].va; |
| |
| switch (width) { |
| case 1: |
| cpu_stb_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra); |
| break; |
| case 2: |
| cpu_stw_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra); |
| break; |
| case 4: |
| cpu_stl_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra); |
| break; |
| case 8: |
| cpu_stq_data_ra(env, va, env->mem_log_stores[slot_num].data64, ra); |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| } |
| |
| void HELPER(commit_store)(CPUHexagonState *env, int slot_num) |
| { |
| uintptr_t ra = GETPC(); |
| commit_store(env, slot_num, ra); |
| } |
| |
| void HELPER(gather_store)(CPUHexagonState *env, uint32_t addr, int slot) |
| { |
| mem_gather_store(env, addr, slot); |
| } |
| |
| void HELPER(commit_hvx_stores)(CPUHexagonState *env) |
| { |
| uintptr_t ra = GETPC(); |
| |
| /* Normal (possibly masked) vector store */ |
| for (int i = 0; i < VSTORES_MAX; i++) { |
| if (env->vstore_pending[i]) { |
| env->vstore_pending[i] = 0; |
| target_ulong va = env->vstore[i].va; |
| int size = env->vstore[i].size; |
| for (int j = 0; j < size; j++) { |
| if (test_bit(j, env->vstore[i].mask)) { |
| cpu_stb_data_ra(env, va + j, env->vstore[i].data.ub[j], ra); |
| } |
| } |
| } |
| } |
| |
| /* Scatter store */ |
| if (env->vtcm_pending) { |
| env->vtcm_pending = false; |
| if (env->vtcm_log.op) { |
| /* Need to perform the scatter read/modify/write at commit time */ |
| if (env->vtcm_log.op_size == 2) { |
| SCATTER_OP_WRITE_TO_MEM(uint16_t); |
| } else if (env->vtcm_log.op_size == 4) { |
| /* Word Scatter += */ |
| SCATTER_OP_WRITE_TO_MEM(uint32_t); |
| } else { |
| g_assert_not_reached(); |
| } |
| } else { |
| for (int i = 0; i < sizeof(MMVector); i++) { |
| if (test_bit(i, env->vtcm_log.mask)) { |
| cpu_stb_data_ra(env, env->vtcm_log.va[i], |
| env->vtcm_log.data.ub[i], ra); |
| clear_bit(i, env->vtcm_log.mask); |
| env->vtcm_log.data.ub[i] = 0; |
| } |
| |
| } |
| } |
| } |
| } |
| |
| static void print_store(CPUHexagonState *env, int slot) |
| { |
| if (!(env->slot_cancelled & (1 << slot))) { |
| uint8_t width = env->mem_log_stores[slot].width; |
| if (width == 1) { |
| uint32_t data = env->mem_log_stores[slot].data32 & 0xff; |
| HEX_DEBUG_LOG("\tmemb[0x" TARGET_FMT_lx "] = %" PRId32 |
| " (0x%02" PRIx32 ")\n", |
| env->mem_log_stores[slot].va, data, data); |
| } else if (width == 2) { |
| uint32_t data = env->mem_log_stores[slot].data32 & 0xffff; |
| HEX_DEBUG_LOG("\tmemh[0x" TARGET_FMT_lx "] = %" PRId32 |
| " (0x%04" PRIx32 ")\n", |
| env->mem_log_stores[slot].va, data, data); |
| } else if (width == 4) { |
| uint32_t data = env->mem_log_stores[slot].data32; |
| HEX_DEBUG_LOG("\tmemw[0x" TARGET_FMT_lx "] = %" PRId32 |
| " (0x%08" PRIx32 ")\n", |
| env->mem_log_stores[slot].va, data, data); |
| } else if (width == 8) { |
| HEX_DEBUG_LOG("\tmemd[0x" TARGET_FMT_lx "] = %" PRId64 |
| " (0x%016" PRIx64 ")\n", |
| env->mem_log_stores[slot].va, |
| env->mem_log_stores[slot].data64, |
| env->mem_log_stores[slot].data64); |
| } else { |
| HEX_DEBUG_LOG("\tBad store width %d\n", width); |
| g_assert_not_reached(); |
| } |
| } |
| } |
| |
| /* This function is a handy place to set a breakpoint */ |
| void HELPER(debug_commit_end)(CPUHexagonState *env, uint32_t this_PC, |
| int pred_written, int has_st0, int has_st1) |
| { |
| bool reg_printed = false; |
| bool pred_printed = false; |
| int i; |
| |
| HEX_DEBUG_LOG("Packet committed: pc = 0x" TARGET_FMT_lx "\n", this_PC); |
| HEX_DEBUG_LOG("slot_cancelled = %d\n", env->slot_cancelled); |
| |
| for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) { |
| if (env->reg_written[i]) { |
| if (!reg_printed) { |
| HEX_DEBUG_LOG("Regs written\n"); |
| reg_printed = true; |
| } |
| HEX_DEBUG_LOG("\tr%d = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")\n", |
| i, env->gpr[i], env->gpr[i]); |
| } |
| } |
| |
| for (i = 0; i < NUM_PREGS; i++) { |
| if (pred_written & (1 << i)) { |
| if (!pred_printed) { |
| HEX_DEBUG_LOG("Predicates written\n"); |
| pred_printed = true; |
| } |
| HEX_DEBUG_LOG("\tp%d = 0x" TARGET_FMT_lx "\n", |
| i, env->pred[i]); |
| } |
| } |
| |
| if (has_st0 || has_st1) { |
| HEX_DEBUG_LOG("Stores\n"); |
| if (has_st0) { |
| print_store(env, 0); |
| } |
| if (has_st1) { |
| print_store(env, 1); |
| } |
| } |
| |
| HEX_DEBUG_LOG("Next PC = " TARGET_FMT_lx "\n", env->gpr[HEX_REG_PC]); |
| HEX_DEBUG_LOG("Exec counters: pkt = " TARGET_FMT_lx |
| ", insn = " TARGET_FMT_lx |
| ", hvx = " TARGET_FMT_lx "\n", |
| env->gpr[HEX_REG_QEMU_PKT_CNT], |
| env->gpr[HEX_REG_QEMU_INSN_CNT], |
| env->gpr[HEX_REG_QEMU_HVX_CNT]); |
| |
| } |
| |
| int32_t HELPER(fcircadd)(int32_t RxV, int32_t offset, int32_t M, int32_t CS) |
| { |
| uint32_t K_const = extract32(M, 24, 4); |
| uint32_t length = extract32(M, 0, 17); |
| uint32_t new_ptr = RxV + offset; |
| uint32_t start_addr; |
| uint32_t end_addr; |
| |
| if (K_const == 0 && length >= 4) { |
| start_addr = CS; |
| end_addr = start_addr + length; |
| } else { |
| /* |
| * Versions v3 and earlier used the K value to specify a power-of-2 size |
| * 2^(K+2) that is greater than the buffer length |
| */ |
| int32_t mask = (1 << (K_const + 2)) - 1; |
| start_addr = RxV & (~mask); |
| end_addr = start_addr | length; |
| } |
| |
| if (new_ptr >= end_addr) { |
| new_ptr -= length; |
| } else if (new_ptr < start_addr) { |
| new_ptr += length; |
| } |
| |
| return new_ptr; |
| } |
| |
| uint32_t HELPER(fbrev)(uint32_t addr) |
| { |
| /* |
| * Bit reverse the low 16 bits of the address |
| */ |
| return deposit32(addr, 0, 16, revbit16(addr)); |
| } |
| |
| static float32 build_float32(uint8_t sign, uint32_t exp, uint32_t mant) |
| { |
| return make_float32( |
| ((sign & 1) << 31) | |
| ((exp & 0xff) << SF_MANTBITS) | |
| (mant & ((1 << SF_MANTBITS) - 1))); |
| } |
| |
| /* |
| * sfrecipa, sfinvsqrta have two 32-bit results |
| * r0,p0=sfrecipa(r1,r2) |
| * r0,p0=sfinvsqrta(r1) |
| * |
| * Since helpers can only return a single value, we pack the two results |
| * into a 64-bit value. |
| */ |
| uint64_t HELPER(sfrecipa)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| int32_t PeV = 0; |
| float32 RdV; |
| int idx; |
| int adjust; |
| int mant; |
| int exp; |
| |
| arch_fpop_start(env); |
| if (arch_sf_recip_common(&RsV, &RtV, &RdV, &adjust, &env->fp_status)) { |
| PeV = adjust; |
| idx = (RtV >> 16) & 0x7f; |
| mant = (recip_lookup_table[idx] << 15) | 1; |
| exp = SF_BIAS - (float32_getexp(RtV) - SF_BIAS) - 1; |
| RdV = build_float32(extract32(RtV, 31, 1), exp, mant); |
| } |
| arch_fpop_end(env); |
| return ((uint64_t)RdV << 32) | PeV; |
| } |
| |
| uint64_t HELPER(sfinvsqrta)(CPUHexagonState *env, float32 RsV) |
| { |
| int PeV = 0; |
| float32 RdV; |
| int idx; |
| int adjust; |
| int mant; |
| int exp; |
| |
| arch_fpop_start(env); |
| if (arch_sf_invsqrt_common(&RsV, &RdV, &adjust, &env->fp_status)) { |
| PeV = adjust; |
| idx = (RsV >> 17) & 0x7f; |
| mant = (invsqrt_lookup_table[idx] << 15); |
| exp = SF_BIAS - ((float32_getexp(RsV) - SF_BIAS) >> 1) - 1; |
| RdV = build_float32(extract32(RsV, 31, 1), exp, mant); |
| } |
| arch_fpop_end(env); |
| return ((uint64_t)RdV << 32) | PeV; |
| } |
| |
| int64_t HELPER(vacsh_val)(CPUHexagonState *env, |
| int64_t RxxV, int64_t RssV, int64_t RttV, |
| uint32_t pkt_need_commit) |
| { |
| for (int i = 0; i < 4; i++) { |
| int xv = sextract64(RxxV, i * 16, 16); |
| int sv = sextract64(RssV, i * 16, 16); |
| int tv = sextract64(RttV, i * 16, 16); |
| int max; |
| xv = xv + tv; |
| sv = sv - tv; |
| max = xv > sv ? xv : sv; |
| /* Note that fSATH can set the OVF bit in usr */ |
| RxxV = deposit64(RxxV, i * 16, 16, fSATH(max)); |
| } |
| return RxxV; |
| } |
| |
| int32_t HELPER(vacsh_pred)(CPUHexagonState *env, |
| int64_t RxxV, int64_t RssV, int64_t RttV) |
| { |
| int32_t PeV = 0; |
| for (int i = 0; i < 4; i++) { |
| int xv = sextract64(RxxV, i * 16, 16); |
| int sv = sextract64(RssV, i * 16, 16); |
| int tv = sextract64(RttV, i * 16, 16); |
| xv = xv + tv; |
| sv = sv - tv; |
| PeV = deposit32(PeV, i * 2, 1, (xv > sv)); |
| PeV = deposit32(PeV, i * 2 + 1, 1, (xv > sv)); |
| } |
| return PeV; |
| } |
| |
| int64_t HELPER(cabacdecbin_val)(int64_t RssV, int64_t RttV) |
| { |
| int64_t RddV = 0; |
| size4u_t state; |
| size4u_t valMPS; |
| size4u_t bitpos; |
| size4u_t range; |
| size4u_t offset; |
| size4u_t rLPS; |
| size4u_t rMPS; |
| |
| state = fEXTRACTU_RANGE(fGETWORD(1, RttV), 5, 0); |
| valMPS = fEXTRACTU_RANGE(fGETWORD(1, RttV), 8, 8); |
| bitpos = fEXTRACTU_RANGE(fGETWORD(0, RttV), 4, 0); |
| range = fGETWORD(0, RssV); |
| offset = fGETWORD(1, RssV); |
| |
| /* calculate rLPS */ |
| range <<= bitpos; |
| offset <<= bitpos; |
| rLPS = rLPS_table_64x4[state][(range >> 29) & 3]; |
| rLPS = rLPS << 23; /* left aligned */ |
| |
| /* calculate rMPS */ |
| rMPS = (range & 0xff800000) - rLPS; |
| |
| /* most probable region */ |
| if (offset < rMPS) { |
| RddV = AC_next_state_MPS_64[state]; |
| fINSERT_RANGE(RddV, 8, 8, valMPS); |
| fINSERT_RANGE(RddV, 31, 23, (rMPS >> 23)); |
| fSETWORD(1, RddV, offset); |
| } |
| /* least probable region */ |
| else { |
| RddV = AC_next_state_LPS_64[state]; |
| fINSERT_RANGE(RddV, 8, 8, ((!state) ? (1 - valMPS) : (valMPS))); |
| fINSERT_RANGE(RddV, 31, 23, (rLPS >> 23)); |
| fSETWORD(1, RddV, (offset - rMPS)); |
| } |
| return RddV; |
| } |
| |
| int32_t HELPER(cabacdecbin_pred)(int64_t RssV, int64_t RttV) |
| { |
| int32_t p0 = 0; |
| size4u_t state; |
| size4u_t valMPS; |
| size4u_t bitpos; |
| size4u_t range; |
| size4u_t offset; |
| size4u_t rLPS; |
| size4u_t rMPS; |
| |
| state = fEXTRACTU_RANGE(fGETWORD(1, RttV), 5, 0); |
| valMPS = fEXTRACTU_RANGE(fGETWORD(1, RttV), 8, 8); |
| bitpos = fEXTRACTU_RANGE(fGETWORD(0, RttV), 4, 0); |
| range = fGETWORD(0, RssV); |
| offset = fGETWORD(1, RssV); |
| |
| /* calculate rLPS */ |
| range <<= bitpos; |
| offset <<= bitpos; |
| rLPS = rLPS_table_64x4[state][(range >> 29) & 3]; |
| rLPS = rLPS << 23; /* left aligned */ |
| |
| /* calculate rMPS */ |
| rMPS = (range & 0xff800000) - rLPS; |
| |
| /* most probable region */ |
| if (offset < rMPS) { |
| p0 = valMPS; |
| |
| } |
| /* least probable region */ |
| else { |
| p0 = valMPS ^ 1; |
| } |
| return p0; |
| } |
| |
| static void probe_store(CPUHexagonState *env, int slot, int mmu_idx, |
| bool is_predicated, uintptr_t retaddr) |
| { |
| if (!is_predicated || !(env->slot_cancelled & (1 << slot))) { |
| size1u_t width = env->mem_log_stores[slot].width; |
| target_ulong va = env->mem_log_stores[slot].va; |
| probe_write(env, va, width, mmu_idx, retaddr); |
| } |
| } |
| |
| /* |
| * Called from a mem_noshuf packet to make sure the load doesn't |
| * raise an exception |
| */ |
| void HELPER(probe_noshuf_load)(CPUHexagonState *env, target_ulong va, |
| int size, int mmu_idx) |
| { |
| uintptr_t retaddr = GETPC(); |
| probe_read(env, va, size, mmu_idx, retaddr); |
| } |
| |
| /* Called during packet commit when there are two scalar stores */ |
| void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int args) |
| { |
| int mmu_idx = FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, MMU_IDX); |
| bool is_predicated = |
| FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED); |
| uintptr_t ra = GETPC(); |
| probe_store(env, 0, mmu_idx, is_predicated, ra); |
| } |
| |
| static void probe_hvx_stores(CPUHexagonState *env, int mmu_idx, |
| uintptr_t retaddr) |
| { |
| /* Normal (possibly masked) vector store */ |
| for (int i = 0; i < VSTORES_MAX; i++) { |
| if (env->vstore_pending[i]) { |
| target_ulong va = env->vstore[i].va; |
| int size = env->vstore[i].size; |
| for (int j = 0; j < size; j++) { |
| if (test_bit(j, env->vstore[i].mask)) { |
| probe_write(env, va + j, 1, mmu_idx, retaddr); |
| } |
| } |
| } |
| } |
| |
| /* Scatter store */ |
| if (env->vtcm_pending) { |
| if (env->vtcm_log.op) { |
| /* Need to perform the scatter read/modify/write at commit time */ |
| if (env->vtcm_log.op_size == 2) { |
| SCATTER_OP_PROBE_MEM(size2u_t, mmu_idx, retaddr); |
| } else if (env->vtcm_log.op_size == 4) { |
| /* Word Scatter += */ |
| SCATTER_OP_PROBE_MEM(size4u_t, mmu_idx, retaddr); |
| } else { |
| g_assert_not_reached(); |
| } |
| } else { |
| for (int i = 0; i < sizeof(MMVector); i++) { |
| if (test_bit(i, env->vtcm_log.mask)) { |
| probe_write(env, env->vtcm_log.va[i], 1, mmu_idx, retaddr); |
| } |
| |
| } |
| } |
| } |
| } |
| |
| void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) |
| { |
| uintptr_t retaddr = GETPC(); |
| probe_hvx_stores(env, mmu_idx, retaddr); |
| } |
| |
| void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) |
| { |
| bool has_st0 = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST0); |
| bool has_st1 = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST1); |
| bool has_hvx_stores = |
| FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_HVX_STORES); |
| bool s0_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S0_IS_PRED); |
| bool s1_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S1_IS_PRED); |
| int mmu_idx = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, MMU_IDX); |
| uintptr_t ra = GETPC(); |
| |
| if (has_st0) { |
| probe_store(env, 0, mmu_idx, s0_is_pred, ra); |
| } |
| if (has_st1) { |
| probe_store(env, 1, mmu_idx, s1_is_pred, ra); |
| } |
| if (has_hvx_stores) { |
| probe_hvx_stores(env, mmu_idx, ra); |
| } |
| } |
| |
| #ifndef CONFIG_HEXAGON_IDEF_PARSER |
| /* |
| * mem_noshuf |
| * Section 5.5 of the Hexagon V67 Programmer's Reference Manual |
| * |
| * If the load is in slot 0 and there is a store in slot1 (that |
| * wasn't cancelled), we have to do the store first. |
| */ |
| static void check_noshuf(CPUHexagonState *env, bool pkt_has_store_s1, |
| uint32_t slot, target_ulong vaddr, int size, |
| uintptr_t ra) |
| { |
| if (slot == 0 && pkt_has_store_s1 && |
| ((env->slot_cancelled & (1 << 1)) == 0)) { |
| probe_read(env, vaddr, size, MMU_USER_IDX, ra); |
| commit_store(env, 1, ra); |
| } |
| } |
| #endif |
| |
| /* Floating point */ |
| float64 HELPER(conv_sf2df)(CPUHexagonState *env, float32 RsV) |
| { |
| float64 out_f64; |
| arch_fpop_start(env); |
| out_f64 = float32_to_float64(RsV, &env->fp_status); |
| arch_fpop_end(env); |
| return out_f64; |
| } |
| |
| float32 HELPER(conv_df2sf)(CPUHexagonState *env, float64 RssV) |
| { |
| float32 out_f32; |
| arch_fpop_start(env); |
| out_f32 = float64_to_float32(RssV, &env->fp_status); |
| arch_fpop_end(env); |
| return out_f32; |
| } |
| |
| float32 HELPER(conv_uw2sf)(CPUHexagonState *env, int32_t RsV) |
| { |
| float32 RdV; |
| arch_fpop_start(env); |
| RdV = uint32_to_float32(RsV, &env->fp_status); |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float64 HELPER(conv_uw2df)(CPUHexagonState *env, int32_t RsV) |
| { |
| float64 RddV; |
| arch_fpop_start(env); |
| RddV = uint32_to_float64(RsV, &env->fp_status); |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| float32 HELPER(conv_w2sf)(CPUHexagonState *env, int32_t RsV) |
| { |
| float32 RdV; |
| arch_fpop_start(env); |
| RdV = int32_to_float32(RsV, &env->fp_status); |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float64 HELPER(conv_w2df)(CPUHexagonState *env, int32_t RsV) |
| { |
| float64 RddV; |
| arch_fpop_start(env); |
| RddV = int32_to_float64(RsV, &env->fp_status); |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| float32 HELPER(conv_ud2sf)(CPUHexagonState *env, int64_t RssV) |
| { |
| float32 RdV; |
| arch_fpop_start(env); |
| RdV = uint64_to_float32(RssV, &env->fp_status); |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float64 HELPER(conv_ud2df)(CPUHexagonState *env, int64_t RssV) |
| { |
| float64 RddV; |
| arch_fpop_start(env); |
| RddV = uint64_to_float64(RssV, &env->fp_status); |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| float32 HELPER(conv_d2sf)(CPUHexagonState *env, int64_t RssV) |
| { |
| float32 RdV; |
| arch_fpop_start(env); |
| RdV = int64_to_float32(RssV, &env->fp_status); |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float64 HELPER(conv_d2df)(CPUHexagonState *env, int64_t RssV) |
| { |
| float64 RddV; |
| arch_fpop_start(env); |
| RddV = int64_to_float64(RssV, &env->fp_status); |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| uint32_t HELPER(conv_sf2uw)(CPUHexagonState *env, float32 RsV) |
| { |
| uint32_t RdV; |
| arch_fpop_start(env); |
| /* Hexagon checks the sign before rounding */ |
| if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RdV = 0; |
| } else { |
| RdV = float32_to_uint32(RsV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| int32_t HELPER(conv_sf2w)(CPUHexagonState *env, float32 RsV) |
| { |
| int32_t RdV; |
| arch_fpop_start(env); |
| /* Hexagon returns -1 for NaN */ |
| if (float32_is_any_nan(RsV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RdV = -1; |
| } else { |
| RdV = float32_to_int32(RsV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| uint64_t HELPER(conv_sf2ud)(CPUHexagonState *env, float32 RsV) |
| { |
| uint64_t RddV; |
| arch_fpop_start(env); |
| /* Hexagon checks the sign before rounding */ |
| if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RddV = 0; |
| } else { |
| RddV = float32_to_uint64(RsV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| int64_t HELPER(conv_sf2d)(CPUHexagonState *env, float32 RsV) |
| { |
| int64_t RddV; |
| arch_fpop_start(env); |
| /* Hexagon returns -1 for NaN */ |
| if (float32_is_any_nan(RsV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RddV = -1; |
| } else { |
| RddV = float32_to_int64(RsV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| uint32_t HELPER(conv_df2uw)(CPUHexagonState *env, float64 RssV) |
| { |
| uint32_t RdV; |
| arch_fpop_start(env); |
| /* Hexagon checks the sign before rounding */ |
| if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RdV = 0; |
| } else { |
| RdV = float64_to_uint32(RssV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| int32_t HELPER(conv_df2w)(CPUHexagonState *env, float64 RssV) |
| { |
| int32_t RdV; |
| arch_fpop_start(env); |
| /* Hexagon returns -1 for NaN */ |
| if (float64_is_any_nan(RssV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RdV = -1; |
| } else { |
| RdV = float64_to_int32(RssV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| uint64_t HELPER(conv_df2ud)(CPUHexagonState *env, float64 RssV) |
| { |
| uint64_t RddV; |
| arch_fpop_start(env); |
| /* Hexagon checks the sign before rounding */ |
| if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RddV = 0; |
| } else { |
| RddV = float64_to_uint64(RssV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| int64_t HELPER(conv_df2d)(CPUHexagonState *env, float64 RssV) |
| { |
| int64_t RddV; |
| arch_fpop_start(env); |
| /* Hexagon returns -1 for NaN */ |
| if (float64_is_any_nan(RssV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RddV = -1; |
| } else { |
| RddV = float64_to_int64(RssV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| uint32_t HELPER(conv_sf2uw_chop)(CPUHexagonState *env, float32 RsV) |
| { |
| uint32_t RdV; |
| arch_fpop_start(env); |
| /* Hexagon checks the sign before rounding */ |
| if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RdV = 0; |
| } else { |
| RdV = float32_to_uint32_round_to_zero(RsV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| int32_t HELPER(conv_sf2w_chop)(CPUHexagonState *env, float32 RsV) |
| { |
| int32_t RdV; |
| arch_fpop_start(env); |
| /* Hexagon returns -1 for NaN */ |
| if (float32_is_any_nan(RsV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RdV = -1; |
| } else { |
| RdV = float32_to_int32_round_to_zero(RsV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| uint64_t HELPER(conv_sf2ud_chop)(CPUHexagonState *env, float32 RsV) |
| { |
| uint64_t RddV; |
| arch_fpop_start(env); |
| /* Hexagon checks the sign before rounding */ |
| if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RddV = 0; |
| } else { |
| RddV = float32_to_uint64_round_to_zero(RsV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| int64_t HELPER(conv_sf2d_chop)(CPUHexagonState *env, float32 RsV) |
| { |
| int64_t RddV; |
| arch_fpop_start(env); |
| /* Hexagon returns -1 for NaN */ |
| if (float32_is_any_nan(RsV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RddV = -1; |
| } else { |
| RddV = float32_to_int64_round_to_zero(RsV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| uint32_t HELPER(conv_df2uw_chop)(CPUHexagonState *env, float64 RssV) |
| { |
| uint32_t RdV; |
| arch_fpop_start(env); |
| /* Hexagon checks the sign before rounding */ |
| if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RdV = 0; |
| } else { |
| RdV = float64_to_uint32_round_to_zero(RssV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| int32_t HELPER(conv_df2w_chop)(CPUHexagonState *env, float64 RssV) |
| { |
| int32_t RdV; |
| arch_fpop_start(env); |
| /* Hexagon returns -1 for NaN */ |
| if (float64_is_any_nan(RssV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RdV = -1; |
| } else { |
| RdV = float64_to_int32_round_to_zero(RssV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| uint64_t HELPER(conv_df2ud_chop)(CPUHexagonState *env, float64 RssV) |
| { |
| uint64_t RddV; |
| arch_fpop_start(env); |
| /* Hexagon checks the sign before rounding */ |
| if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RddV = 0; |
| } else { |
| RddV = float64_to_uint64_round_to_zero(RssV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| int64_t HELPER(conv_df2d_chop)(CPUHexagonState *env, float64 RssV) |
| { |
| int64_t RddV; |
| arch_fpop_start(env); |
| /* Hexagon returns -1 for NaN */ |
| if (float64_is_any_nan(RssV)) { |
| float_raise(float_flag_invalid, &env->fp_status); |
| RddV = -1; |
| } else { |
| RddV = float64_to_int64_round_to_zero(RssV, &env->fp_status); |
| } |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| float32 HELPER(sfadd)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| float32 RdV; |
| arch_fpop_start(env); |
| RdV = float32_add(RsV, RtV, &env->fp_status); |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float32 HELPER(sfsub)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| float32 RdV; |
| arch_fpop_start(env); |
| RdV = float32_sub(RsV, RtV, &env->fp_status); |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| int32_t HELPER(sfcmpeq)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| int32_t PdV; |
| arch_fpop_start(env); |
| PdV = f8BITSOF(float32_eq_quiet(RsV, RtV, &env->fp_status)); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| int32_t HELPER(sfcmpgt)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| int cmp; |
| int32_t PdV; |
| arch_fpop_start(env); |
| cmp = float32_compare_quiet(RsV, RtV, &env->fp_status); |
| PdV = f8BITSOF(cmp == float_relation_greater); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| int32_t HELPER(sfcmpge)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| int cmp; |
| int32_t PdV; |
| arch_fpop_start(env); |
| cmp = float32_compare_quiet(RsV, RtV, &env->fp_status); |
| PdV = f8BITSOF(cmp == float_relation_greater || |
| cmp == float_relation_equal); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| int32_t HELPER(sfcmpuo)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| int32_t PdV; |
| arch_fpop_start(env); |
| PdV = f8BITSOF(float32_unordered_quiet(RsV, RtV, &env->fp_status)); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| float32 HELPER(sfmax)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| float32 RdV; |
| arch_fpop_start(env); |
| RdV = float32_maximum_number(RsV, RtV, &env->fp_status); |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float32 HELPER(sfmin)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| float32 RdV; |
| arch_fpop_start(env); |
| RdV = float32_minimum_number(RsV, RtV, &env->fp_status); |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| int32_t HELPER(sfclass)(CPUHexagonState *env, float32 RsV, int32_t uiV) |
| { |
| int32_t PdV = 0; |
| arch_fpop_start(env); |
| if (fGETBIT(0, uiV) && float32_is_zero(RsV)) { |
| PdV = 0xff; |
| } |
| if (fGETBIT(1, uiV) && float32_is_normal(RsV)) { |
| PdV = 0xff; |
| } |
| if (fGETBIT(2, uiV) && float32_is_denormal(RsV)) { |
| PdV = 0xff; |
| } |
| if (fGETBIT(3, uiV) && float32_is_infinity(RsV)) { |
| PdV = 0xff; |
| } |
| if (fGETBIT(4, uiV) && float32_is_any_nan(RsV)) { |
| PdV = 0xff; |
| } |
| set_float_exception_flags(0, &env->fp_status); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| float32 HELPER(sffixupn)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| float32 RdV = 0; |
| int adjust; |
| arch_fpop_start(env); |
| arch_sf_recip_common(&RsV, &RtV, &RdV, &adjust, &env->fp_status); |
| RdV = RsV; |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float32 HELPER(sffixupd)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| float32 RdV = 0; |
| int adjust; |
| arch_fpop_start(env); |
| arch_sf_recip_common(&RsV, &RtV, &RdV, &adjust, &env->fp_status); |
| RdV = RtV; |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float32 HELPER(sffixupr)(CPUHexagonState *env, float32 RsV) |
| { |
| float32 RdV = 0; |
| int adjust; |
| arch_fpop_start(env); |
| arch_sf_invsqrt_common(&RsV, &RdV, &adjust, &env->fp_status); |
| RdV = RsV; |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float64 HELPER(dfadd)(CPUHexagonState *env, float64 RssV, float64 RttV) |
| { |
| float64 RddV; |
| arch_fpop_start(env); |
| RddV = float64_add(RssV, RttV, &env->fp_status); |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| float64 HELPER(dfsub)(CPUHexagonState *env, float64 RssV, float64 RttV) |
| { |
| float64 RddV; |
| arch_fpop_start(env); |
| RddV = float64_sub(RssV, RttV, &env->fp_status); |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| float64 HELPER(dfmax)(CPUHexagonState *env, float64 RssV, float64 RttV) |
| { |
| float64 RddV; |
| arch_fpop_start(env); |
| RddV = float64_maximum_number(RssV, RttV, &env->fp_status); |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| float64 HELPER(dfmin)(CPUHexagonState *env, float64 RssV, float64 RttV) |
| { |
| float64 RddV; |
| arch_fpop_start(env); |
| RddV = float64_minimum_number(RssV, RttV, &env->fp_status); |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| int32_t HELPER(dfcmpeq)(CPUHexagonState *env, float64 RssV, float64 RttV) |
| { |
| int32_t PdV; |
| arch_fpop_start(env); |
| PdV = f8BITSOF(float64_eq_quiet(RssV, RttV, &env->fp_status)); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| int32_t HELPER(dfcmpgt)(CPUHexagonState *env, float64 RssV, float64 RttV) |
| { |
| int cmp; |
| int32_t PdV; |
| arch_fpop_start(env); |
| cmp = float64_compare_quiet(RssV, RttV, &env->fp_status); |
| PdV = f8BITSOF(cmp == float_relation_greater); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| int32_t HELPER(dfcmpge)(CPUHexagonState *env, float64 RssV, float64 RttV) |
| { |
| int cmp; |
| int32_t PdV; |
| arch_fpop_start(env); |
| cmp = float64_compare_quiet(RssV, RttV, &env->fp_status); |
| PdV = f8BITSOF(cmp == float_relation_greater || |
| cmp == float_relation_equal); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| int32_t HELPER(dfcmpuo)(CPUHexagonState *env, float64 RssV, float64 RttV) |
| { |
| int32_t PdV; |
| arch_fpop_start(env); |
| PdV = f8BITSOF(float64_unordered_quiet(RssV, RttV, &env->fp_status)); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| int32_t HELPER(dfclass)(CPUHexagonState *env, float64 RssV, int32_t uiV) |
| { |
| int32_t PdV = 0; |
| arch_fpop_start(env); |
| if (fGETBIT(0, uiV) && float64_is_zero(RssV)) { |
| PdV = 0xff; |
| } |
| if (fGETBIT(1, uiV) && float64_is_normal(RssV)) { |
| PdV = 0xff; |
| } |
| if (fGETBIT(2, uiV) && float64_is_denormal(RssV)) { |
| PdV = 0xff; |
| } |
| if (fGETBIT(3, uiV) && float64_is_infinity(RssV)) { |
| PdV = 0xff; |
| } |
| if (fGETBIT(4, uiV) && float64_is_any_nan(RssV)) { |
| PdV = 0xff; |
| } |
| set_float_exception_flags(0, &env->fp_status); |
| arch_fpop_end(env); |
| return PdV; |
| } |
| |
| float32 HELPER(sfmpy)(CPUHexagonState *env, float32 RsV, float32 RtV) |
| { |
| float32 RdV; |
| arch_fpop_start(env); |
| RdV = internal_mpyf(RsV, RtV, &env->fp_status); |
| arch_fpop_end(env); |
| return RdV; |
| } |
| |
| float32 HELPER(sffma)(CPUHexagonState *env, float32 RxV, |
| float32 RsV, float32 RtV) |
| { |
| arch_fpop_start(env); |
| RxV = internal_fmafx(RsV, RtV, RxV, 0, &env->fp_status); |
| arch_fpop_end(env); |
| return RxV; |
| } |
| |
| static bool is_zero_prod(float32 a, float32 b) |
| { |
| return ((float32_is_zero(a) && is_finite(b)) || |
| (float32_is_zero(b) && is_finite(a))); |
| } |
| |
| static float32 check_nan(float32 dst, float32 x, float_status *fp_status) |
| { |
| float32 ret = dst; |
| if (float32_is_any_nan(x)) { |
| if (extract32(x, 22, 1) == 0) { |
| float_raise(float_flag_invalid, fp_status); |
| } |
| ret = make_float32(0xffffffff); /* nan */ |
| } |
| return ret; |
| } |
| |
| float32 HELPER(sffma_sc)(CPUHexagonState *env, float32 RxV, |
| float32 RsV, float32 RtV, float32 PuV) |
| { |
| size4s_t tmp; |
| arch_fpop_start(env); |
| RxV = check_nan(RxV, RxV, &env->fp_status); |
| RxV = check_nan(RxV, RsV, &env->fp_status); |
| RxV = check_nan(RxV, RtV, &env->fp_status); |
| tmp = internal_fmafx(RsV, RtV, RxV, fSXTN(8, 64, PuV), &env->fp_status); |
| if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) { |
| RxV = tmp; |
| } |
| arch_fpop_end(env); |
| return RxV; |
| } |
| |
| float32 HELPER(sffms)(CPUHexagonState *env, float32 RxV, |
| float32 RsV, float32 RtV) |
| { |
| float32 neg_RsV; |
| arch_fpop_start(env); |
| neg_RsV = float32_set_sign(RsV, float32_is_neg(RsV) ? 0 : 1); |
| RxV = internal_fmafx(neg_RsV, RtV, RxV, 0, &env->fp_status); |
| arch_fpop_end(env); |
| return RxV; |
| } |
| |
| static bool is_inf_prod(int32_t a, int32_t b) |
| { |
| return (float32_is_infinity(a) && float32_is_infinity(b)) || |
| (float32_is_infinity(a) && is_finite(b) && !float32_is_zero(b)) || |
| (float32_is_infinity(b) && is_finite(a) && !float32_is_zero(a)); |
| } |
| |
| float32 HELPER(sffma_lib)(CPUHexagonState *env, float32 RxV, |
| float32 RsV, float32 RtV) |
| { |
| bool infinp; |
| bool infminusinf; |
| float32 tmp; |
| |
| arch_fpop_start(env); |
| set_float_rounding_mode(float_round_nearest_even, &env->fp_status); |
| infminusinf = float32_is_infinity(RxV) && |
| is_inf_prod(RsV, RtV) && |
| (fGETBIT(31, RsV ^ RxV ^ RtV) != 0); |
| infinp = float32_is_infinity(RxV) || |
| float32_is_infinity(RtV) || |
| float32_is_infinity(RsV); |
| RxV = check_nan(RxV, RxV, &env->fp_status); |
| RxV = check_nan(RxV, RsV, &env->fp_status); |
| RxV = check_nan(RxV, RtV, &env->fp_status); |
| tmp = internal_fmafx(RsV, RtV, RxV, 0, &env->fp_status); |
| if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) { |
| RxV = tmp; |
| } |
| set_float_exception_flags(0, &env->fp_status); |
| if (float32_is_infinity(RxV) && !infinp) { |
| RxV = RxV - 1; |
| } |
| if (infminusinf) { |
| RxV = 0; |
| } |
| arch_fpop_end(env); |
| return RxV; |
| } |
| |
| float32 HELPER(sffms_lib)(CPUHexagonState *env, float32 RxV, |
| float32 RsV, float32 RtV) |
| { |
| bool infinp; |
| bool infminusinf; |
| float32 tmp; |
| |
| arch_fpop_start(env); |
| set_float_rounding_mode(float_round_nearest_even, &env->fp_status); |
| infminusinf = float32_is_infinity(RxV) && |
| is_inf_prod(RsV, RtV) && |
| (fGETBIT(31, RsV ^ RxV ^ RtV) == 0); |
| infinp = float32_is_infinity(RxV) || |
| float32_is_infinity(RtV) || |
| float32_is_infinity(RsV); |
| RxV = check_nan(RxV, RxV, &env->fp_status); |
| RxV = check_nan(RxV, RsV, &env->fp_status); |
| RxV = check_nan(RxV, RtV, &env->fp_status); |
| float32 minus_RsV = float32_sub(float32_zero, RsV, &env->fp_status); |
| tmp = internal_fmafx(minus_RsV, RtV, RxV, 0, &env->fp_status); |
| if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) { |
| RxV = tmp; |
| } |
| set_float_exception_flags(0, &env->fp_status); |
| if (float32_is_infinity(RxV) && !infinp) { |
| RxV = RxV - 1; |
| } |
| if (infminusinf) { |
| RxV = 0; |
| } |
| arch_fpop_end(env); |
| return RxV; |
| } |
| |
| float64 HELPER(dfmpyfix)(CPUHexagonState *env, float64 RssV, float64 RttV) |
| { |
| int64_t RddV; |
| arch_fpop_start(env); |
| if (float64_is_denormal(RssV) && |
| (float64_getexp(RttV) >= 512) && |
| float64_is_normal(RttV)) { |
| RddV = float64_mul(RssV, make_float64(0x4330000000000000), |
| &env->fp_status); |
| } else if (float64_is_denormal(RttV) && |
| (float64_getexp(RssV) >= 512) && |
| float64_is_normal(RssV)) { |
| RddV = float64_mul(RssV, make_float64(0x3cb0000000000000), |
| &env->fp_status); |
| } else { |
| RddV = RssV; |
| } |
| arch_fpop_end(env); |
| return RddV; |
| } |
| |
| float64 HELPER(dfmpyhh)(CPUHexagonState *env, float64 RxxV, |
| float64 RssV, float64 RttV) |
| { |
| arch_fpop_start(env); |
| RxxV = internal_mpyhh(RssV, RttV, RxxV, &env->fp_status); |
| arch_fpop_end(env); |
| return RxxV; |
| } |
| |
| /* Histogram instructions */ |
| |
| void HELPER(vhist)(CPUHexagonState *env) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int lane = 0; lane < 8; lane++) { |
| for (int i = 0; i < sizeof(MMVector) / 8; ++i) { |
| unsigned char value = input->ub[(sizeof(MMVector) / 8) * lane + i]; |
| unsigned char regno = value >> 3; |
| unsigned char element = value & 7; |
| |
| env->VRegs[regno].uh[(sizeof(MMVector) / 16) * lane + element]++; |
| } |
| } |
| } |
| |
| void HELPER(vhistq)(CPUHexagonState *env) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int lane = 0; lane < 8; lane++) { |
| for (int i = 0; i < sizeof(MMVector) / 8; ++i) { |
| unsigned char value = input->ub[(sizeof(MMVector) / 8) * lane + i]; |
| unsigned char regno = value >> 3; |
| unsigned char element = value & 7; |
| |
| if (fGETQBIT(env->qtmp, sizeof(MMVector) / 8 * lane + i)) { |
| env->VRegs[regno].uh[ |
| (sizeof(MMVector) / 16) * lane + element]++; |
| } |
| } |
| } |
| } |
| |
| void HELPER(vwhist256)(CPUHexagonState *env) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int i = 0; i < (sizeof(MMVector) / 2); i++) { |
| unsigned int bucket = fGETUBYTE(0, input->h[i]); |
| unsigned int weight = fGETUBYTE(1, input->h[i]); |
| unsigned int vindex = (bucket >> 3) & 0x1F; |
| unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7); |
| |
| env->VRegs[vindex].uh[elindex] = |
| env->VRegs[vindex].uh[elindex] + weight; |
| } |
| } |
| |
| void HELPER(vwhist256q)(CPUHexagonState *env) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int i = 0; i < (sizeof(MMVector) / 2); i++) { |
| unsigned int bucket = fGETUBYTE(0, input->h[i]); |
| unsigned int weight = fGETUBYTE(1, input->h[i]); |
| unsigned int vindex = (bucket >> 3) & 0x1F; |
| unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7); |
| |
| if (fGETQBIT(env->qtmp, 2 * i)) { |
| env->VRegs[vindex].uh[elindex] = |
| env->VRegs[vindex].uh[elindex] + weight; |
| } |
| } |
| } |
| |
| void HELPER(vwhist256_sat)(CPUHexagonState *env) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int i = 0; i < (sizeof(MMVector) / 2); i++) { |
| unsigned int bucket = fGETUBYTE(0, input->h[i]); |
| unsigned int weight = fGETUBYTE(1, input->h[i]); |
| unsigned int vindex = (bucket >> 3) & 0x1F; |
| unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7); |
| |
| env->VRegs[vindex].uh[elindex] = |
| fVSATUH(env->VRegs[vindex].uh[elindex] + weight); |
| } |
| } |
| |
| void HELPER(vwhist256q_sat)(CPUHexagonState *env) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int i = 0; i < (sizeof(MMVector) / 2); i++) { |
| unsigned int bucket = fGETUBYTE(0, input->h[i]); |
| unsigned int weight = fGETUBYTE(1, input->h[i]); |
| unsigned int vindex = (bucket >> 3) & 0x1F; |
| unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7); |
| |
| if (fGETQBIT(env->qtmp, 2 * i)) { |
| env->VRegs[vindex].uh[elindex] = |
| fVSATUH(env->VRegs[vindex].uh[elindex] + weight); |
| } |
| } |
| } |
| |
| void HELPER(vwhist128)(CPUHexagonState *env) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int i = 0; i < (sizeof(MMVector) / 2); i++) { |
| unsigned int bucket = fGETUBYTE(0, input->h[i]); |
| unsigned int weight = fGETUBYTE(1, input->h[i]); |
| unsigned int vindex = (bucket >> 3) & 0x1F; |
| unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3); |
| |
| env->VRegs[vindex].uw[elindex] = |
| env->VRegs[vindex].uw[elindex] + weight; |
| } |
| } |
| |
| void HELPER(vwhist128q)(CPUHexagonState *env) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int i = 0; i < (sizeof(MMVector) / 2); i++) { |
| unsigned int bucket = fGETUBYTE(0, input->h[i]); |
| unsigned int weight = fGETUBYTE(1, input->h[i]); |
| unsigned int vindex = (bucket >> 3) & 0x1F; |
| unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3); |
| |
| if (fGETQBIT(env->qtmp, 2 * i)) { |
| env->VRegs[vindex].uw[elindex] = |
| env->VRegs[vindex].uw[elindex] + weight; |
| } |
| } |
| } |
| |
| void HELPER(vwhist128m)(CPUHexagonState *env, int32_t uiV) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int i = 0; i < (sizeof(MMVector) / 2); i++) { |
| unsigned int bucket = fGETUBYTE(0, input->h[i]); |
| unsigned int weight = fGETUBYTE(1, input->h[i]); |
| unsigned int vindex = (bucket >> 3) & 0x1F; |
| unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3); |
| |
| if ((bucket & 1) == uiV) { |
| env->VRegs[vindex].uw[elindex] = |
| env->VRegs[vindex].uw[elindex] + weight; |
| } |
| } |
| } |
| |
| void HELPER(vwhist128qm)(CPUHexagonState *env, int32_t uiV) |
| { |
| MMVector *input = &env->tmp_VRegs[0]; |
| |
| for (int i = 0; i < (sizeof(MMVector) / 2); i++) { |
| unsigned int bucket = fGETUBYTE(0, input->h[i]); |
| unsigned int weight = fGETUBYTE(1, input->h[i]); |
| unsigned int vindex = (bucket >> 3) & 0x1F; |
| unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3); |
| |
| if (((bucket & 1) == uiV) && fGETQBIT(env->qtmp, 2 * i)) { |
| env->VRegs[vindex].uw[elindex] = |
| env->VRegs[vindex].uw[elindex] + weight; |
| } |
| } |
| } |
| |
| /* These macros can be referenced in the generated helper functions */ |
| #define warn(...) /* Nothing */ |
| #define fatal(...) g_assert_not_reached(); |
| |
| #define BOGUS_HELPER(tag) \ |
| printf("ERROR: bogus helper: " #tag "\n") |
| |
| #include "helper_funcs_generated.c.inc" |