aboutsummaryrefslogtreecommitdiffstats
path: root/target/ppc/translate/fixedpoint-impl.c.inc
diff options
context:
space:
mode:
Diffstat (limited to 'target/ppc/translate/fixedpoint-impl.c.inc')
-rw-r--r--target/ppc/translate/fixedpoint-impl.c.inc494
1 files changed, 494 insertions, 0 deletions
diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc
new file mode 100644
index 000000000..7fecff457
--- /dev/null
+++ b/target/ppc/translate/fixedpoint-impl.c.inc
@@ -0,0 +1,494 @@
+/*
+ * Power ISA decode for Fixed-Point Facility instructions
+ *
+ * Copyright (c) 2021 Instituto de Pesquisas Eldorado (eldorado.org.br)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Fixed-Point Load/Store Instructions
+ */
+
+static bool do_ldst(DisasContext *ctx, int rt, int ra, TCGv displ, bool update,
+ bool store, MemOp mop)
+{
+ TCGv ea;
+
+ if (update && (ra == 0 || (!store && ra == rt))) {
+ gen_invalid(ctx);
+ return true;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+
+ ea = do_ea_calc(ctx, ra, displ);
+ mop ^= ctx->default_tcg_memop_mask;
+ if (store) {
+ tcg_gen_qemu_st_tl(cpu_gpr[rt], ea, ctx->mem_idx, mop);
+ } else {
+ tcg_gen_qemu_ld_tl(cpu_gpr[rt], ea, ctx->mem_idx, mop);
+ }
+ if (update) {
+ tcg_gen_mov_tl(cpu_gpr[ra], ea);
+ }
+ tcg_temp_free(ea);
+
+ return true;
+}
+
+static bool do_ldst_D(DisasContext *ctx, arg_D *a, bool update, bool store,
+ MemOp mop)
+{
+ return do_ldst(ctx, a->rt, a->ra, tcg_constant_tl(a->si), update, store, mop);
+}
+
+static bool do_ldst_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool update,
+ bool store, MemOp mop)
+{
+ arg_D d;
+ if (!resolve_PLS_D(ctx, &d, a)) {
+ return true;
+ }
+ return do_ldst_D(ctx, &d, update, store, mop);
+}
+
+static bool do_ldst_X(DisasContext *ctx, arg_X *a, bool update,
+ bool store, MemOp mop)
+{
+ return do_ldst(ctx, a->rt, a->ra, cpu_gpr[a->rb], update, store, mop);
+}
+
+static bool do_ldst_quad(DisasContext *ctx, arg_D *a, bool store, bool prefixed)
+{
+#if defined(TARGET_PPC64)
+ TCGv ea;
+ TCGv_i64 low_addr_gpr, high_addr_gpr;
+ MemOp mop;
+
+ REQUIRE_INSNS_FLAGS(ctx, 64BX);
+
+ if (!prefixed && !(ctx->insns_flags2 & PPC2_LSQ_ISA207)) {
+ if (ctx->pr) {
+ /* lq and stq were privileged prior to V. 2.07 */
+ gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return true;
+ }
+
+ if (ctx->le_mode) {
+ gen_align_no_le(ctx);
+ return true;
+ }
+ }
+
+ if (!store && unlikely(a->ra == a->rt)) {
+ gen_invalid(ctx);
+ return true;
+ }
+
+ gen_set_access_type(ctx, ACCESS_INT);
+ ea = do_ea_calc(ctx, a->ra, tcg_constant_tl(a->si));
+
+ if (prefixed || !ctx->le_mode) {
+ low_addr_gpr = cpu_gpr[a->rt];
+ high_addr_gpr = cpu_gpr[a->rt + 1];
+ } else {
+ low_addr_gpr = cpu_gpr[a->rt + 1];
+ high_addr_gpr = cpu_gpr[a->rt];
+ }
+
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+ if (HAVE_ATOMIC128) {
+ mop = DEF_MEMOP(MO_128);
+ TCGv_i32 oi = tcg_constant_i32(make_memop_idx(mop, ctx->mem_idx));
+ if (store) {
+ if (ctx->le_mode) {
+ gen_helper_stq_le_parallel(cpu_env, ea, low_addr_gpr,
+ high_addr_gpr, oi);
+ } else {
+ gen_helper_stq_be_parallel(cpu_env, ea, high_addr_gpr,
+ low_addr_gpr, oi);
+
+ }
+ } else {
+ if (ctx->le_mode) {
+ gen_helper_lq_le_parallel(low_addr_gpr, cpu_env, ea, oi);
+ tcg_gen_ld_i64(high_addr_gpr, cpu_env,
+ offsetof(CPUPPCState, retxh));
+ } else {
+ gen_helper_lq_be_parallel(high_addr_gpr, cpu_env, ea, oi);
+ tcg_gen_ld_i64(low_addr_gpr, cpu_env,
+ offsetof(CPUPPCState, retxh));
+ }
+ }
+ } else {
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ }
+ } else {
+ mop = DEF_MEMOP(MO_Q);
+ if (store) {
+ tcg_gen_qemu_st_i64(low_addr_gpr, ea, ctx->mem_idx, mop);
+ } else {
+ tcg_gen_qemu_ld_i64(low_addr_gpr, ea, ctx->mem_idx, mop);
+ }
+
+ gen_addr_add(ctx, ea, ea, 8);
+
+ if (store) {
+ tcg_gen_qemu_st_i64(high_addr_gpr, ea, ctx->mem_idx, mop);
+ } else {
+ tcg_gen_qemu_ld_i64(high_addr_gpr, ea, ctx->mem_idx, mop);
+ }
+ }
+ tcg_temp_free(ea);
+#else
+ qemu_build_not_reached();
+#endif
+
+ return true;
+}
+
+static bool do_ldst_quad_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool store)
+{
+ arg_D d;
+ if (!resolve_PLS_D(ctx, &d, a)) {
+ return true;
+ }
+
+ return do_ldst_quad(ctx, &d, store, true);
+}
+
+/* Load Byte and Zero */
+TRANS(LBZ, do_ldst_D, false, false, MO_UB)
+TRANS(LBZX, do_ldst_X, false, false, MO_UB)
+TRANS(LBZU, do_ldst_D, true, false, MO_UB)
+TRANS(LBZUX, do_ldst_X, true, false, MO_UB)
+TRANS(PLBZ, do_ldst_PLS_D, false, false, MO_UB)
+
+/* Load Halfword and Zero */
+TRANS(LHZ, do_ldst_D, false, false, MO_UW)
+TRANS(LHZX, do_ldst_X, false, false, MO_UW)
+TRANS(LHZU, do_ldst_D, true, false, MO_UW)
+TRANS(LHZUX, do_ldst_X, true, false, MO_UW)
+TRANS(PLHZ, do_ldst_PLS_D, false, false, MO_UW)
+
+/* Load Halfword Algebraic */
+TRANS(LHA, do_ldst_D, false, false, MO_SW)
+TRANS(LHAX, do_ldst_X, false, false, MO_SW)
+TRANS(LHAU, do_ldst_D, true, false, MO_SW)
+TRANS(LHAXU, do_ldst_X, true, false, MO_SW)
+TRANS(PLHA, do_ldst_PLS_D, false, false, MO_SW)
+
+/* Load Word and Zero */
+TRANS(LWZ, do_ldst_D, false, false, MO_UL)
+TRANS(LWZX, do_ldst_X, false, false, MO_UL)
+TRANS(LWZU, do_ldst_D, true, false, MO_UL)
+TRANS(LWZUX, do_ldst_X, true, false, MO_UL)
+TRANS(PLWZ, do_ldst_PLS_D, false, false, MO_UL)
+
+/* Load Word Algebraic */
+TRANS64(LWA, do_ldst_D, false, false, MO_SL)
+TRANS64(LWAX, do_ldst_X, false, false, MO_SL)
+TRANS64(LWAUX, do_ldst_X, true, false, MO_SL)
+TRANS64(PLWA, do_ldst_PLS_D, false, false, MO_SL)
+
+/* Load Doubleword */
+TRANS64(LD, do_ldst_D, false, false, MO_Q)
+TRANS64(LDX, do_ldst_X, false, false, MO_Q)
+TRANS64(LDU, do_ldst_D, true, false, MO_Q)
+TRANS64(LDUX, do_ldst_X, true, false, MO_Q)
+TRANS64(PLD, do_ldst_PLS_D, false, false, MO_Q)
+
+/* Load Quadword */
+TRANS64(LQ, do_ldst_quad, false, false);
+TRANS64(PLQ, do_ldst_quad_PLS_D, false);
+
+/* Store Byte */
+TRANS(STB, do_ldst_D, false, true, MO_UB)
+TRANS(STBX, do_ldst_X, false, true, MO_UB)
+TRANS(STBU, do_ldst_D, true, true, MO_UB)
+TRANS(STBUX, do_ldst_X, true, true, MO_UB)
+TRANS(PSTB, do_ldst_PLS_D, false, true, MO_UB)
+
+/* Store Halfword */
+TRANS(STH, do_ldst_D, false, true, MO_UW)
+TRANS(STHX, do_ldst_X, false, true, MO_UW)
+TRANS(STHU, do_ldst_D, true, true, MO_UW)
+TRANS(STHUX, do_ldst_X, true, true, MO_UW)
+TRANS(PSTH, do_ldst_PLS_D, false, true, MO_UW)
+
+/* Store Word */
+TRANS(STW, do_ldst_D, false, true, MO_UL)
+TRANS(STWX, do_ldst_X, false, true, MO_UL)
+TRANS(STWU, do_ldst_D, true, true, MO_UL)
+TRANS(STWUX, do_ldst_X, true, true, MO_UL)
+TRANS(PSTW, do_ldst_PLS_D, false, true, MO_UL)
+
+/* Store Doubleword */
+TRANS64(STD, do_ldst_D, false, true, MO_Q)
+TRANS64(STDX, do_ldst_X, false, true, MO_Q)
+TRANS64(STDU, do_ldst_D, true, true, MO_Q)
+TRANS64(STDUX, do_ldst_X, true, true, MO_Q)
+TRANS64(PSTD, do_ldst_PLS_D, false, true, MO_Q)
+
+/* Store Quadword */
+TRANS64(STQ, do_ldst_quad, true, false);
+TRANS64(PSTQ, do_ldst_quad_PLS_D, true);
+
+/*
+ * Fixed-Point Compare Instructions
+ */
+
+static bool do_cmp_X(DisasContext *ctx, arg_X_bfl *a, bool s)
+{
+ if ((ctx->insns_flags & PPC_64B) == 0) {
+ /*
+ * For 32-bit implementations, The Programming Environments Manual says
+ * that "the L field must be cleared, otherwise the instruction form is
+ * invalid." It seems, however, that most 32-bit CPUs ignore invalid
+ * forms (e.g., section "Instruction Formats" of the 405 and 440
+ * manuals, "Integer Compare Instructions" of the 601 manual), with the
+ * notable exception of the e500 and e500mc, where L=1 was reported to
+ * cause an exception.
+ */
+ if (a->l) {
+ if ((ctx->insns_flags2 & PPC2_BOOKE206)) {
+ /*
+ * For 32-bit Book E v2.06 implementations (i.e. e500/e500mc),
+ * generate an illegal instruction exception.
+ */
+ return false;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Invalid form of CMP%s at 0x" TARGET_FMT_lx ", L = 1\n",
+ s ? "" : "L", ctx->cia);
+ }
+ }
+ gen_op_cmp32(cpu_gpr[a->ra], cpu_gpr[a->rb], s, a->bf);
+ return true;
+ }
+
+ /* For 64-bit implementations, deal with bit L accordingly. */
+ if (a->l) {
+ gen_op_cmp(cpu_gpr[a->ra], cpu_gpr[a->rb], s, a->bf);
+ } else {
+ gen_op_cmp32(cpu_gpr[a->ra], cpu_gpr[a->rb], s, a->bf);
+ }
+ return true;
+}
+
+static bool do_cmp_D(DisasContext *ctx, arg_D_bf *a, bool s)
+{
+ if ((ctx->insns_flags & PPC_64B) == 0) {
+ /*
+ * For 32-bit implementations, The Programming Environments Manual says
+ * that "the L field must be cleared, otherwise the instruction form is
+ * invalid." It seems, however, that most 32-bit CPUs ignore invalid
+ * forms (e.g., section "Instruction Formats" of the 405 and 440
+ * manuals, "Integer Compare Instructions" of the 601 manual), with the
+ * notable exception of the e500 and e500mc, where L=1 was reported to
+ * cause an exception.
+ */
+ if (a->l) {
+ if ((ctx->insns_flags2 & PPC2_BOOKE206)) {
+ /*
+ * For 32-bit Book E v2.06 implementations (i.e. e500/e500mc),
+ * generate an illegal instruction exception.
+ */
+ return false;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Invalid form of CMP%s at 0x" TARGET_FMT_lx ", L = 1\n",
+ s ? "I" : "LI", ctx->cia);
+ }
+ }
+ gen_op_cmp32(cpu_gpr[a->ra], tcg_constant_tl(a->imm), s, a->bf);
+ return true;
+ }
+
+ /* For 64-bit implementations, deal with bit L accordingly. */
+ if (a->l) {
+ gen_op_cmp(cpu_gpr[a->ra], tcg_constant_tl(a->imm), s, a->bf);
+ } else {
+ gen_op_cmp32(cpu_gpr[a->ra], tcg_constant_tl(a->imm), s, a->bf);
+ }
+ return true;
+}
+
+TRANS(CMP, do_cmp_X, true);
+TRANS(CMPL, do_cmp_X, false);
+TRANS(CMPI, do_cmp_D, true);
+TRANS(CMPLI, do_cmp_D, false);
+
+/*
+ * Fixed-Point Arithmetic Instructions
+ */
+
+static bool trans_ADDI(DisasContext *ctx, arg_D *a)
+{
+ if (a->ra) {
+ tcg_gen_addi_tl(cpu_gpr[a->rt], cpu_gpr[a->ra], a->si);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[a->rt], a->si);
+ }
+ return true;
+}
+
+static bool trans_PADDI(DisasContext *ctx, arg_PLS_D *a)
+{
+ arg_D d;
+ if (!resolve_PLS_D(ctx, &d, a)) {
+ return true;
+ }
+ return trans_ADDI(ctx, &d);
+}
+
+static bool trans_ADDIS(DisasContext *ctx, arg_D *a)
+{
+ a->si <<= 16;
+ return trans_ADDI(ctx, a);
+}
+
+static bool trans_ADDPCIS(DisasContext *ctx, arg_DX *a)
+{
+ REQUIRE_INSNS_FLAGS2(ctx, ISA300);
+ tcg_gen_movi_tl(cpu_gpr[a->rt], ctx->base.pc_next + (a->d << 16));
+ return true;
+}
+
+static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a)
+{
+ gen_invalid(ctx);
+ return true;
+}
+
+static bool trans_PNOP(DisasContext *ctx, arg_PNOP *a)
+{
+ return true;
+}
+
+static bool do_set_bool_cond(DisasContext *ctx, arg_X_bi *a, bool neg, bool rev)
+{
+ REQUIRE_INSNS_FLAGS2(ctx, ISA310);
+ uint32_t mask = 0x08 >> (a->bi & 0x03);
+ TCGCond cond = rev ? TCG_COND_EQ : TCG_COND_NE;
+ TCGv temp = tcg_temp_new();
+
+ tcg_gen_extu_i32_tl(temp, cpu_crf[a->bi >> 2]);
+ tcg_gen_andi_tl(temp, temp, mask);
+ tcg_gen_setcondi_tl(cond, cpu_gpr[a->rt], temp, 0);
+ if (neg) {
+ tcg_gen_neg_tl(cpu_gpr[a->rt], cpu_gpr[a->rt]);
+ }
+ tcg_temp_free(temp);
+
+ return true;
+}
+
+TRANS(SETBC, do_set_bool_cond, false, false)
+TRANS(SETBCR, do_set_bool_cond, false, true)
+TRANS(SETNBC, do_set_bool_cond, true, false)
+TRANS(SETNBCR, do_set_bool_cond, true, true)
+
+static bool trans_CFUGED(DisasContext *ctx, arg_X *a)
+{
+ REQUIRE_64BIT(ctx);
+ REQUIRE_INSNS_FLAGS2(ctx, ISA310);
+#if defined(TARGET_PPC64)
+ gen_helper_CFUGED(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]);
+#else
+ qemu_build_not_reached();
+#endif
+ return true;
+}
+
+static void do_cntzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask, int64_t trail)
+{
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+
+ tcg_gen_and_i64(t0, src, mask);
+ if (trail) {
+ tcg_gen_ctzi_i64(t0, t0, -1);
+ } else {
+ tcg_gen_clzi_i64(t0, t0, -1);
+ }
+
+ tcg_gen_setcondi_i64(TCG_COND_NE, t1, t0, -1);
+ tcg_gen_andi_i64(t0, t0, 63);
+ tcg_gen_xori_i64(t0, t0, 63);
+ if (trail) {
+ tcg_gen_shl_i64(t0, mask, t0);
+ tcg_gen_shl_i64(t0, t0, t1);
+ } else {
+ tcg_gen_shr_i64(t0, mask, t0);
+ tcg_gen_shr_i64(t0, t0, t1);
+ }
+
+ tcg_gen_ctpop_i64(dst, t0);
+
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+}
+
+static bool trans_CNTLZDM(DisasContext *ctx, arg_X *a)
+{
+ REQUIRE_64BIT(ctx);
+ REQUIRE_INSNS_FLAGS2(ctx, ISA310);
+#if defined(TARGET_PPC64)
+ do_cntzdm(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb], false);
+#else
+ qemu_build_not_reached();
+#endif
+ return true;
+}
+
+static bool trans_CNTTZDM(DisasContext *ctx, arg_X *a)
+{
+ REQUIRE_64BIT(ctx);
+ REQUIRE_INSNS_FLAGS2(ctx, ISA310);
+#if defined(TARGET_PPC64)
+ do_cntzdm(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb], true);
+#else
+ qemu_build_not_reached();
+#endif
+ return true;
+}
+
+static bool trans_PDEPD(DisasContext *ctx, arg_X *a)
+{
+ REQUIRE_64BIT(ctx);
+ REQUIRE_INSNS_FLAGS2(ctx, ISA310);
+#if defined(TARGET_PPC64)
+ gen_helper_PDEPD(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]);
+#else
+ qemu_build_not_reached();
+#endif
+ return true;
+}
+
+static bool trans_PEXTD(DisasContext *ctx, arg_X *a)
+{
+ REQUIRE_64BIT(ctx);
+ REQUIRE_INSNS_FLAGS2(ctx, ISA310);
+#if defined(TARGET_PPC64)
+ gen_helper_PEXTD(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]);
+#else
+ qemu_build_not_reached();
+#endif
+ return true;
+}