static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) { const char *opn = "loongson_cp2"; uint32_t opc, shift_max; TCGv_i64 t0, t1; opc = MASK_LMI(ctx->opcode); switch (opc) { case OPC_ADD_CP2: case OPC_SUB_CP2: case OPC_DADD_CP2: case OPC_DSUB_CP2: t0 = tcg_temp_local_new_i64(); t1 = tcg_temp_local_new_i64(); break; default: t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); break; } gen_load_fpr64(ctx, t0, rs); gen_load_fpr64(ctx, t1, rt); #define LMI_HELPER(UP, LO) \ case OPC_##UP: gen_helper_##LO(t0, t0, t1); opn = #LO; break #define LMI_HELPER_1(UP, LO) \ case OPC_##UP: gen_helper_##LO(t0, t0); opn = #LO; break #define LMI_DIRECT(UP, LO, OP) \ case OPC_##UP: tcg_gen_##OP##_i64(t0, t0, t1); opn = #LO; break switch (opc) { LMI_HELPER(PADDSH, paddsh); LMI_HELPER(PADDUSH, paddush); LMI_HELPER(PADDH, paddh); LMI_HELPER(PADDW, paddw); LMI_HELPER(PADDSB, paddsb); LMI_HELPER(PADDUSB, paddusb); LMI_HELPER(PADDB, paddb); LMI_HELPER(PSUBSH, psubsh); LMI_HELPER(PSUBUSH, psubush); LMI_HELPER(PSUBH, psubh); LMI_HELPER(PSUBW, psubw); LMI_HELPER(PSUBSB, psubsb); LMI_HELPER(PSUBUSB, psubusb); LMI_HELPER(PSUBB, psubb); LMI_HELPER(PSHUFH, pshufh); LMI_HELPER(PACKSSWH, packsswh); LMI_HELPER(PACKSSHB, packsshb); LMI_HELPER(PACKUSHB, packushb); LMI_HELPER(PUNPCKLHW, punpcklhw); LMI_HELPER(PUNPCKHHW, punpckhhw); LMI_HELPER(PUNPCKLBH, punpcklbh); LMI_HELPER(PUNPCKHBH, punpckhbh); LMI_HELPER(PUNPCKLWD, punpcklwd); LMI_HELPER(PUNPCKHWD, punpckhwd); LMI_HELPER(PAVGH, pavgh); LMI_HELPER(PAVGB, pavgb); LMI_HELPER(PMAXSH, pmaxsh); LMI_HELPER(PMINSH, pminsh); LMI_HELPER(PMAXUB, pmaxub); LMI_HELPER(PMINUB, pminub); LMI_HELPER(PCMPEQW, pcmpeqw); LMI_HELPER(PCMPGTW, pcmpgtw); LMI_HELPER(PCMPEQH, pcmpeqh); LMI_HELPER(PCMPGTH, pcmpgth); LMI_HELPER(PCMPEQB, pcmpeqb); LMI_HELPER(PCMPGTB, pcmpgtb); LMI_HELPER(PSLLW, psllw); LMI_HELPER(PSLLH, psllh); LMI_HELPER(PSRLW, psrlw); LMI_HELPER(PSRLH, psrlh); LMI_HELPER(PSRAW, psraw); LMI_HELPER(PSRAH, psrah); LMI_HELPER(PMULLH, pmullh); LMI_HELPER(PMULHH, pmulhh); LMI_HELPER(PMULHUH, pmulhuh); LMI_HELPER(PMADDHW, pmaddhw); LMI_HELPER(PASUBUB, pasubub); LMI_HELPER_1(BIADD, biadd); LMI_HELPER_1(PMOVMSKB, pmovmskb); LMI_DIRECT(PADDD, paddd, add); LMI_DIRECT(PSUBD, psubd, sub); LMI_DIRECT(XOR_CP2, xor, xor); LMI_DIRECT(NOR_CP2, nor, nor); LMI_DIRECT(AND_CP2, and, and); LMI_DIRECT(PANDN, pandn, andc); LMI_DIRECT(OR, or, or); case OPC_PINSRH_0: tcg_gen_deposit_i64(t0, t0, t1, 0, 16); opn = "pinsrh_0"; break; case OPC_PINSRH_1: tcg_gen_deposit_i64(t0, t0, t1, 16, 16); opn = "pinsrh_1"; break; case OPC_PINSRH_2: tcg_gen_deposit_i64(t0, t0, t1, 32, 16); opn = "pinsrh_2"; break; case OPC_PINSRH_3: tcg_gen_deposit_i64(t0, t0, t1, 48, 16); opn = "pinsrh_3"; break; case OPC_PEXTRH: tcg_gen_andi_i64(t1, t1, 3); tcg_gen_shli_i64(t1, t1, 4); tcg_gen_shr_i64(t0, t0, t1); tcg_gen_ext16u_i64(t0, t0); opn = "pextrh"; break; case OPC_ADDU_CP2: tcg_gen_add_i64(t0, t0, t1); tcg_gen_ext32s_i64(t0, t0); opn = "addu"; break; case OPC_SUBU_CP2: tcg_gen_sub_i64(t0, t0, t1); tcg_gen_ext32s_i64(t0, t0); opn = "addu"; break; case OPC_SLL_CP2: opn = "sll"; shift_max = 32; goto do_shift; case OPC_SRL_CP2: opn = "srl"; shift_max = 32; goto do_shift; case OPC_SRA_CP2: opn = "sra"; shift_max = 32; goto do_shift; case OPC_DSLL_CP2: opn = "dsll"; shift_max = 64; goto do_shift; case OPC_DSRL_CP2: opn = "dsrl"; shift_max = 64; goto do_shift; case OPC_DSRA_CP2: opn = "dsra"; shift_max = 64; goto do_shift; do_shift: /* Make sure shift count isn't TCG undefined behaviour. */ tcg_gen_andi_i64(t1, t1, shift_max - 1); switch (opc) { case OPC_SLL_CP2: case OPC_DSLL_CP2: tcg_gen_shl_i64(t0, t0, t1); break; case OPC_SRA_CP2: case OPC_DSRA_CP2: /* Since SRA is UndefinedResult without sign-extended inputs, we can treat SRA and DSRA the same. */ tcg_gen_sar_i64(t0, t0, t1); break; case OPC_SRL_CP2: /* We want to shift in zeros for SRL; zero-extend first. */ tcg_gen_ext32u_i64(t0, t0); /* FALLTHRU */ case OPC_DSRL_CP2: tcg_gen_shr_i64(t0, t0, t1); break; } if (shift_max == 32) { tcg_gen_ext32s_i64(t0, t0); } /* Shifts larger than MAX produce zero. */ tcg_gen_setcondi_i64(TCG_COND_LTU, t1, t1, shift_max); tcg_gen_neg_i64(t1, t1); tcg_gen_and_i64(t0, t0, t1); break; case OPC_ADD_CP2: case OPC_DADD_CP2: { TCGv_i64 t2 = tcg_temp_new_i64(); int lab = gen_new_label(); tcg_gen_mov_i64(t2, t0); tcg_gen_add_i64(t0, t1, t2); if (opc == OPC_ADD_CP2) { tcg_gen_ext32s_i64(t0, t0); } tcg_gen_xor_i64(t1, t1, t2); tcg_gen_xor_i64(t2, t2, t0); tcg_gen_andc_i64(t1, t2, t1); tcg_temp_free_i64(t2); tcg_gen_brcondi_i64(TCG_COND_GE, t1, 0, lab); generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(lab); opn = (opc == OPC_ADD_CP2 ? "add" : "dadd"); break; } case OPC_SUB_CP2: case OPC_DSUB_CP2: { TCGv_i64 t2 = tcg_temp_new_i64(); int lab = gen_new_label(); tcg_gen_mov_i64(t2, t0); tcg_gen_sub_i64(t0, t1, t2); if (opc == OPC_SUB_CP2) { tcg_gen_ext32s_i64(t0, t0); } tcg_gen_xor_i64(t1, t1, t2); tcg_gen_xor_i64(t2, t2, t0); tcg_gen_and_i64(t1, t1, t2); tcg_temp_free_i64(t2); tcg_gen_brcondi_i64(TCG_COND_GE, t1, 0, lab); generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(lab); opn = (opc == OPC_SUB_CP2 ? "sub" : "dsub"); break; } case OPC_PMULUW: tcg_gen_ext32u_i64(t0, t0); tcg_gen_ext32u_i64(t1, t1); tcg_gen_mul_i64(t0, t0, t1); opn = "pmuluw"; break; case OPC_SEQU_CP2: case OPC_SEQ_CP2: case OPC_SLTU_CP2: case OPC_SLT_CP2: case OPC_SLEU_CP2: case OPC_SLE_CP2: /* ??? Document is unclear: Set FCC[CC]. Does that mean the FD field is the CC field? */ default: MIPS_INVAL(opn); generate_exception(ctx, EXCP_RI); return; } #undef LMI_HELPER #undef LMI_DIRECT gen_store_fpr64(ctx, t0, rd); (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[rd], fregnames[rs], fregnames[rt]); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); }