aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/math-emu
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2013-11-04 11:53:14 -0500
committerScott Wood <scottwood@freescale.com>2014-01-07 19:33:48 -0500
commit28414a6def9dc00dcd0d0f3eea6911fda9a4a4e1 (patch)
treeb47cad129a84f13e93086144c29b615012daeebe /arch/powerpc/math-emu
parent640e922501103aaf2e0abb4cf4de5d49fa8342f7 (diff)
powerpc: fix e500 SPE float rounding inexactness detection
The e500 SPE floating-point emulation code for the rounding modes rounding to positive or negative infinity (which may not be implemented in hardware) tries to avoid emulating rounding if the result was inexact. However, it tests inexactness using the sticky bit with the cumulative result of previous operations, rather than with the non-sticky bits relating to the operation that generated the interrupt. Furthermore, when a vector operation generates the interrupt, it's possible that only one of the low and high parts is inexact, and so only that part should have rounding emulated. This results in incorrect rounding of exact results in these modes when the sticky bit is set from a previous operation. (I'm not sure why the rounding interrupts are generated at all when the result is exact, but empirically the hardware does generate them.) This patch checks for inexactness using the correct bits of SPEFSCR, and ensures that rounding only occurs when the relevant part of the result was actually inexact. Signed-off-by: Joseph Myers <joseph@codesourcery.com> Signed-off-by: Scott Wood <scottwood@freescale.com>
Diffstat (limited to 'arch/powerpc/math-emu')
-rw-r--r--arch/powerpc/math-emu/math_efp.c23
1 files changed, 16 insertions, 7 deletions
diff --git a/arch/powerpc/math-emu/math_efp.c b/arch/powerpc/math-emu/math_efp.c
index 59835c625dc6..ecdf35d8cb00 100644
--- a/arch/powerpc/math-emu/math_efp.c
+++ b/arch/powerpc/math-emu/math_efp.c
@@ -680,7 +680,8 @@ int speround_handler(struct pt_regs *regs)
680{ 680{
681 union dw_union fgpr; 681 union dw_union fgpr;
682 int s_lo, s_hi; 682 int s_lo, s_hi;
683 unsigned long speinsn, type, fc; 683 int lo_inexact, hi_inexact;
684 unsigned long speinsn, type, fc, fptype;
684 685
685 if (get_user(speinsn, (unsigned int __user *) regs->nip)) 686 if (get_user(speinsn, (unsigned int __user *) regs->nip))
686 return -EFAULT; 687 return -EFAULT;
@@ -693,8 +694,12 @@ int speround_handler(struct pt_regs *regs)
693 __FPU_FPSCR = mfspr(SPRN_SPEFSCR); 694 __FPU_FPSCR = mfspr(SPRN_SPEFSCR);
694 pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); 695 pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
695 696
697 fptype = (speinsn >> 5) & 0x7;
698
696 /* No need to round if the result is exact */ 699 /* No need to round if the result is exact */
697 if (!(__FPU_FPSCR & FP_EX_INEXACT)) 700 lo_inexact = __FPU_FPSCR & (SPEFSCR_FG | SPEFSCR_FX);
701 hi_inexact = __FPU_FPSCR & (SPEFSCR_FGH | SPEFSCR_FXH);
702 if (!(lo_inexact || (hi_inexact && fptype == VCT)))
698 return 0; 703 return 0;
699 704
700 fc = (speinsn >> 21) & 0x1f; 705 fc = (speinsn >> 21) & 0x1f;
@@ -705,7 +710,7 @@ int speround_handler(struct pt_regs *regs)
705 710
706 pr_debug("round fgpr: %08x %08x\n", fgpr.wp[0], fgpr.wp[1]); 711 pr_debug("round fgpr: %08x %08x\n", fgpr.wp[0], fgpr.wp[1]);
707 712
708 switch ((speinsn >> 5) & 0x7) { 713 switch (fptype) {
709 /* Since SPE instructions on E500 core can handle round to nearest 714 /* Since SPE instructions on E500 core can handle round to nearest
710 * and round toward zero with IEEE-754 complied, we just need 715 * and round toward zero with IEEE-754 complied, we just need
711 * to handle round toward +Inf and round toward -Inf by software. 716 * to handle round toward +Inf and round toward -Inf by software.
@@ -728,11 +733,15 @@ int speround_handler(struct pt_regs *regs)
728 733
729 case VCT: 734 case VCT:
730 if (FP_ROUNDMODE == FP_RND_PINF) { 735 if (FP_ROUNDMODE == FP_RND_PINF) {
731 if (!s_lo) fgpr.wp[1]++; /* Z_low > 0, choose Z1 */ 736 if (lo_inexact && !s_lo)
732 if (!s_hi) fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */ 737 fgpr.wp[1]++; /* Z_low > 0, choose Z1 */
738 if (hi_inexact && !s_hi)
739 fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */
733 } else { /* round to -Inf */ 740 } else { /* round to -Inf */
734 if (s_lo) fgpr.wp[1]++; /* Z_low < 0, choose Z2 */ 741 if (lo_inexact && s_lo)
735 if (s_hi) fgpr.wp[0]++; /* Z_high < 0, choose Z2 */ 742 fgpr.wp[1]++; /* Z_low < 0, choose Z2 */
743 if (hi_inexact && s_hi)
744 fgpr.wp[0]++; /* Z_high < 0, choose Z2 */
736 } 745 }
737 break; 746 break;
738 747