[pypy-svn] r36737 - in pypy/branch/i386-regalloc/pypy/jit/codegen/i386: . test

arigo at codespeak.net arigo at codespeak.net
Sun Jan 14 14:39:26 CET 2007


Author: arigo
Date: Sun Jan 14 14:39:24 2007
New Revision: 36737

Modified:
   pypy/branch/i386-regalloc/pypy/jit/codegen/i386/rgenop.py
   pypy/branch/i386-regalloc/pypy/jit/codegen/i386/ri386.py
   pypy/branch/i386-regalloc/pypy/jit/codegen/i386/test/test_operation.py
Log:
Add many instructions: arithmetic, [gs]etfield, [gs]etarrayitem, malloc.


Modified: pypy/branch/i386-regalloc/pypy/jit/codegen/i386/rgenop.py
==============================================================================
--- pypy/branch/i386-regalloc/pypy/jit/codegen/i386/rgenop.py	(original)
+++ pypy/branch/i386-regalloc/pypy/jit/codegen/i386/rgenop.py	Sun Jan 14 14:39:24 2007
@@ -40,6 +40,15 @@
         self.x = x
     def allocate(self, allocator):
         allocator.using(self.x)
+    def generate(self, allocator):
+        try:
+            dstop = allocator.get_operand(self)
+        except KeyError:
+            return    # result not used
+        srcop = allocator.get_operand(self.x)
+        return self.generate2(allocator.mc, dstop, srcop)
+    def generate2(self, mc, dstop, srcop):
+        raise NotImplementedError
 
 class UnaryOp(Op1):
     def generate(self, allocator):
@@ -54,16 +63,30 @@
     opname = 'int_neg'
     emit = staticmethod(I386CodeBuilder.NEG)
 
+class OpIntInvert(UnaryOp):
+    opname = 'int_invert'
+    emit = staticmethod(I386CodeBuilder.NOT)
+
+class OpIntAbs(Op1):
+    opname = 'int_abs'
+    def generate2(self, mc, dstop, srcop):
+        # ABS-computing code from Psyco, found by exhaustive search
+        # on *all* short sequences of operations :-)
+        inplace = (dstop == srcop)
+        if inplace or not (isinstance(srcop, REG) or isinstance(dstop, REG)):
+            mc.MOV(ecx, srcop)
+            srcop = ecx
+        if not inplace:
+            mc.MOV(dstop, srcop)
+        mc.SHL(dstop, imm8(1))
+        mc.SBB(dstop, srcop)
+        mc.SBB(ecx, ecx)
+        mc.XOR(dstop, ecx)
+
 class OpSameAs(Op1):
     clobbers_cc = False
-    def generate(self, allocator):
-        try:
-            dstop = allocator.get_operand(self)
-        except KeyError:
-            return    # result not used
-        srcop = allocator.get_operand(self.x)
+    def generate2(self, mc, dstop, srcop):
         if srcop != dstop:
-            mc = allocator.mc
             try:
                 mc.MOV(dstop, srcop)
             except FailedToImplement:
@@ -91,9 +114,6 @@
     def allocate(self, allocator):
         allocator.using(self.x)
         allocator.using(self.y)
-
-class BinaryOp(Op2):
-    commutative = False
     def generate(self, allocator):
         try:
             dstop = allocator.get_operand(self)
@@ -101,10 +121,16 @@
             return    # simple operation whose result is not used anyway
         op1 = allocator.get_operand(self.x)
         op2 = allocator.get_operand(self.y)
+        self.generate3(allocator.mc, dstop, op1, op2)
+    def generate3(self, mc, dstop, op1, op2):
+        raise NotImplementedError
+
+class BinaryOp(Op2):
+    commutative = False
+    def generate3(self, mc, dstop, op1, op2):
         # now all of dstop, op1 and op2 may alias each other and be in
         # a register, in the stack or an immediate... finding a correct
         # and encodable combination of instructions is loads of fun
-        mc = allocator.mc
         if dstop == op1:
             case = 1       # optimize for this common case
         elif self.commutative and dstop == op2:
@@ -151,16 +177,21 @@
     opname = 'int_sub'
     emit = staticmethod(I386CodeBuilder.SUB)
 
+class OpIntAnd(BinaryOp):
+    opname = 'int_and'
+    emit = staticmethod(I386CodeBuilder.AND)
+
+class OpIntOr(BinaryOp):
+    opname = 'int_or'
+    emit = staticmethod(I386CodeBuilder.OR)
+
+class OpIntXor(BinaryOp):
+    opname = 'int_xor'
+    emit = staticmethod(I386CodeBuilder.XOR)
+
 class OpIntMul(Op2):
     opname = 'int_mul'
-    def generate(self, allocator):
-        try:
-            dstop = allocator.get_operand(self)
-        except KeyError:
-            return    # simple operation whose result is not used anyway
-        op1 = allocator.get_operand(self.x)
-        op2 = allocator.get_operand(self.y)
-        mc = allocator.mc
+    def generate3(self, mc, dstop, op1, op2):
         if isinstance(dstop, REG):
             tmpop = dstop
         else:
@@ -181,15 +212,8 @@
 class OpIntFloorDiv(Op2):
     opname = 'int_floordiv'
     divmod_result_register = eax
-    def generate(self, allocator):
-        try:
-            dstop = allocator.get_operand(self)
-        except KeyError:
-            return    # simple operation whose result is not used anyway
-        op1 = allocator.get_operand(self.x)
-        op2 = allocator.get_operand(self.y)
+    def generate3(self, mc, dstop, op1, op2):
         # not very efficient but not a very common operation either
-        mc = allocator.mc
         if dstop != eax:
             mc.PUSH(eax)
         if dstop != edx:
@@ -212,6 +236,37 @@
     opname = 'int_mod'
     divmod_result_register = edx
 
+class OpIntLShift(Op2):
+    opname = 'int_lshift'
+    def generate3(self, mc, dstop, op1, op2):
+        # XXX not optimized
+        mc.MOV(ecx, op2)
+        if dstop != op1:
+            try:
+                mc.MOV(dstop, op1)
+            except FailedToImplement:
+                mc.PUSH(op1)
+                mc.POP(dstop)
+        mc.SHL(dstop, cl)
+        mc.CMP(ecx, imm8(32))
+        mc.SBB(ecx, ecx)
+        mc.AND(dstop, ecx)
+
+class OpIntRShift(Op2):
+    opname = 'int_rshift'
+    def generate3(self, mc, dstop, op1, op2):
+        # XXX not optimized
+        mc.MOV(ecx, imm(31))
+        mc.CMP(op2, ecx)
+        mc.CMOVBE(ecx, op2)
+        if dstop != op1:
+            try:
+                mc.MOV(dstop, op1)
+            except FailedToImplement:
+                mc.PUSH(op1)
+                mc.POP(dstop)
+        mc.SAR(dstop, cl)
+
 class OpCompare2(Op2):
     result_kind = RK_CC
     def generate(self, allocator):
@@ -337,6 +392,220 @@
                 mc.MOV(dstop, eax)
             mc.POP(eax)
 
+def array_item_operand(mc, base, arraytoken, opindex):
+    # may use ecx
+    _, startoffset, itemoffset = arraytoken
+
+    if isinstance(opindex, IMM32):
+        startoffset += itemoffset * opindex.value
+        opindex = None
+        indexshift = 0
+    elif itemoffset in SIZE2SHIFT:
+        if not isinstance(opindex, REG):
+            mc.MOV(ecx, opindex)
+            opindex = ecx
+        indexshift = SIZE2SHIFT[itemoffset]
+    else:
+        mc.IMUL(ecx, opindex, imm(itemoffset))
+        opindex = ecx
+        indexshift = 0
+
+    assert base is not ecx
+    if isinstance(base, MODRM):
+        if opindex != ecx:
+            mc.MOV(ecx, base)
+        else:   # waaaa
+            opindex = None
+            if indexshift > 0:
+                mc.SHL(ecx, imm8(indexshift))
+            mc.ADD(ecx, base)
+        base = ecx
+    elif isinstance(base, IMM32):
+        startoffset += base.value
+        base = None
+
+    if itemoffset == 1:
+        return memSIB8(base, opindex, indexshift, startoffset)
+    else:
+        return memSIB (base, opindex, indexshift, startoffset)
+
+class OpComputeSize(Operation):
+    def __init__(self, varsizealloctoken, gv_length):
+        self.varsizealloctoken = varsizealloctoken
+        self.gv_length = gv_length
+    def allocate(self, allocator):
+        self.using(self.gv_length)
+    def generate(self, allocator):
+        dstop = allocator.get_operand(self)
+        srcop = allocator.get_operand(self.gv_length)
+        mc = allocator.mc
+        op_size = array_item_operand(mc, None, self.varsizealloctoken, srcop)
+        try:
+            mc.LEA(dstop, op_size)
+        except FailedToImplement:
+            mc.LEA(ecx, op_size)
+            mc.MOV(dstop, ecx)
+
+def hard_store(mc, opmemtarget, opvalue, itemsize=WORD):
+    # For the possibly hard cases of stores
+    # Generates a store to 'opmemtarget' of size 'itemsize' == 1, 2 or 4.
+    # If it is 1, opmemtarget must be a MODRM8; otherwise, it must be a MODRM.
+    if itemsize == WORD:
+        try:
+            mc.MOV(opmemtarget, opvalue)
+        except FailedToImplement:
+            if opmemtarget.involves_ecx():
+                mc.PUSH(opvalue)
+                mc.POP(opmemtarget)
+            else:
+                mc.MOV(ecx, opvalue)
+                mc.MOV(opmemtarget, ecx)
+    else:
+        must_pop_eax = False
+        if itemsize == 1:
+            if isinstance(opvalue, REG) and opvalue.lowest8bits:
+                # a register whose lower 8 bits are directly readable
+                opvalue = opvalue.lowest8bits
+            elif isinstance(opvalue, IMM8):
+                pass
+            else:
+                if opmemtarget.involves_ecx():    # grumble!
+                    mc.PUSH(eax)
+                    must_pop_eax = True
+                    scratch = eax
+                else:
+                    scratch = ecx
+                if opvalue.width == 1:
+                    mc.MOV(scratch.lowest8bits, opvalue)
+                else:
+                    mc.MOV(scratch, opvalue)
+                opvalue = scratch.lowest8bits
+        else:
+            assert itemsize == 2
+            if isinstance(opvalue, MODRM) or type(opvalue) is IMM32:
+                # no support for now to encode 16-bit immediates,
+                # so we use a scratch register for this case too
+                if opmemtarget.involves_ecx():    # grumble!
+                    mc.PUSH(eax)
+                    must_pop_eax = True
+                    scratch = eax
+                else:
+                    scratch = ecx
+                mc.MOV(scratch, opvalue)
+                opvalue = scratch
+            mc.o16()    # prefix for the MOV below
+        # and eventually, the real store:
+        mc.MOV(opmemtarget, opvalue)
+        if must_pop_eax:
+            mc.POP(eax)
+
+def hard_load(mc, opdst, opmemsource, itemsize=WORD):
+    # For the possibly hard cases of stores
+    # Generates a load from 'opmemsource' of size 'itemsize' == 1, 2 or 4.
+    # If it is 1, opmemtarget must be a MODRM8; otherwise, it must be a MODRM.
+    if itemsize == WORD:
+        try:
+            mc.MOV(opdst, opmemsource)
+        except FailedToImplement:               # opdst is a MODRM
+            if opmemtarget.involves_ecx():
+                mc.PUSH(opmemsource)
+                mc.POP(opdst)
+            else:
+                mc.MOV(ecx, opmemsource)
+                mc.MOV(opdst, ecx)
+    else:
+        try:
+            mc.MOVZX(opdst, opmemsource)
+        except FailedToImplement:               # opdst is a MODRM
+            if opmemtarget.involves_ecx():
+                mc.PUSH(eax)
+                mc.MOVZX(eax, opmemsource)
+                mc.MOV(opdst, eax)
+                mc.POP(eax)
+            else:
+                mc.MOVZX(ecx, opmemsource)
+                mc.MOV(opdst, ecx)
+
+class OpGetField(Operation):
+    def __init__(self, fieldtoken, gv_ptr, gv_value):
+        self.fieldtoken = fieldtoken
+        self.gv_ptr   = gv_ptr
+    def allocate(self, allocator):
+        self.using(self.gv_ptr)
+    def generate(self, allocator):
+        try:
+            dstop = allocator.get_operand(self)
+        except KeyError:
+            return    # result not used
+        opptr = allocator.get_operand(self.gv_ptr)
+        mc = allocator.mc
+        if not isinstance(opptr, REG):
+            mc.MOV(ecx, opptr)
+            opptr = ecx
+        offset, fieldsize = self.fieldtoken
+        opsource = mem(opptr, offset)
+        hard_load(mc, dstop, opsource, fieldsize)
+
+class OpSetField(Operation):
+    result_kind = RK_NO_RESULT
+    def __init__(self, fieldtoken, gv_ptr, gv_value):
+        self.fieldtoken = fieldtoken
+        self.gv_ptr   = gv_ptr
+        self.gv_value = gv_value
+    def allocate(self, allocator):
+        self.using(self.gv_ptr)
+        self.using(self.gv_value)
+    def generate(self, allocator):
+        opptr   = allocator.get_operand(self.gv_ptr)
+        opvalue = allocator.get_operand(self.gv_value)
+        mc = allocator.mc
+        if not isinstance(opptr, REG):
+            mc.MOV(ecx, opptr)
+            opptr = ecx
+        offset, fieldsize = self.fieldtoken
+        optarget = mem(opptr, offset)
+        hard_store(mc, optarget, opvalue, fieldsize)
+
+class OpGetArrayItem(Operation):
+    def __init__(self, arraytoken, gv_array, gv_index):
+        self.arraytoken = arraytoken
+        self.gv_array = gv_array
+        self.gv_index = gv_index
+    def allocate(self, allocator):
+        self.using(self.gv_array)
+        self.using(self.gv_index)
+    def generate(self, allocator):
+        try:
+            dstop = allocator.get_operand(self)
+        except KeyError:
+            return    # result not used
+        oparray = allocator.get_operand(self.gv_array)
+        opindex = allocator.get_operand(self.gv_index)
+        mc = allocator.mc
+        opsource = array_item_operand(mc, oparray, self.arraytoken, opindex)
+        _, _, itemsize = arraytoken
+        hard_load(mc, dstop, opsource, itemsize)
+
+class OpSetArrayItem(Operation):
+    result_kind = RK_NO_RESULT
+    def __init__(self, arraytoken, gv_array, gv_index, gv_value):
+        self.arraytoken = arraytoken
+        self.gv_array = gv_array
+        self.gv_index = gv_index
+        self.gv_value = gv_value
+    def allocate(self, allocator):
+        self.using(self.gv_array)
+        self.using(self.gv_index)
+        self.using(self.gv_value)
+    def generate(self, allocator):
+        oparray = allocator.get_operand(self.gv_array)
+        opindex = allocator.get_operand(self.gv_index)
+        opvalue = allocator.get_operand(self.gv_value)
+        mc = allocator.mc
+        optarget = array_item_operand(mc, oparray, self.arraytoken, opindex)
+        _, _, itemsize = arraytoken
+        hard_store(mc, optarget, opvalue, itemsize)
+
 # ____________________________________________________________
 
 class IntConst(GenConst):
@@ -477,6 +746,19 @@
 OPCLASSES2 = setup_opclasses(Op2)
 del setup_opclasses
 
+# identity operations
+OPCLASSES1['cast_bool_to_int'] = None
+OPCLASSES1['cast_int_to_char'] = None
+OPCLASSES1['cast_int_to_unichar'] = None
+
+# synonyms
+OPCLASSES2['char_lt'] =                            OPCLASSES2['int_lt']
+OPCLASSES2['char_le'] =                            OPCLASSES2['int_le']
+OPCLASSES2['char_eq'] = OPCLASSES2['unichar_eq'] = OPCLASSES2['int_eq']
+OPCLASSES2['char_ne'] = OPCLASSES2['unichar_ne'] = OPCLASSES2['int_ne']
+OPCLASSES2['char_gt'] =                            OPCLASSES2['int_gt']
+OPCLASSES2['char_ge'] =                            OPCLASSES2['int_ge']
+
 def setup_conditions():
     result1 = [None] * 16
     result2 = [None] * 16
@@ -493,6 +775,45 @@
     assert 0 <= cond < INSN_JMP
     return cond ^ 1
 
+SIZE2SHIFT = {1: 0,
+              2: 1,
+              4: 2,
+              8: 3}
+
+# ____________________________________________________________
+
+GC_MALLOC = lltype.Ptr(lltype.FuncType([lltype.Signed], llmemory.Address))
+
+def gc_malloc(size):
+    from pypy.rpython.lltypesystem.lloperation import llop
+    return llop.call_boehm_gc_alloc(llmemory.Address, size)
+
+def gc_malloc_fnaddr():
+    """Returns the address of the Boehm 'malloc' function."""
+    if we_are_translated():
+        gc_malloc_ptr = llhelper(GC_MALLOC, gc_malloc)
+        return lltype.cast_ptr_to_int(gc_malloc_ptr)
+    else:
+        # <pedronis> don't do this at home
+        import threading
+        if not isinstance(threading.currentThread(), threading._MainThread):
+            import py
+            py.test.skip("must run in the main thread")
+        try:
+            from ctypes import cast, c_void_p
+            from pypy.rpython.rctypes.tool import util
+            path = util.find_library('gc')
+            if path is None:
+                raise ImportError("Boehm (libgc) not found")
+            boehmlib = util.load_library(path)
+        except ImportError, e:
+            import py
+            py.test.skip(str(e))
+        else:
+            GC_malloc = boehmlib.GC_malloc
+            return cast(GC_malloc, c_void_p).value
+
+# ____________________________________________________________
 
 class StackOpCache:
     INITIAL_STACK_EBP_OFS = -4
@@ -867,11 +1188,57 @@
         self.operations.append(op)
         return op
 
+    def genop_same_as(self, kind, gv_x):
+        if gv_x.is_const:    # must always return a var
+            op = OpSameAs(gv_x)
+            self.operations.append(op)
+            return op
+        else:
+            return gv_x
+
     def genop_call(self, sigtoken, gv_fnptr, args_gv):
         op = OpCall(sigtoken, gv_fnptr, list(args_gv))
         self.operations.append(op)
         return op
 
+    def genop_malloc_fixedsize(self, size):
+        # XXX boehm only, no atomic/non atomic distinction for now
+        op = OpCall(MALLOC_SIGTOKEN,
+                    IntConst(gc_malloc_fnaddr()),
+                    [IntConst(size)])
+        self.operations.append(op)
+        return op
+
+    def genop_malloc_varsize(self, varsizealloctoken, gv_size):
+        # XXX boehm only, no atomic/non atomic distinction for now
+        # XXX no overflow checking for now
+        opsz = OpComputeSize(varsizealloctoken, gv_size)
+        self.operations.append(opsz)
+        opmalloc = OpCall(MALLOC_SIGTOKEN,
+                          IntConst(gc_malloc_fnaddr()),
+                          [opsz])
+        self.operations.append(opmalloc)
+        lengthtoken, _, _ = varsizealloctoken
+        self.operations.append(OpSetField(lengthtoken, opmalloc, opsz))
+        return opmalloc
+
+    def genop_getfield(self, fieldtoken, gv_ptr):
+        op = OpGetField(fieldtoken, gv_ptr)
+        self.operations.append(op)
+        return op
+
+    def genop_setfield(self, fieldtoken, gv_ptr, gv_value):
+        self.operations.append(OpSetField(fieldtoken, gv_ptr, gv_value))
+
+    def genop_getarrayitem(self, arraytoken, gv_array, gv_index):
+        op = OpGetArrayItem(arraytoken, gv_array, gv_index)
+        self.operations.append(op)
+        return op
+
+    def genop_setarrayitem(self, arraytoken, gv_array, gv_index, gv_value):
+        self.operations.append(OpSetArrayItem(arraytoken, gv_array,
+                                              gv_index, gv_value))
+
     def flexswitch(self, gv_exitswitch, args_gv):
         reg = FlexSwitch.REG
         mc = self.generate_block_code(args_gv, [gv_exitswitch], [reg],
@@ -991,16 +1358,16 @@
             arrayfield = T._arrayfld
             ARRAYFIELD = getattr(T, arrayfield)
             arraytoken = RI386GenOp.arrayToken(ARRAYFIELD)
-            length_offset, items_offset, item_size = arraytoken
+            (lengthoffset, lengthsize), itemsoffset, itemsize = arraytoken
             arrayfield_offset = llmemory.offsetof(T, arrayfield)
-            return (arrayfield_offset+length_offset,
-                    arrayfield_offset+items_offset,
-                    item_size)
+            return ((arrayfield_offset+lengthoffset, lengthsize),
+                    arrayfield_offset+itemsoffset,
+                    itemsize)
 
     @staticmethod
     @specialize.memo()    
     def arrayToken(A):
-        return (llmemory.ArrayLengthOffset(A),
+        return ((llmemory.ArrayLengthOffset(A), WORD),
                 llmemory.ArrayItemsOffset(A),
                 llmemory.ItemOffset(A.OF))
 
@@ -1033,3 +1400,5 @@
 
 global_rgenop = RI386GenOp()
 RI386GenOp.constPrebuiltGlobal = global_rgenop.genconst
+
+MALLOC_SIGTOKEN = RI386GenOp.sigToken(GC_MALLOC.TO)

Modified: pypy/branch/i386-regalloc/pypy/jit/codegen/i386/ri386.py
==============================================================================
--- pypy/branch/i386-regalloc/pypy/jit/codegen/i386/ri386.py	(original)
+++ pypy/branch/i386-regalloc/pypy/jit/codegen/i386/ri386.py	Sun Jan 14 14:39:24 2007
@@ -6,6 +6,7 @@
 
 class REG(OPERAND):
     width = 4
+    lowest8bits = None
     def __repr__(self):
         return '<%s>' % self.__class__.__name__.lower()
     def assembler(self):
@@ -123,6 +124,18 @@
         else:
             return unpack(offset)
 
+    def involves_ecx(self):
+        # very custom: is ecx present in this mod/rm?
+        mod = self.byte & 0xC0
+        rm  = self.byte & 0x07
+        if mod != 0xC0 and rm == 4:
+            SIB = ord(self.extradata[0])
+            index = (SIB & 0x38) >> 3
+            base  = (SIB & 0x07)
+            return base == ECX.op or index == ECX.op
+        else:
+            return rm == ECX.op
+
 
 class MODRM8(MODRM):
     width = 1
@@ -164,6 +177,11 @@
 dh = DH()
 bh = BH()
 
+eax.lowest8bits = al
+ecx.lowest8bits = cl
+edx.lowest8bits = dl
+ebx.lowest8bits = bl
+
 registers = [eax, ecx, edx, ebx, esp, ebp, esi, edi]
 registers8 = [al, cl, dl, bl, ah, ch, dh, bh]
 

Modified: pypy/branch/i386-regalloc/pypy/jit/codegen/i386/test/test_operation.py
==============================================================================
--- pypy/branch/i386-regalloc/pypy/jit/codegen/i386/test/test_operation.py	(original)
+++ pypy/branch/i386-regalloc/pypy/jit/codegen/i386/test/test_operation.py	Sun Jan 14 14:39:24 2007
@@ -6,13 +6,16 @@
 from pypy.jit.codegen import graph2rgenop
 from pypy.jit.codegen.i386.rgenop import RI386GenOp
 from pypy.rpython.memory.lltypelayout import convert_offset_to_int
-from pypy.rlib.rarithmetic import r_uint
+from pypy.rlib.rarithmetic import r_uint, intmask
 from ctypes import cast, c_void_p, CFUNCTYPE, c_int, c_float
 from pypy import conftest
 
 def conv(n):
     if not isinstance(n, int):
-        n = convert_offset_to_int(n)
+        if isinstance(n, tuple):
+            n = tuple(map(conv, n))
+        else:
+            n = convert_offset_to_int(n)
     return n
 
 
@@ -25,12 +28,12 @@
     @staticmethod
     @specialize.memo()
     def fieldToken(T, name):
-        return tuple(map(conv, RI386GenOp.fieldToken(T, name)))
+        return conv(RI386GenOp.fieldToken(T, name))
 
     @staticmethod
     @specialize.memo()
     def arrayToken(A):
-        return tuple(map(conv, RI386GenOp.arrayToken(A)))
+        return conv(RI386GenOp.arrayToken(A))
 
     @staticmethod
     @specialize.memo()
@@ -40,7 +43,7 @@
     @staticmethod
     @specialize.memo()
     def varsizeAllocToken(A):
-        return tuple(map(conv, RI386GenOp.varsizeAllocToken(A)))
+        return conv(RI386GenOp.varsizeAllocToken(A))
 
 
 class I386TestBasicMixin(object):
@@ -86,8 +89,10 @@
                    lambda x, y: abs(-x),
                    ]:
             fp = self.rgen(fn, [int, int])
-            assert fp(40, 2) == fn(40, 2)
-            assert fp(25, 3) == fn(25, 3)
+            assert fp(40, 2) == intmask(fn(40, 2))
+            assert fp(25, 3) == intmask(fn(25, 3))
+            assert fp(149, 32) == intmask(fn(149, 32))
+            assert fp(149, 33) == intmask(fn(149, 33))
 
     def test_comparison(self):
         for fn in [lambda x, y: int(x <  y),



More information about the Pypy-commit mailing list