@@ -22,92 +22,99 @@
static bool trans_mul(DisasContext *ctx, arg_mul *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_MUL, a->rd, a->rs1, a->rs2);
- return true;
+ return trans_arith(ctx, a, &tcg_gen_mul_tl);
}
static bool trans_mulh(DisasContext *ctx, arg_mulh *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_MULH, a->rd, a->rs1, a->rs2);
+ TCGv source1 = tcg_temp_new();
+ TCGv source2 = tcg_temp_new();
+ gen_get_gpr(source1, a->rs1);
+ gen_get_gpr(source2, a->rs2);
+
+ tcg_gen_muls2_tl(source2, source1, source1, source2);
+
+ gen_set_gpr(a->rd, source1);
+ tcg_temp_free(source1);
+ tcg_temp_free(source2);
return true;
}
static bool trans_mulhsu(DisasContext *ctx, arg_mulhsu *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_MULHSU, a->rd, a->rs1, a->rs2);
- return true;
+ return trans_arith(ctx, a, &gen_mulhsu);
}
static bool trans_mulhu(DisasContext *ctx, arg_mulhu *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_MULHU, a->rd, a->rs1, a->rs2);
+ TCGv source1 = tcg_temp_new();
+ TCGv source2 = tcg_temp_new();
+ gen_get_gpr(source1, a->rs1);
+ gen_get_gpr(source2, a->rs2);
+
+ tcg_gen_mulu2_tl(source2, source1, source1, source2);
+
+ gen_set_gpr(a->rd, source1);
+ tcg_temp_free(source1);
+ tcg_temp_free(source2);
return true;
}
static bool trans_div(DisasContext *ctx, arg_div *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_DIV, a->rd, a->rs1, a->rs2);
- return true;
+ return trans_arith(ctx, a, &gen_div);
}
static bool trans_divu(DisasContext *ctx, arg_divu *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_DIVU, a->rd, a->rs1, a->rs2);
- return true;
+ return trans_arith(ctx, a, &gen_divu);
}
static bool trans_rem(DisasContext *ctx, arg_rem *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_REM, a->rd, a->rs1, a->rs2);
- return true;
+ return trans_arith(ctx, a, &gen_rem);
}
static bool trans_remu(DisasContext *ctx, arg_remu *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_REMU, a->rd, a->rs1, a->rs2);
- return true;
+ return trans_arith(ctx, a, &gen_remu);
}
#ifdef TARGET_RISCV64
static bool trans_mulw(DisasContext *ctx, arg_mulw *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_MULW, a->rd, a->rs1, a->rs2);
- return true;
+ return trans_arith(ctx, a, &gen_mulw);
}
static bool trans_divw(DisasContext *ctx, arg_divw *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_DIVW, a->rd, a->rs1, a->rs2);
- return true;
+ return gen_arith_div_w(ctx, a, &gen_div);
}
static bool trans_divuw(DisasContext *ctx, arg_divuw *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_DIVUW, a->rd, a->rs1, a->rs2);
- return true;
+ return gen_arith_div_w(ctx, a, &gen_divu);
}
static bool trans_remw(DisasContext *ctx, arg_remw *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_REMW, a->rd, a->rs1, a->rs2);
- return true;
+ return gen_arith_div_w(ctx, a, &gen_rem);
}
static bool trans_remuw(DisasContext *ctx, arg_remuw *a)
{
REQUIRE_EXT(ctx, RVM);
- gen_arith(ctx, OPC_RISC_REMUW, a->rd, a->rs1, a->rs2);
- return true;
+ return gen_arith_div_w(ctx, a, &gen_remu);
}
#endif
@@ -188,193 +188,112 @@ static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2)
tcg_temp_free(rh);
}
-static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
- int rs2)
-{
- TCGv source1, source2, cond1, cond2, zeroreg, resultopt1;
- source1 = tcg_temp_new();
- source2 = tcg_temp_new();
- gen_get_gpr(source1, rs1);
- gen_get_gpr(source2, rs2);
-
- switch (opc) {
- CASE_OP_32_64(OPC_RISC_MUL):
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- tcg_gen_mul_tl(source1, source1, source2);
- break;
- case OPC_RISC_MULH:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- tcg_gen_muls2_tl(source2, source1, source1, source2);
- break;
- case OPC_RISC_MULHSU:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- gen_mulhsu(source1, source1, source2);
- break;
- case OPC_RISC_MULHU:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- tcg_gen_mulu2_tl(source2, source1, source1, source2);
- break;
-#if defined(TARGET_RISCV64)
- case OPC_RISC_DIVW:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- tcg_gen_ext32s_tl(source1, source1);
- tcg_gen_ext32s_tl(source2, source2);
- /* fall through to DIV */
-#endif
- case OPC_RISC_DIV:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- /* Handle by altering args to tcg_gen_div to produce req'd results:
- * For overflow: want source1 in source1 and 1 in source2
- * For div by zero: want -1 in source1 and 1 in source2 -> -1 result */
- cond1 = tcg_temp_new();
- cond2 = tcg_temp_new();
- zeroreg = tcg_const_tl(0);
- resultopt1 = tcg_temp_new();
-
- tcg_gen_movi_tl(resultopt1, (target_ulong)-1);
- tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)(~0L));
- tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1,
- ((target_ulong)1) << (TARGET_LONG_BITS - 1));
- tcg_gen_and_tl(cond1, cond1, cond2); /* cond1 = overflow */
- tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, 0); /* cond2 = div 0 */
- /* if div by zero, set source1 to -1, otherwise don't change */
- tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond2, zeroreg, source1,
- resultopt1);
- /* if overflow or div by zero, set source2 to 1, else don't change */
- tcg_gen_or_tl(cond1, cond1, cond2);
- tcg_gen_movi_tl(resultopt1, (target_ulong)1);
- tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
- resultopt1);
- tcg_gen_div_tl(source1, source1, source2);
-
- tcg_temp_free(cond1);
- tcg_temp_free(cond2);
- tcg_temp_free(zeroreg);
- tcg_temp_free(resultopt1);
- break;
-#if defined(TARGET_RISCV64)
- case OPC_RISC_DIVUW:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- tcg_gen_ext32u_tl(source1, source1);
- tcg_gen_ext32u_tl(source2, source2);
- /* fall through to DIVU */
-#endif
- case OPC_RISC_DIVU:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- cond1 = tcg_temp_new();
- zeroreg = tcg_const_tl(0);
- resultopt1 = tcg_temp_new();
-
- tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0);
- tcg_gen_movi_tl(resultopt1, (target_ulong)-1);
- tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, source1,
- resultopt1);
- tcg_gen_movi_tl(resultopt1, (target_ulong)1);
- tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
- resultopt1);
- tcg_gen_divu_tl(source1, source1, source2);
-
- tcg_temp_free(cond1);
- tcg_temp_free(zeroreg);
- tcg_temp_free(resultopt1);
- break;
-#if defined(TARGET_RISCV64)
- case OPC_RISC_REMW:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- tcg_gen_ext32s_tl(source1, source1);
- tcg_gen_ext32s_tl(source2, source2);
- /* fall through to REM */
-#endif
- case OPC_RISC_REM:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- cond1 = tcg_temp_new();
- cond2 = tcg_temp_new();
- zeroreg = tcg_const_tl(0);
- resultopt1 = tcg_temp_new();
-
- tcg_gen_movi_tl(resultopt1, 1L);
- tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)-1);
- tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1,
- (target_ulong)1 << (TARGET_LONG_BITS - 1));
- tcg_gen_and_tl(cond2, cond1, cond2); /* cond1 = overflow */
- tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); /* cond2 = div 0 */
- /* if overflow or div by zero, set source2 to 1, else don't change */
- tcg_gen_or_tl(cond2, cond1, cond2);
- tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond2, zeroreg, source2,
- resultopt1);
- tcg_gen_rem_tl(resultopt1, source1, source2);
- /* if div by zero, just return the original dividend */
- tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1,
- source1);
-
- tcg_temp_free(cond1);
- tcg_temp_free(cond2);
- tcg_temp_free(zeroreg);
- tcg_temp_free(resultopt1);
- break;
-#if defined(TARGET_RISCV64)
- case OPC_RISC_REMUW:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- tcg_gen_ext32u_tl(source1, source1);
- tcg_gen_ext32u_tl(source2, source2);
- /* fall through to REMU */
-#endif
- case OPC_RISC_REMU:
- if (!has_ext(ctx, RVM)) {
- goto do_illegal;
- }
- cond1 = tcg_temp_new();
- zeroreg = tcg_const_tl(0);
- resultopt1 = tcg_temp_new();
-
- tcg_gen_movi_tl(resultopt1, (target_ulong)1);
- tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0);
- tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
- resultopt1);
- tcg_gen_remu_tl(resultopt1, source1, source2);
- /* if div by zero, just return the original dividend */
- tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1,
- source1);
-
- tcg_temp_free(cond1);
- tcg_temp_free(zeroreg);
- tcg_temp_free(resultopt1);
- break;
- do_illegal:
- default:
- gen_exception_illegal(ctx);
- return;
- }
-
- if (opc & 0x8) { /* sign extend for W instructions */
- tcg_gen_ext32s_tl(source1, source1);
- }
-
- gen_set_gpr(rd, source1);
- tcg_temp_free(source1);
- tcg_temp_free(source2);
+static void gen_div(TCGv ret, TCGv source1, TCGv source2)
+{
+ TCGv cond1, cond2, zeroreg, resultopt1;
+ /*
+ * Handle by altering args to tcg_gen_div to produce req'd results:
+ * For overflow: want source1 in source1 and 1 in source2
+ * For div by zero: want -1 in source1 and 1 in source2 -> -1 result
+ */
+ cond1 = tcg_temp_new();
+ cond2 = tcg_temp_new();
+ zeroreg = tcg_const_tl(0);
+ resultopt1 = tcg_temp_new();
+
+ tcg_gen_movi_tl(resultopt1, (target_ulong)-1);
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)(~0L));
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1,
+ ((target_ulong)1) << (TARGET_LONG_BITS - 1));
+ tcg_gen_and_tl(cond1, cond1, cond2); /* cond1 = overflow */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, 0); /* cond2 = div 0 */
+ /* if div by zero, set source1 to -1, otherwise don't change */
+ tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond2, zeroreg, source1,
+ resultopt1);
+ /* if overflow or div by zero, set source2 to 1, else don't change */
+ tcg_gen_or_tl(cond1, cond1, cond2);
+ tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+ tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+ resultopt1);
+ tcg_gen_div_tl(ret, source1, source2);
+
+ tcg_temp_free(cond1);
+ tcg_temp_free(cond2);
+ tcg_temp_free(zeroreg);
+ tcg_temp_free(resultopt1);
+}
+
+static void gen_divu(TCGv ret, TCGv source1, TCGv source2)
+{
+ TCGv cond1, zeroreg, resultopt1;
+ cond1 = tcg_temp_new();
+
+ zeroreg = tcg_const_tl(0);
+ resultopt1 = tcg_temp_new();
+
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0);
+ tcg_gen_movi_tl(resultopt1, (target_ulong)-1);
+ tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, source1,
+ resultopt1);
+ tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+ tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+ resultopt1);
+ tcg_gen_divu_tl(ret, source1, source2);
+
+ tcg_temp_free(cond1);
+ tcg_temp_free(zeroreg);
+ tcg_temp_free(resultopt1);
+}
+
+static void gen_rem(TCGv ret, TCGv source1, TCGv source2)
+{
+ TCGv cond1, cond2, zeroreg, resultopt1;
+
+ cond1 = tcg_temp_new();
+ cond2 = tcg_temp_new();
+ zeroreg = tcg_const_tl(0);
+ resultopt1 = tcg_temp_new();
+
+ tcg_gen_movi_tl(resultopt1, 1L);
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)-1);
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1,
+ (target_ulong)1 << (TARGET_LONG_BITS - 1));
+ tcg_gen_and_tl(cond2, cond1, cond2); /* cond1 = overflow */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); /* cond2 = div 0 */
+ /* if overflow or div by zero, set source2 to 1, else don't change */
+ tcg_gen_or_tl(cond2, cond1, cond2);
+ tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond2, zeroreg, source2,
+ resultopt1);
+ tcg_gen_rem_tl(resultopt1, source1, source2);
+ /* if div by zero, just return the original dividend */
+ tcg_gen_movcond_tl(TCG_COND_EQ, ret, cond1, zeroreg, resultopt1,
+ source1);
+
+ tcg_temp_free(cond1);
+ tcg_temp_free(cond2);
+ tcg_temp_free(zeroreg);
+ tcg_temp_free(resultopt1);
+}
+
+static void gen_remu(TCGv ret, TCGv source1, TCGv source2)
+{
+ TCGv cond1, zeroreg, resultopt1;
+ cond1 = tcg_temp_new();
+ zeroreg = tcg_const_tl(0);
+ resultopt1 = tcg_temp_new();
+
+ tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0);
+ tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+ resultopt1);
+ tcg_gen_remu_tl(resultopt1, source1, source2);
+ /* if div by zero, just return the original dividend */
+ tcg_gen_movcond_tl(TCG_COND_EQ, ret, cond1, zeroreg, resultopt1,
+ source1);
+
+ tcg_temp_free(cond1);
+ tcg_temp_free(zeroreg);
+ tcg_temp_free(resultopt1);
}
static void gen_jal(DisasContext *ctx, int rd, target_ulong imm)
@@ -681,6 +600,33 @@ static void gen_subw(TCGv ret, TCGv arg1, TCGv arg2)
tcg_gen_ext32s_tl(ret, ret);
}
+static void gen_mulw(TCGv ret, TCGv arg1, TCGv arg2)
+{
+ tcg_gen_mul_tl(ret, arg1, arg2);
+ tcg_gen_ext32s_tl(ret, ret);
+}
+
+static bool gen_arith_div_w(DisasContext *ctx, arg_r *a,
+ void(*func)(TCGv, TCGv, TCGv))
+{
+ TCGv source1, source2;
+ source1 = tcg_temp_new();
+ source2 = tcg_temp_new();
+
+ gen_get_gpr(source1, a->rs1);
+ gen_get_gpr(source2, a->rs2);
+ tcg_gen_ext32s_tl(source1, source1);
+ tcg_gen_ext32s_tl(source2, source2);
+
+ (*func)(source1, source1, source2);
+
+ tcg_gen_ext32s_tl(source1, source1);
+ gen_set_gpr(a->rd, source1);
+ tcg_temp_free(source1);
+ tcg_temp_free(source2);
+ return true;
+}
+
#endif
static bool trans_arith(DisasContext *ctx, arg_r *a,