[pypy-svn] r36825 - in pypy/dist/pypy/jit/codegen: i386 i386/test test

arigo at codespeak.net arigo at codespeak.net
Tue Jan 16 17:43:26 CET 2007


Author: arigo
Date: Tue Jan 16 17:43:23 2007
New Revision: 36825

Modified:
   pypy/dist/pypy/jit/codegen/i386/rgenop.py
   pypy/dist/pypy/jit/codegen/i386/test/test_operation.py
   pypy/dist/pypy/jit/codegen/test/operation_tests.py
Log:
Fix the signed division and modulo code in the i386 backend.  At least,
I assume that they are fixed, because the tests pass, but the code is
hairy :-)

Added tests for the case of a constant divisor, which I had to do
differently because of lack of registers.  It makes sense anyway to test
this specifically, as they are likely to be special-cased in a more
optimizing version of the backend (e.g. replacing "/ 8" with ">> 3").


Modified: pypy/dist/pypy/jit/codegen/i386/rgenop.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/i386/rgenop.py	(original)
+++ pypy/dist/pypy/jit/codegen/i386/rgenop.py	Tue Jan 16 17:43:23 2007
@@ -237,13 +237,94 @@
     opname = 'int_floordiv'
     input_is_64bits = True
     reg_containing_result = eax
-    emit = staticmethod(I386CodeBuilder.IDIV)
+    @staticmethod
+    def emit(mc, op2):
+        # from the PPC backend which has the same problem:
+        # 
+        #   grumble, the powerpc handles division when the signs of x
+        #   and y differ the other way to how cpython wants it.  this
+        #   crawling horror is a branch-free way of computing the right
+        #   remainder in all cases.  it's probably not optimal.
+        #
+        #   we need to adjust the result iff the remainder is non-zero
+        #   and the signs of x and y differ.  in the standard-ish PPC
+        #   way, we compute boolean values as either all-bits-0 or
+        #   all-bits-1 and "and" them together, resulting in either
+        #   adding 0 or -1 as needed in the final step.
+        #
+        #                 Python    i386
+        #    20/3    =     6, 2     6, 2
+        # (-20)/3    =    -7, 1    -6,-2      # operand signs differ
+        #    20/(-3) =    -7,-1    -6, 2      # operand signs differ
+        # (-20)/(-3) =     6,-2     6,-2
+        #
+        if isinstance(op2, IMM32):
+            # if op2 is an immediate, we do an initial adjustment of operand 1
+            # so that we get directly the correct answer
+            if op2.value >= 0:
+                # if op1 is negative, subtract (op2-1)
+                mc.MOV(ecx, edx)       # -1 if op1 is negative, 0 otherwise
+                mc.AND(ecx, imm(op2.value-1))
+                mc.SUB(eax, ecx)
+                mc.SBB(edx, imm8(0))
+            else:
+                # if op1 is positive (or null), add (op2-1)
+                mc.MOV(ecx, edx)
+                mc.NEG(ecx)            # -1 if op1 is positive, 0 otherwise
+                mc.AND(ecx, imm(op2.value-1))
+                mc.ADD(eax, ecx)
+                mc.ADC(edx, imm8(0))
+            mc.MOV(ecx, op2)
+            mc.IDIV(ecx)
+        else:
+            # subtract 1 to the result if the operand signs differ and
+            # the remainder is not zero
+            mc.MOV(ecx, eax)
+            mc.IDIV(op2)
+            mc.XOR(ecx, op2)
+            mc.SAR(ecx, imm8(31)) # -1 if signs differ, 0 otherwise
+            mc.AND(ecx, edx)      # nonnull if signs differ and edx != 0
+            mc.CMP(ecx, imm8(1))  # no carry flag iff signs differ and edx != 0
+            mc.ADC(eax, imm8(-1)) # subtract 1 iff no carry flag
 
 class OpIntMod(MulOrDivOp):
     opname = 'int_mod'
     input_is_64bits = True
     reg_containing_result = edx
-    emit = staticmethod(I386CodeBuilder.IDIV)
+    @staticmethod
+    def emit(mc, op2):
+        #                 Python    i386
+        #    20/3    =     6, 2     6, 2
+        # (-20)/3    =    -7, 1    -6,-2      # operand signs differ
+        #    20/(-3) =    -7,-1    -6, 2      # operand signs differ
+        # (-20)/(-3) =     6,-2     6,-2
+        #
+        if isinstance(op2, IMM32):
+            mc.MOV(ecx, op2)
+            mc.IDIV(ecx)
+            if op2.value >= 0:
+                # if the result is negative, add op2 to it
+                mc.MOV(ecx, edx)
+                mc.SAR(ecx, imm8(31))
+                mc.AND(ecx, imm(op2.value))
+                mc.ADD(edx, ecx)
+            else:
+                # if the result is > 0, subtract op2 from it
+                mc.MOV(ecx, edx)
+                mc.NEG(ecx)
+                mc.SAR(ecx, imm8(31))
+                mc.AND(ecx, imm(op2.value))
+                mc.SUB(edx, ecx)
+        else:
+            # if the operand signs differ and the remainder is not zero,
+            # add operand2 to the result
+            mc.MOV(ecx, eax)
+            mc.IDIV(op2)
+            mc.XOR(ecx, op2)
+            mc.SAR(ecx, imm8(31)) # -1 if signs differ, 0 otherwise
+            mc.AND(ecx, edx)      # nonnull if signs differ and edx != 0
+            mc.CMOVNZ(ecx, op2)   # == op2  if signs differ and edx != 0
+            mc.ADD(edx, ecx)
 
 class OpUIntMul(MulOrDivOp):
     opname = 'uint_mul'

Modified: pypy/dist/pypy/jit/codegen/i386/test/test_operation.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/i386/test/test_operation.py	(original)
+++ pypy/dist/pypy/jit/codegen/i386/test/test_operation.py	Tue Jan 16 17:43:23 2007
@@ -45,3 +45,5 @@
 class TestOperation(I386TestMixin, OperationTests):
     pass
 
+    # for the individual tests see
+    # ====> ../../test/operation_tests.py

Modified: pypy/dist/pypy/jit/codegen/test/operation_tests.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/test/operation_tests.py	(original)
+++ pypy/dist/pypy/jit/codegen/test/operation_tests.py	Tue Jan 16 17:43:23 2007
@@ -258,3 +258,12 @@
             assert fp(2.0) == fn(2.0), op
             assert fp(0.0) == fn(0.0), op
             assert fp(-2.0) == fn(-2.0), op
+
+    def test_constants_in_divmod(self):
+        for op in ['x // y', 'x % y']:
+            for constant in range(1, 20):
+                fn = eval("lambda x: " + op, {'y': constant})
+                fp = self.rgen(fn, [int], int)
+                for operand1 in range(-32, 33):
+                    res = fp(operand1)
+                    assert res == eval(op, {'x': operand1, 'y': constant})



More information about the Pypy-commit mailing list