target-ppc: Add ISA2.06 divdeu[o] Instructions
This patch adds the Divide Doubleword Extended Unsigned instructions. This instruction requires dividing a 128-bit value by a 64 bit value. Since 128 bit integer division is not supported in TCG, a helper is used. An architecture independent 128-bit division routine is added to host-utils. Signed-off-by: Tom Musta <tommusta@gmail.com> Reviewed-by: Richard Henderson <rth@twiddle.net> [agraf: use ||] Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
parent
a824bc191a
commit
98d1eb2748
@ -44,9 +44,23 @@ static inline void muls64(uint64_t *plow, uint64_t *phigh,
|
||||
*plow = r;
|
||||
*phigh = r >> 64;
|
||||
}
|
||||
|
||||
static inline int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor)
|
||||
{
|
||||
if (divisor == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
__uint128_t dividend = ((__uint128_t)*phigh << 64) | *plow;
|
||||
__uint128_t result = dividend / divisor;
|
||||
*plow = result;
|
||||
*phigh = dividend % divisor;
|
||||
return result > UINT64_MAX;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b);
|
||||
void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b);
|
||||
int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor);
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -31,6 +31,7 @@ DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32)
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
DEF_HELPER_3(mulldo, i64, env, i64, i64)
|
||||
DEF_HELPER_4(divdeu, i64, env, i64, i64, i32)
|
||||
#endif
|
||||
|
||||
DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
|
@ -41,6 +41,33 @@ uint64_t helper_mulldo(CPUPPCState *env, uint64_t arg1, uint64_t arg2)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
|
||||
uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe)
|
||||
{
|
||||
uint64_t rt = 0;
|
||||
int overflow = 0;
|
||||
|
||||
overflow = divu128(&rt, &ra, rb);
|
||||
|
||||
if (unlikely(overflow)) {
|
||||
rt = 0; /* Undefined */
|
||||
}
|
||||
|
||||
if (oe) {
|
||||
if (unlikely(overflow)) {
|
||||
env->so = env->ov = 1;
|
||||
} else {
|
||||
env->ov = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
target_ulong helper_cntlzw(target_ulong t)
|
||||
{
|
||||
return clz32(t);
|
||||
|
@ -984,6 +984,20 @@ GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1);
|
||||
/* divw divw. divwo divwo. */
|
||||
GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0);
|
||||
GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1);
|
||||
|
||||
/* div[wd]eu[o][.] */
|
||||
#define GEN_DIVE(name, hlpr, compute_ov) \
|
||||
static void gen_##name(DisasContext *ctx) \
|
||||
{ \
|
||||
TCGv_i32 t0 = tcg_const_i32(compute_ov); \
|
||||
gen_helper_##hlpr(cpu_gpr[rD(ctx->opcode)], cpu_env, \
|
||||
cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); \
|
||||
tcg_temp_free_i32(t0); \
|
||||
if (unlikely(Rc(ctx->opcode) != 0)) { \
|
||||
gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); \
|
||||
} \
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1,
|
||||
TCGv arg2, int sign, int compute_ov)
|
||||
@ -1032,6 +1046,10 @@ GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1);
|
||||
/* divw divw. divwo divwo. */
|
||||
GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0);
|
||||
GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1);
|
||||
|
||||
GEN_DIVE(divdeu, divdeu, 0);
|
||||
GEN_DIVE(divdeuo, divdeu, 1);
|
||||
|
||||
#endif
|
||||
|
||||
/* mulhw mulhw. */
|
||||
@ -9707,6 +9725,9 @@ GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1),
|
||||
GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0),
|
||||
GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1),
|
||||
|
||||
GEN_HANDLER_E(divdeu, 0x1F, 0x09, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206),
|
||||
GEN_HANDLER_E(divdeuo, 0x1F, 0x09, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206),
|
||||
|
||||
#undef GEN_INT_ARITH_MUL_HELPER
|
||||
#define GEN_INT_ARITH_MUL_HELPER(name, opc3) \
|
||||
GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B)
|
||||
|
@ -86,4 +86,42 @@ void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
|
||||
}
|
||||
*phigh = rh;
|
||||
}
|
||||
|
||||
/* Unsigned 128x64 division. Returns 1 if overflow (divide by zero or */
|
||||
/* quotient exceeds 64 bits). Otherwise returns quotient via plow and */
|
||||
/* remainder via phigh. */
|
||||
int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor)
|
||||
{
|
||||
uint64_t dhi = *phigh;
|
||||
uint64_t dlo = *plow;
|
||||
unsigned i;
|
||||
uint64_t carry = 0;
|
||||
|
||||
if (divisor == 0) {
|
||||
return 1;
|
||||
} else if (dhi == 0) {
|
||||
*plow = dlo / divisor;
|
||||
*phigh = dlo % divisor;
|
||||
return 0;
|
||||
} else if (dhi > divisor) {
|
||||
return 1;
|
||||
} else {
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
carry = dhi >> 63;
|
||||
dhi = (dhi << 1) | (dlo >> 63);
|
||||
if (carry || (dhi >= divisor)) {
|
||||
dhi -= divisor;
|
||||
carry = 1;
|
||||
} else {
|
||||
carry = 0;
|
||||
}
|
||||
dlo = (dlo << 1) | carry;
|
||||
}
|
||||
|
||||
*plow = dlo;
|
||||
*phigh = dhi;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif /* !CONFIG_INT128 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user