qemu/target/ppc/translate/fixedpoint-impl.c.inc
Víctor Colombo 670f1da374 target/ppc: Implement hashst and hashchk
Implementation for instructions hashst and hashchk, which were added
in Power ISA 3.1B.

It was decided to implement the hash algorithm from ground up in this
patch exactly as described in Power ISA.

Signed-off-by: Víctor Colombo <victor.colombo@eldorado.org.br>
Reviewed-by: Lucas Mateus Castro <lucas.araujo@eldorado.org.br>
Message-Id: <20220715205439.161110-3-victor.colombo@eldorado.org.br>
[danielhb: fix block comment in excp_helper.c]
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2022-09-20 10:54:06 -03:00

575 lines
16 KiB
PHP

/*
* 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)) {
/* lq and stq were privileged prior to V. 2.07 */
REQUIRE_SV(ctx);
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_UQ);
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_UQ)
TRANS64(LDX, do_ldst_X, false, false, MO_UQ)
TRANS64(LDU, do_ldst_D, true, false, MO_UQ)
TRANS64(LDUX, do_ldst_X, true, false, MO_UQ)
TRANS64(PLD, do_ldst_PLS_D, false, false, MO_UQ)
/* 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_UQ)
TRANS64(STDX, do_ldst_X, false, true, MO_UQ)
TRANS64(STDU, do_ldst_D, true, true, MO_UQ)
TRANS64(STDUX, do_ldst_X, true, true, MO_UQ)
TRANS64(PSTD, do_ldst_PLS_D, false, true, MO_UQ)
/* 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;
}
static bool trans_ADDG6S(DisasContext *ctx, arg_X *a)
{
const uint64_t carry_bits = 0x1111111111111111ULL;
TCGv t0, t1, carry, zero = tcg_constant_tl(0);
REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206);
t0 = tcg_temp_new();
t1 = tcg_const_tl(0);
carry = tcg_const_tl(0);
for (int i = 0; i < 16; i++) {
tcg_gen_shri_tl(t0, cpu_gpr[a->ra], i * 4);
tcg_gen_andi_tl(t0, t0, 0xf);
tcg_gen_add_tl(t1, t1, t0);
tcg_gen_shri_tl(t0, cpu_gpr[a->rb], i * 4);
tcg_gen_andi_tl(t0, t0, 0xf);
tcg_gen_add_tl(t1, t1, t0);
tcg_gen_andi_tl(t1, t1, 0x10);
tcg_gen_setcond_tl(TCG_COND_NE, t1, t1, zero);
tcg_gen_shli_tl(t0, t1, i * 4);
tcg_gen_or_tl(carry, carry, t0);
}
tcg_gen_xori_tl(carry, carry, (target_long)carry_bits);
tcg_gen_muli_tl(cpu_gpr[a->rt], carry, 6);
tcg_temp_free(t0);
tcg_temp_free(t1);
tcg_temp_free(carry);
return true;
}
static bool trans_CDTBCD(DisasContext *ctx, arg_X_sa *a)
{
REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206);
gen_helper_CDTBCD(cpu_gpr[a->ra], cpu_gpr[a->rs]);
return true;
}
static bool trans_CBCDTD(DisasContext *ctx, arg_X_sa *a)
{
REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206);
gen_helper_CBCDTD(cpu_gpr[a->ra], cpu_gpr[a->rs]);
return true;
}
static bool do_hash(DisasContext *ctx, arg_X *a, bool priv,
void (*helper)(TCGv_ptr, TCGv, TCGv, TCGv))
{
TCGv ea;
if (!(ctx->insns_flags2 & PPC2_ISA310)) {
/* if version is before v3.1, this operation is a nop */
return true;
}
if (priv) {
/* if instruction is privileged but the context is in user space */
REQUIRE_SV(ctx);
}
if (unlikely(a->ra == 0)) {
/* if RA=0, the instruction form is invalid */
gen_invalid(ctx);
return true;
}
ea = do_ea_calc(ctx, a->ra, tcg_constant_tl(a->rt));
helper(cpu_env, ea, cpu_gpr[a->ra], cpu_gpr[a->rb]);
tcg_temp_free(ea);
return true;
}
TRANS(HASHST, do_hash, false, gen_helper_HASHST)
TRANS(HASHCHK, do_hash, false, gen_helper_HASHCHK)