x86: Make fprem like the fprem on a real x87
authorAndreas Sandberg <andreas@sandberg.pp.se>
Tue, 18 Jun 2013 14:10:42 +0000 (16:10 +0200)
committerAndreas Sandberg <andreas@sandberg.pp.se>
Tue, 18 Jun 2013 14:10:42 +0000 (16:10 +0200)
The current implementation of fprem simply does an fmod and doesn't
simulate any of the iterative behavior in a real fprem. This isn't
normally a problem, however, it can lead to problems when switching
between CPU models. If switching from a real CPU in the middle of an
fprem loop to a simulated CPU, the output of the fprem loop becomes
correupted. This changeset changes the fprem implementation to work
like the one on real hardware.

src/arch/x86/isa/insts/x87/arithmetic/partial_remainder.py
src/arch/x86/isa/microops/fpop.isa

index b02184e1a1291c1d933cd27b6146beadd8fa8f19..42aabfdf68370284eed94cef68f9af73949e660f 100644 (file)
 
 microcode = '''
 def macroop FPREM {
-    premfp st(0), st(1), st(0)
+    premfp st(0), st(1), st(0), SetStatus=True
 };
 
 def macroop FPREM1 {
-    premfp st(0), st(1), st(0)
+    premfp st(0), st(1), st(0), SetStatus=True
 };
 '''
index e6372ba6be891e75432c89316ba787801b3fc794..22d6fbcdad60f127d9a474847068fa10e71a35b5 100644 (file)
@@ -330,11 +330,34 @@ let {{
 
     class PremFp(FpBinaryOp):
         code = '''
-            FpDestReg = fmod(FpSrcReg1, FpSrcReg2);
-            DPRINTF(X86, "src1: %lf, src2: %lf, dest: %lf\\n", FpSrcReg1, FpSrcReg2, FpDestReg);
+            MiscReg new_fsw(FSW);
+            int src1_exp;
+            int src2_exp;
+            std::frexp(FpSrcReg1, &src1_exp);
+            std::frexp(FpSrcReg2, &src2_exp);
+
+            const int d(src2_exp - src1_exp);
+            if (d < 64) {
+                const int64_t q(std::trunc(FpSrcReg2 / FpSrcReg1));
+                FpDestReg = FpSrcReg2 - FpSrcReg1 * q;
+                new_fsw &= ~(CC0Bit | CC1Bit | CC2Bit | CC2Bit);
+                new_fsw |= (q & 0x1) ? CC1Bit : 0;
+                new_fsw |= (q & 0x2) ? CC3Bit : 0;
+                new_fsw |= (q & 0x4) ? CC0Bit : 0;
+            } else {
+                const int n(42);
+                const int64_t qq(std::trunc(
+                    FpSrcReg2 / std::ldexp(FpSrcReg1, d - n)));
+                FpDestReg = FpSrcReg2 - std::ldexp(FpSrcReg1 * qq, d - n);
+                new_fsw |= CC2Bit;
+            }
+            DPRINTF(X86, "src1: %lf, src2: %lf, dest: %lf, FSW: 0x%x\\n",
+                    FpSrcReg1, FpSrcReg2, FpDestReg, new_fsw);
         '''
         op_class = 'FloatDivOp'
 
+        flag_code = 'FSW = new_fsw;'
+
     class Compfp(FpBinaryOp):
         def __init__(self, src1, src2, spm=0, setStatus=False, \
                 dataSize="env.dataSize"):