| /* Accurate fp support for CGEN-based simulators. |
| Copyright (C) 1999 Cygnus Solutions. |
| |
| This implemention assumes: |
| typedef USI SF; |
| typedef UDI DF; |
| |
| TODO: |
| - lazy encoding/decoding |
| - checking return code (say by callback) |
| - proper rounding |
| */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #include "sim-main.h" |
| #include "sim-fpu.h" |
| |
| /* SF mode support */ |
| |
| static SF |
| addsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| status = sim_fpu_add (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| subsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| status = sim_fpu_sub (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| mulsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| status = sim_fpu_mul (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| divsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| status = sim_fpu_div (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| remsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| status = sim_fpu_rem (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| negsf (CGEN_FPU* fpu, SF x) |
| { |
| sim_fpu op1; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| status = sim_fpu_neg (&ans, &op1); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| abssf (CGEN_FPU* fpu, SF x) |
| { |
| sim_fpu op1; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| status = sim_fpu_abs (&ans, &op1); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| sqrtsf (CGEN_FPU* fpu, SF x) |
| { |
| sim_fpu op1; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| status = sim_fpu_sqrt (&ans, &op1); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| invsf (CGEN_FPU* fpu, SF x) |
| { |
| sim_fpu op1; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| status = sim_fpu_inv (&ans, &op1); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| minsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| status = sim_fpu_min (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static SF |
| maxsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned32 res; |
| sim_fpu_status status; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| status = sim_fpu_max (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to32 (&res, &ans); |
| |
| return res; |
| } |
| |
| static CGEN_FP_CMP |
| cmpsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| |
| if (sim_fpu_is_nan (&op1) |
| || sim_fpu_is_nan (&op2)) |
| return FP_CMP_NAN; |
| |
| if (x < y) |
| return FP_CMP_LT; |
| if (x > y) |
| return FP_CMP_GT; |
| return FP_CMP_EQ; |
| } |
| |
| static int |
| eqsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| return sim_fpu_is_eq (&op1, &op2); |
| } |
| |
| static int |
| nesf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| return sim_fpu_is_ne (&op1, &op2); |
| } |
| |
| static int |
| ltsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| return sim_fpu_is_lt (&op1, &op2); |
| } |
| |
| static int |
| lesf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| return sim_fpu_is_le (&op1, &op2); |
| } |
| |
| static int |
| gtsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| return sim_fpu_is_gt (&op1, &op2); |
| } |
| |
| static int |
| gesf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| return sim_fpu_is_ge (&op1, &op2); |
| } |
| |
| static int |
| unorderedsf (CGEN_FPU* fpu, SF x, SF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_32to (&op2, y); |
| return sim_fpu_is_nan (&op1) || sim_fpu_is_nan (&op2); |
| } |
| |
| |
| static DF |
| fextsfdf (CGEN_FPU* fpu, int how UNUSED, SF x) |
| { |
| sim_fpu op1; |
| unsigned64 res; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_to64 (&res, &op1); |
| |
| return res; |
| } |
| |
| static SF |
| ftruncdfsf (CGEN_FPU* fpu, int how UNUSED, DF x) |
| { |
| sim_fpu op1; |
| unsigned32 res; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_to32 (&res, &op1); |
| |
| return res; |
| } |
| |
| static SF |
| floatsisf (CGEN_FPU* fpu, int how UNUSED, SI x) |
| { |
| sim_fpu ans; |
| unsigned32 res; |
| |
| sim_fpu_i32to (&ans, x, sim_fpu_round_near); |
| sim_fpu_to32 (&res, &ans); |
| return res; |
| } |
| |
| static DF |
| floatsidf (CGEN_FPU* fpu, int how UNUSED, SI x) |
| { |
| sim_fpu ans; |
| unsigned64 res; |
| |
| sim_fpu_i32to (&ans, x, sim_fpu_round_near); |
| sim_fpu_to64 (&res, &ans); |
| return res; |
| } |
| |
| static DF |
| floatdidf (CGEN_FPU* fpu, int how UNUSED, DI x) |
| { |
| sim_fpu ans; |
| unsigned64 res; |
| |
| sim_fpu_i64to (&ans, x, sim_fpu_round_near); |
| sim_fpu_to64 (&res, &ans); |
| return res; |
| } |
| |
| static SF |
| ufloatsisf (CGEN_FPU* fpu, int how UNUSED, USI x) |
| { |
| sim_fpu ans; |
| unsigned32 res; |
| |
| sim_fpu_u32to (&ans, x, sim_fpu_round_near); |
| sim_fpu_to32 (&res, &ans); |
| return res; |
| } |
| |
| static SI |
| fixsfsi (CGEN_FPU* fpu, int how UNUSED, SF x) |
| { |
| sim_fpu op1; |
| signed32 res; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_to32i (&res, &op1, sim_fpu_round_near); |
| return res; |
| } |
| |
| static SI |
| fixdfsi (CGEN_FPU* fpu, int how UNUSED, DF x) |
| { |
| sim_fpu op1; |
| signed32 res; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_to32i (&res, &op1, sim_fpu_round_near); |
| return res; |
| } |
| |
| static DI |
| fixdfdi (CGEN_FPU* fpu, int how UNUSED, DF x) |
| { |
| sim_fpu op1; |
| signed64 res; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_to64i (&res, &op1, sim_fpu_round_near); |
| return res; |
| } |
| |
| static USI |
| ufixsfsi (CGEN_FPU* fpu, int how UNUSED, SF x) |
| { |
| sim_fpu op1; |
| unsigned32 res; |
| |
| sim_fpu_32to (&op1, x); |
| sim_fpu_to32u (&res, &op1, sim_fpu_round_near); |
| return res; |
| } |
| |
| /* DF mode support */ |
| |
| static DF |
| adddf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| status = sim_fpu_add (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| subdf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| status = sim_fpu_sub (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| muldf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| status = sim_fpu_mul (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| divdf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| status = sim_fpu_div (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| remdf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| status = sim_fpu_rem (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64(&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| negdf (CGEN_FPU* fpu, DF x) |
| { |
| sim_fpu op1; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| status = sim_fpu_neg (&ans, &op1); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| absdf (CGEN_FPU* fpu, DF x) |
| { |
| sim_fpu op1; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| status = sim_fpu_abs (&ans, &op1); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| sqrtdf (CGEN_FPU* fpu, DF x) |
| { |
| sim_fpu op1; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| status = sim_fpu_sqrt (&ans, &op1); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| invdf (CGEN_FPU* fpu, DF x) |
| { |
| sim_fpu op1; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| status = sim_fpu_inv (&ans, &op1); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| mindf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| status = sim_fpu_min (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static DF |
| maxdf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| sim_fpu ans; |
| unsigned64 res; |
| sim_fpu_status status; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| status = sim_fpu_max (&ans, &op1, &op2); |
| if (status != 0) |
| (*fpu->ops->error) (fpu, status); |
| sim_fpu_to64 (&res, &ans); |
| |
| return res; |
| } |
| |
| static CGEN_FP_CMP |
| cmpdf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| |
| if (sim_fpu_is_nan (&op1) |
| || sim_fpu_is_nan (&op2)) |
| return FP_CMP_NAN; |
| |
| if (x < y) |
| return FP_CMP_LT; |
| if (x > y) |
| return FP_CMP_GT; |
| return FP_CMP_EQ; |
| } |
| |
| static int |
| eqdf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| return sim_fpu_is_eq (&op1, &op2); |
| } |
| |
| static int |
| nedf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| return sim_fpu_is_ne (&op1, &op2); |
| } |
| |
| static int |
| ltdf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| return sim_fpu_is_lt (&op1, &op2); |
| } |
| |
| static int |
| ledf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| return sim_fpu_is_le (&op1, &op2); |
| } |
| |
| static int |
| gtdf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| return sim_fpu_is_gt (&op1, &op2); |
| } |
| |
| static int |
| gedf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| return sim_fpu_is_ge (&op1, &op2); |
| } |
| |
| static int |
| unordereddf (CGEN_FPU* fpu, DF x, DF y) |
| { |
| sim_fpu op1; |
| sim_fpu op2; |
| |
| sim_fpu_64to (&op1, x); |
| sim_fpu_64to (&op2, y); |
| return sim_fpu_is_nan (&op1) || sim_fpu_is_nan (&op2); |
| } |
| |
| /* Initialize FP_OPS to use accurate library. */ |
| |
| void |
| cgen_init_accurate_fpu (SIM_CPU* cpu, CGEN_FPU* fpu, CGEN_FPU_ERROR_FN* error) |
| { |
| CGEN_FP_OPS* o; |
| |
| fpu->owner = cpu; |
| /* ??? small memory leak, not freed by sim_close */ |
| fpu->ops = (CGEN_FP_OPS*) xmalloc (sizeof (CGEN_FP_OPS)); |
| |
| o = fpu->ops; |
| memset (o, 0, sizeof (*o)); |
| |
| o->error = error; |
| |
| o->addsf = addsf; |
| o->subsf = subsf; |
| o->mulsf = mulsf; |
| o->divsf = divsf; |
| o->remsf = remsf; |
| o->negsf = negsf; |
| o->abssf = abssf; |
| o->sqrtsf = sqrtsf; |
| o->invsf = invsf; |
| o->minsf = minsf; |
| o->maxsf = maxsf; |
| o->cmpsf = cmpsf; |
| o->eqsf = eqsf; |
| o->nesf = nesf; |
| o->ltsf = ltsf; |
| o->lesf = lesf; |
| o->gtsf = gtsf; |
| o->gesf = gesf; |
| o->unorderedsf = unorderedsf; |
| |
| o->adddf = adddf; |
| o->subdf = subdf; |
| o->muldf = muldf; |
| o->divdf = divdf; |
| o->remdf = remdf; |
| o->negdf = negdf; |
| o->absdf = absdf; |
| o->sqrtdf = sqrtdf; |
| o->invdf = invdf; |
| o->mindf = mindf; |
| o->maxdf = maxdf; |
| o->cmpdf = cmpdf; |
| o->eqdf = eqdf; |
| o->nedf = nedf; |
| o->ltdf = ltdf; |
| o->ledf = ledf; |
| o->gtdf = gtdf; |
| o->gedf = gedf; |
| o->unordereddf = unordereddf; |
| o->fextsfdf = fextsfdf; |
| o->ftruncdfsf = ftruncdfsf; |
| o->floatsisf = floatsisf; |
| o->floatsidf = floatsidf; |
| o->floatdidf = floatdidf; |
| o->ufloatsisf = ufloatsisf; |
| o->fixsfsi = fixsfsi; |
| o->fixdfsi = fixdfsi; |
| o->fixdfdi = fixdfdi; |
| o->ufixsfsi = ufixsfsi; |
| } |