target-alpha: Implement IEEE FP qualifiers.

IEEE FP instructions are split up so that the rounding mode
coming from the instruction and exceptions (both masking and
delivery) are handled external to the base FP operation.
FP exceptions are properly raised for non-finite inputs to
instructions that do not indicate software completion.

A shortcut is applied if CONFIG_SOFTFLOAT_INLINE is defined
at the top of translate.c: data is loaded and stored into
FP_STATUS directly instead of using the functional interface
defined by "softfloat.h".

Signed-off-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
Richard Henderson 2010-01-04 14:27:23 -08:00 committed by Aurelien Jarno
parent 6c71232122
commit f24518b502
3 changed files with 687 additions and 114 deletions

View file

@ -33,6 +33,7 @@
#include "helper.h"
#undef ALPHA_DEBUG_DISAS
#define CONFIG_SOFTFLOAT_INLINE
#ifdef ALPHA_DEBUG_DISAS
# define LOG_DISAS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
@ -49,6 +50,11 @@ struct DisasContext {
#endif
CPUAlphaState *env;
uint32_t amask;
/* Current rounding mode for this TB. */
int tb_rm;
/* Current flush-to-zero setting for this TB. */
int tb_ftz;
};
/* global register indexes */
@ -442,62 +448,333 @@ static void gen_fcmov(TCGCond inv_cond, int ra, int rb, int rc)
gen_set_label(l1);
}
#define FARITH2(name) \
static inline void glue(gen_f, name)(int rb, int rc) \
{ \
if (unlikely(rc == 31)) \
return; \
\
if (rb != 31) \
gen_helper_ ## name (cpu_fir[rc], cpu_fir[rb]); \
else { \
TCGv tmp = tcg_const_i64(0); \
gen_helper_ ## name (cpu_fir[rc], tmp); \
tcg_temp_free(tmp); \
} \
#define QUAL_RM_N 0x080 /* Round mode nearest even */
#define QUAL_RM_C 0x000 /* Round mode chopped */
#define QUAL_RM_M 0x040 /* Round mode minus infinity */
#define QUAL_RM_D 0x0c0 /* Round mode dynamic */
#define QUAL_RM_MASK 0x0c0
#define QUAL_U 0x100 /* Underflow enable (fp output) */
#define QUAL_V 0x100 /* Overflow enable (int output) */
#define QUAL_S 0x400 /* Software completion enable */
#define QUAL_I 0x200 /* Inexact detection enable */
static void gen_qual_roundmode(DisasContext *ctx, int fn11)
{
TCGv_i32 tmp;
fn11 &= QUAL_RM_MASK;
if (fn11 == ctx->tb_rm) {
return;
}
ctx->tb_rm = fn11;
tmp = tcg_temp_new_i32();
switch (fn11) {
case QUAL_RM_N:
tcg_gen_movi_i32(tmp, float_round_nearest_even);
break;
case QUAL_RM_C:
tcg_gen_movi_i32(tmp, float_round_to_zero);
break;
case QUAL_RM_M:
tcg_gen_movi_i32(tmp, float_round_down);
break;
case QUAL_RM_D:
tcg_gen_ld8u_i32(tmp, cpu_env, offsetof(CPUState, fpcr_dyn_round));
break;
}
#if defined(CONFIG_SOFTFLOAT_INLINE)
/* ??? The "softfloat.h" interface is to call set_float_rounding_mode.
With CONFIG_SOFTFLOAT that expands to an out-of-line call that just
sets the one field. */
tcg_gen_st8_i32(tmp, cpu_env,
offsetof(CPUState, fp_status.float_rounding_mode));
#else
gen_helper_setroundmode(tmp);
#endif
tcg_temp_free_i32(tmp);
}
FARITH2(sqrts)
static void gen_qual_flushzero(DisasContext *ctx, int fn11)
{
TCGv_i32 tmp;
fn11 &= QUAL_U;
if (fn11 == ctx->tb_ftz) {
return;
}
ctx->tb_ftz = fn11;
tmp = tcg_temp_new_i32();
if (fn11) {
/* Underflow is enabled, use the FPCR setting. */
tcg_gen_ld8u_i32(tmp, cpu_env, offsetof(CPUState, fpcr_flush_to_zero));
} else {
/* Underflow is disabled, force flush-to-zero. */
tcg_gen_movi_i32(tmp, 1);
}
#if defined(CONFIG_SOFTFLOAT_INLINE)
tcg_gen_st8_i32(tmp, cpu_env,
offsetof(CPUState, fp_status.flush_to_zero));
#else
gen_helper_setflushzero(tmp);
#endif
tcg_temp_free_i32(tmp);
}
static TCGv gen_ieee_input(int reg, int fn11, int is_cmp)
{
TCGv val = tcg_temp_new();
if (reg == 31) {
tcg_gen_movi_i64(val, 0);
} else if (fn11 & QUAL_S) {
gen_helper_ieee_input_s(val, cpu_fir[reg]);
} else if (is_cmp) {
gen_helper_ieee_input_cmp(val, cpu_fir[reg]);
} else {
gen_helper_ieee_input(val, cpu_fir[reg]);
}
return val;
}
static void gen_fp_exc_clear(void)
{
#if defined(CONFIG_SOFTFLOAT_INLINE)
TCGv_i32 zero = tcg_const_i32(0);
tcg_gen_st8_i32(zero, cpu_env,
offsetof(CPUState, fp_status.float_exception_flags));
tcg_temp_free_i32(zero);
#else
gen_helper_fp_exc_clear();
#endif
}
static void gen_fp_exc_raise_ignore(int rc, int fn11, int ignore)
{
/* ??? We ought to be able to do something with imprecise exceptions.
E.g. notice we're still in the trap shadow of something within the
TB and do not generate the code to signal the exception; end the TB
when an exception is forced to arrive, either by consumption of a
register value or TRAPB or EXCB. */
TCGv_i32 exc = tcg_temp_new_i32();
TCGv_i32 reg;
#if defined(CONFIG_SOFTFLOAT_INLINE)
tcg_gen_ld8u_i32(exc, cpu_env,
offsetof(CPUState, fp_status.float_exception_flags));
#else
gen_helper_fp_exc_get(exc);
#endif
if (ignore) {
tcg_gen_andi_i32(exc, exc, ~ignore);
}
/* ??? Pass in the regno of the destination so that the helper can
set EXC_MASK, which contains a bitmask of destination registers
that have caused arithmetic traps. A simple userspace emulation
does not require this. We do need it for a guest kernel's entArith,
or if we were to do something clever with imprecise exceptions. */
reg = tcg_const_i32(rc + 32);
if (fn11 & QUAL_S) {
gen_helper_fp_exc_raise_s(exc, reg);
} else {
gen_helper_fp_exc_raise(exc, reg);
}
tcg_temp_free_i32(reg);
tcg_temp_free_i32(exc);
}
static inline void gen_fp_exc_raise(int rc, int fn11)
{
gen_fp_exc_raise_ignore(rc, fn11, fn11 & QUAL_I ? 0 : float_flag_inexact);
}
#define FARITH2(name) \
static inline void glue(gen_f, name)(int rb, int rc) \
{ \
if (unlikely(rc == 31)) { \
return; \
} \
if (rb != 31) { \
gen_helper_ ## name (cpu_fir[rc], cpu_fir[rb]); \
} else { \
TCGv tmp = tcg_const_i64(0); \
gen_helper_ ## name (cpu_fir[rc], tmp); \
tcg_temp_free(tmp); \
} \
}
FARITH2(cvtlq)
FARITH2(cvtql)
FARITH2(cvtql_v)
FARITH2(cvtql_sv)
/* ??? VAX instruction qualifiers ignored. */
FARITH2(sqrtf)
FARITH2(sqrtg)
FARITH2(sqrtt)
FARITH2(cvtgf)
FARITH2(cvtgq)
FARITH2(cvtqf)
FARITH2(cvtqg)
FARITH2(cvtst)
FARITH2(cvtts)
FARITH2(cvttq)
FARITH2(cvtqs)
FARITH2(cvtqt)
FARITH2(cvtlq)
FARITH2(cvtql)
FARITH2(cvtqlv)
FARITH2(cvtqlsv)
#define FARITH3(name) \
static inline void glue(gen_f, name)(int ra, int rb, int rc) \
{ \
if (unlikely(rc == 31)) \
return; \
\
if (ra != 31) { \
if (rb != 31) \
gen_helper_ ## name (cpu_fir[rc], cpu_fir[ra], cpu_fir[rb]); \
else { \
TCGv tmp = tcg_const_i64(0); \
gen_helper_ ## name (cpu_fir[rc], cpu_fir[ra], tmp); \
tcg_temp_free(tmp); \
} \
} else { \
TCGv tmp = tcg_const_i64(0); \
if (rb != 31) \
gen_helper_ ## name (cpu_fir[rc], tmp, cpu_fir[rb]); \
else \
gen_helper_ ## name (cpu_fir[rc], tmp, tmp); \
tcg_temp_free(tmp); \
} \
static void gen_ieee_arith2(DisasContext *ctx, void (*helper)(TCGv, TCGv),
int rb, int rc, int fn11)
{
TCGv vb;
/* ??? This is wrong: the instruction is not a nop, it still may
raise exceptions. */
if (unlikely(rc == 31)) {
return;
}
gen_qual_roundmode(ctx, fn11);
gen_qual_flushzero(ctx, fn11);
gen_fp_exc_clear();
vb = gen_ieee_input(rb, fn11, 0);
helper(cpu_fir[rc], vb);
tcg_temp_free(vb);
gen_fp_exc_raise(rc, fn11);
}
#define IEEE_ARITH2(name) \
static inline void glue(gen_f, name)(DisasContext *ctx, \
int rb, int rc, int fn11) \
{ \
gen_ieee_arith2(ctx, gen_helper_##name, rb, rc, fn11); \
}
IEEE_ARITH2(sqrts)
IEEE_ARITH2(sqrtt)
IEEE_ARITH2(cvtst)
IEEE_ARITH2(cvtts)
static void gen_fcvttq(DisasContext *ctx, int rb, int rc, int fn11)
{
TCGv vb;
int ignore = 0;
/* ??? This is wrong: the instruction is not a nop, it still may
raise exceptions. */
if (unlikely(rc == 31)) {
return;
}
/* No need to set flushzero, since we have an integer output. */
gen_fp_exc_clear();
vb = gen_ieee_input(rb, fn11, 0);
/* Almost all integer conversions use cropped rounding, and most
also do not have integer overflow enabled. Special case that. */
switch (fn11) {
case QUAL_RM_C:
gen_helper_cvttq_c(cpu_fir[rc], vb);
break;
case QUAL_V | QUAL_RM_C:
case QUAL_S | QUAL_V | QUAL_RM_C:
ignore = float_flag_inexact;
/* FALLTHRU */
case QUAL_S | QUAL_V | QUAL_I | QUAL_RM_C:
gen_helper_cvttq_svic(cpu_fir[rc], vb);
break;
default:
gen_qual_roundmode(ctx, fn11);
gen_helper_cvttq(cpu_fir[rc], vb);
ignore |= (fn11 & QUAL_V ? 0 : float_flag_overflow);
ignore |= (fn11 & QUAL_I ? 0 : float_flag_inexact);
break;
}
tcg_temp_free(vb);
gen_fp_exc_raise_ignore(rc, fn11, ignore);
}
static void gen_ieee_intcvt(DisasContext *ctx, void (*helper)(TCGv, TCGv),
int rb, int rc, int fn11)
{
TCGv vb;
/* ??? This is wrong: the instruction is not a nop, it still may
raise exceptions. */
if (unlikely(rc == 31)) {
return;
}
gen_qual_roundmode(ctx, fn11);
if (rb == 31) {
vb = tcg_const_i64(0);
} else {
vb = cpu_fir[rb];
}
/* The only exception that can be raised by integer conversion
is inexact. Thus we only need to worry about exceptions when
inexact handling is requested. */
if (fn11 & QUAL_I) {
gen_fp_exc_clear();
helper(cpu_fir[rc], vb);
gen_fp_exc_raise(rc, fn11);
} else {
helper(cpu_fir[rc], vb);
}
if (rb == 31) {
tcg_temp_free(vb);
}
}
#define IEEE_INTCVT(name) \
static inline void glue(gen_f, name)(DisasContext *ctx, \
int rb, int rc, int fn11) \
{ \
gen_ieee_intcvt(ctx, gen_helper_##name, rb, rc, fn11); \
}
IEEE_INTCVT(cvtqs)
IEEE_INTCVT(cvtqt)
#define FARITH3(name) \
static inline void glue(gen_f, name)(int ra, int rb, int rc) \
{ \
TCGv va, vb; \
\
if (unlikely(rc == 31)) { \
return; \
} \
if (ra == 31) { \
va = tcg_const_i64(0); \
} else { \
va = cpu_fir[ra]; \
} \
if (rb == 31) { \
vb = tcg_const_i64(0); \
} else { \
vb = cpu_fir[rb]; \
} \
\
gen_helper_ ## name (cpu_fir[rc], va, vb); \
\
if (ra == 31) { \
tcg_temp_free(va); \
} \
if (rb == 31) { \
tcg_temp_free(vb); \
} \
}
/* ??? Ought to expand these inline; simple masking operations. */
FARITH3(cpys)
FARITH3(cpysn)
FARITH3(cpyse)
/* ??? VAX instruction qualifiers ignored. */
FARITH3(addf)
FARITH3(subf)
FARITH3(mulf)
@ -509,21 +786,80 @@ FARITH3(divg)
FARITH3(cmpgeq)
FARITH3(cmpglt)
FARITH3(cmpgle)
FARITH3(adds)
FARITH3(subs)
FARITH3(muls)
FARITH3(divs)
FARITH3(addt)
FARITH3(subt)
FARITH3(mult)
FARITH3(divt)
FARITH3(cmptun)
FARITH3(cmpteq)
FARITH3(cmptlt)
FARITH3(cmptle)
FARITH3(cpys)
FARITH3(cpysn)
FARITH3(cpyse)
static void gen_ieee_arith3(DisasContext *ctx,
void (*helper)(TCGv, TCGv, TCGv),
int ra, int rb, int rc, int fn11)
{
TCGv va, vb;
/* ??? This is wrong: the instruction is not a nop, it still may
raise exceptions. */
if (unlikely(rc == 31)) {
return;
}
gen_qual_roundmode(ctx, fn11);
gen_qual_flushzero(ctx, fn11);
gen_fp_exc_clear();
va = gen_ieee_input(ra, fn11, 0);
vb = gen_ieee_input(rb, fn11, 0);
helper(cpu_fir[rc], va, vb);
tcg_temp_free(va);
tcg_temp_free(vb);
gen_fp_exc_raise(rc, fn11);
}
#define IEEE_ARITH3(name) \
static inline void glue(gen_f, name)(DisasContext *ctx, \
int ra, int rb, int rc, int fn11) \
{ \
gen_ieee_arith3(ctx, gen_helper_##name, ra, rb, rc, fn11); \
}
IEEE_ARITH3(adds)
IEEE_ARITH3(subs)
IEEE_ARITH3(muls)
IEEE_ARITH3(divs)
IEEE_ARITH3(addt)
IEEE_ARITH3(subt)
IEEE_ARITH3(mult)
IEEE_ARITH3(divt)
static void gen_ieee_compare(DisasContext *ctx,
void (*helper)(TCGv, TCGv, TCGv),
int ra, int rb, int rc, int fn11)
{
TCGv va, vb;
/* ??? This is wrong: the instruction is not a nop, it still may
raise exceptions. */
if (unlikely(rc == 31)) {
return;
}
gen_fp_exc_clear();
va = gen_ieee_input(ra, fn11, 1);
vb = gen_ieee_input(rb, fn11, 1);
helper(cpu_fir[rc], va, vb);
tcg_temp_free(va);
tcg_temp_free(vb);
gen_fp_exc_raise(rc, fn11);
}
#define IEEE_CMP3(name) \
static inline void glue(gen_f, name)(DisasContext *ctx, \
int ra, int rb, int rc, int fn11) \
{ \
gen_ieee_compare(ctx, gen_helper_##name, ra, rb, rc, fn11); \
}
IEEE_CMP3(cmptun)
IEEE_CMP3(cmpteq)
IEEE_CMP3(cmptlt)
IEEE_CMP3(cmptle)
static inline uint64_t zapnot_mask(uint8_t lit)
{
@ -1607,7 +1943,7 @@ static inline int translate_one(DisasContext *ctx, uint32_t insn)
}
break;
case 0x14:
switch (fpfn) { /* f11 & 0x3F */
switch (fpfn) { /* fn11 & 0x3F */
case 0x04:
/* ITOFS */
if (!(ctx->amask & AMASK_FIX))
@ -1632,7 +1968,7 @@ static inline int translate_one(DisasContext *ctx, uint32_t insn)
/* SQRTS */
if (!(ctx->amask & AMASK_FIX))
goto invalid_opc;
gen_fsqrts(rb, rc);
gen_fsqrts(ctx, rb, rc, fn11);
break;
case 0x14:
/* ITOFF */
@ -1669,7 +2005,7 @@ static inline int translate_one(DisasContext *ctx, uint32_t insn)
/* SQRTT */
if (!(ctx->amask & AMASK_FIX))
goto invalid_opc;
gen_fsqrtt(rb, rc);
gen_fsqrtt(ctx, rb, rc, fn11);
break;
default:
goto invalid_opc;
@ -1678,7 +2014,7 @@ static inline int translate_one(DisasContext *ctx, uint32_t insn)
case 0x15:
/* VAX floating point */
/* XXX: rounding mode and trap are ignored (!) */
switch (fpfn) { /* f11 & 0x3F */
switch (fpfn) { /* fn11 & 0x3F */
case 0x00:
/* ADDF */
gen_faddf(ra, rb, rc);
@ -1761,77 +2097,75 @@ static inline int translate_one(DisasContext *ctx, uint32_t insn)
break;
case 0x16:
/* IEEE floating-point */
/* XXX: rounding mode and traps are ignored (!) */
switch (fpfn) { /* f11 & 0x3F */
switch (fpfn) { /* fn11 & 0x3F */
case 0x00:
/* ADDS */
gen_fadds(ra, rb, rc);
gen_fadds(ctx, ra, rb, rc, fn11);
break;
case 0x01:
/* SUBS */
gen_fsubs(ra, rb, rc);
gen_fsubs(ctx, ra, rb, rc, fn11);
break;
case 0x02:
/* MULS */
gen_fmuls(ra, rb, rc);
gen_fmuls(ctx, ra, rb, rc, fn11);
break;
case 0x03:
/* DIVS */
gen_fdivs(ra, rb, rc);
gen_fdivs(ctx, ra, rb, rc, fn11);
break;
case 0x20:
/* ADDT */
gen_faddt(ra, rb, rc);
gen_faddt(ctx, ra, rb, rc, fn11);
break;
case 0x21:
/* SUBT */
gen_fsubt(ra, rb, rc);
gen_fsubt(ctx, ra, rb, rc, fn11);
break;
case 0x22:
/* MULT */
gen_fmult(ra, rb, rc);
gen_fmult(ctx, ra, rb, rc, fn11);
break;
case 0x23:
/* DIVT */
gen_fdivt(ra, rb, rc);
gen_fdivt(ctx, ra, rb, rc, fn11);
break;
case 0x24:
/* CMPTUN */
gen_fcmptun(ra, rb, rc);
gen_fcmptun(ctx, ra, rb, rc, fn11);
break;
case 0x25:
/* CMPTEQ */
gen_fcmpteq(ra, rb, rc);
gen_fcmpteq(ctx, ra, rb, rc, fn11);
break;
case 0x26:
/* CMPTLT */
gen_fcmptlt(ra, rb, rc);
gen_fcmptlt(ctx, ra, rb, rc, fn11);
break;
case 0x27:
/* CMPTLE */
gen_fcmptle(ra, rb, rc);
gen_fcmptle(ctx, ra, rb, rc, fn11);
break;
case 0x2C:
/* XXX: incorrect */
if (fn11 == 0x2AC || fn11 == 0x6AC) {
/* CVTST */
gen_fcvtst(rb, rc);
gen_fcvtst(ctx, rb, rc, fn11);
} else {
/* CVTTS */
gen_fcvtts(rb, rc);
gen_fcvtts(ctx, rb, rc, fn11);
}
break;
case 0x2F:
/* CVTTQ */
gen_fcvttq(rb, rc);
gen_fcvttq(ctx, rb, rc, fn11);
break;
case 0x3C:
/* CVTQS */
gen_fcvtqs(rb, rc);
gen_fcvtqs(ctx, rb, rc, fn11);
break;
case 0x3E:
/* CVTQT */
gen_fcvtqt(rb, rc);
gen_fcvtqt(ctx, rb, rc, fn11);
break;
default:
goto invalid_opc;
@ -1910,11 +2244,11 @@ static inline int translate_one(DisasContext *ctx, uint32_t insn)
break;
case 0x130:
/* CVTQL/V */
gen_fcvtqlv(rb, rc);
gen_fcvtql_v(rb, rc);
break;
case 0x530:
/* CVTQL/SV */
gen_fcvtqlsv(rb, rc);
gen_fcvtql_sv(rb, rc);
break;
default:
goto invalid_opc;
@ -2597,6 +2931,17 @@ static inline void gen_intermediate_code_internal(CPUState *env,
ctx.mem_idx = ((env->ps >> 3) & 3);
ctx.pal_mode = env->ipr[IPR_EXC_ADDR] & 1;
#endif
/* ??? Every TB begins with unset rounding mode, to be initialized on
the first fp insn of the TB. Alternately we could define a proper
default for every TB (e.g. QUAL_RM_N or QUAL_RM_D) and make sure
to reset the FP_STATUS to that default at the end of any TB that
changes the default. We could even (gasp) dynamiclly figure out
what default would be most efficient given the running program. */
ctx.tb_rm = -1;
/* Similarly for flush-to-zero. */
ctx.tb_ftz = -1;
num_insns = 0;
max_insns = tb->cflags & CF_COUNT_MASK;
if (max_insns == 0)