target/riscv: Zero extend the inputs of divuw and remuw

While running the GCC test suite against 4.0.0-rc0, Kito found a
regression introduced by the decodetree conversion that caused divuw and
remuw to sign-extend their inputs. The ISA manual says they are
supposed to be zero extended:

DIVW and DIVUW instructions are only valid for RV64, and divide the
lower 32 bits of rs1 by the lower 32 bits of rs2, treating them as
signed and unsigned integers respectively, placing the 32-bit
quotient in rd, sign-extended to 64 bits. REMW and REMUW
instructions are only valid for RV64, and provide the corresponding
signed and unsigned remainder operations respectively. Both REMW
and REMUW always sign-extend the 32-bit result to 64 bits, including
on a divide by zero.

Here's Kito's reduced test case from the GCC test suite

unsigned calc_mp(unsigned mod)
{
unsigned a,b,c;
c=-1;
a=c/mod;
b=0-a*mod;
if (b > mod) { a += 1; b-=mod; }
return b;
}

int main(int argc, char *argv[])
{
unsigned x = 1234;
unsigned y = calc_mp(x);

if ((sizeof (y) == 4 && y != 680)
|| (sizeof (y) == 2 && y != 134))
abort ();
exit (0);
}

I haven't done any other testing on this, but it does fix the test case.

Backports commit f17e02cd3731bdfe2942d1d0b2a92f26da02408c from qemu
This commit is contained in:
Palmer Dabbelt 2019-03-26 20:38:08 -04:00 committed by Lioncash
parent 8719b3edb3
commit fc662c281a
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
2 changed files with 25 additions and 2 deletions

View file

@ -106,7 +106,7 @@ static bool trans_divw(DisasContext *ctx, arg_divw *a)
static bool trans_divuw(DisasContext *ctx, arg_divuw *a) static bool trans_divuw(DisasContext *ctx, arg_divuw *a)
{ {
REQUIRE_EXT(ctx, RVM); REQUIRE_EXT(ctx, RVM);
return gen_arith_div_w(ctx, a, &gen_divu); return gen_arith_div_uw(ctx, a, &gen_divu);
} }
static bool trans_remw(DisasContext *ctx, arg_remw *a) static bool trans_remw(DisasContext *ctx, arg_remw *a)
@ -118,6 +118,6 @@ static bool trans_remw(DisasContext *ctx, arg_remw *a)
static bool trans_remuw(DisasContext *ctx, arg_remuw *a) static bool trans_remuw(DisasContext *ctx, arg_remuw *a)
{ {
REQUIRE_EXT(ctx, RVM); REQUIRE_EXT(ctx, RVM);
return gen_arith_div_w(ctx, a, &gen_remu); return gen_arith_div_uw(ctx, a, &gen_remu);
} }
#endif #endif

View file

@ -620,6 +620,29 @@ static bool gen_arith_div_w(DisasContext *ctx, arg_r *a,
tcg_temp_free(tcg_ctx, source2); tcg_temp_free(tcg_ctx, source2);
return true; return true;
} }
static bool gen_arith_div_uw(DisasContext *ctx, arg_r *a,
void(*func)(TCGContext *, TCGv, TCGv, TCGv))
{
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
TCGv source1, source2;
source1 = tcg_temp_new(tcg_ctx);
source2 = tcg_temp_new(tcg_ctx);
gen_get_gpr(ctx, source1, a->rs1);
gen_get_gpr(ctx, source2, a->rs2);
tcg_gen_ext32u_tl(tcg_ctx, source1, source1);
tcg_gen_ext32u_tl(tcg_ctx, source2, source2);
(*func)(tcg_ctx, source1, source1, source2);
tcg_gen_ext32s_tl(tcg_ctx, source1, source1);
gen_set_gpr(ctx, a->rd, source1);
tcg_temp_free(tcg_ctx, source1);
tcg_temp_free(tcg_ctx, source2);
return true;
}
#endif #endif
static bool gen_arith(DisasContext *ctx, arg_r *a, static bool gen_arith(DisasContext *ctx, arg_r *a,