Commits

Hakan Ardo  committed 1140c50 Merge

hg merge jit-short_from_state

  • Participants
  • Parent commits 3cb0372, 781d749
  • Branches jit-str_in_preamble

Comments (0)

Files changed (11)

File pypy/interpreter/baseobjspace.py

         """Unpack an iterable object into a real (interpreter-level) list.
         Raise an OperationError(w_ValueError) if the length is wrong."""
         w_iterator = self.iter(w_iterable)
-        items = []
+        # If we know the expected length we can preallocate.
+        if expected_length == -1:
+            items = []
+        else:
+            items = [None] * expected_length
+        idx = 0
         while True:
             try:
                 w_item = self.next(w_iterator)
                 if not e.match(self, self.w_StopIteration):
                     raise
                 break  # done
-            if expected_length != -1 and len(items) == expected_length:
+            if expected_length != -1 and idx == expected_length:
                 raise OperationError(self.w_ValueError,
                                      self.wrap("too many values to unpack"))
-            items.append(w_item)
-        if expected_length != -1 and len(items) < expected_length:
-            i = len(items)
-            if i == 1:
+            if expected_length == -1:
+                items.append(w_item)
+            else:
+                items[idx] = w_item
+            idx += 1
+        if expected_length != -1 and idx < expected_length:
+            if idx == 1:
                 plural = ""
             else:
                 plural = "s"
             raise OperationError(self.w_ValueError,
                       self.wrap("need more than %d value%s to unpack" %
-                                (i, plural)))
+                                (idx, plural)))
         return items
 
     unpackiterable_unroll = jit.unroll_safe(func_with_new_name(unpackiterable,

File pypy/jit/metainterp/optimizeopt/optimizer.py

     def get_key_box(self):
         return self.box
 
-    def enum_forced_boxes(self, boxes, already_seen):
-        key = self.get_key_box()
-        if key not in already_seen:
-            boxes.append(self.force_box())
-            already_seen[self.get_key_box()] = None
-
     def get_cloned(self, optimizer, valuemap, force_if_needed=True):
         if self in valuemap:
             return valuemap[self]

File pypy/jit/metainterp/optimizeopt/unroll.py

 from pypy.jit.metainterp.optimizeopt.optimizer import *
-from pypy.jit.metainterp.optimizeopt.virtualize import AbstractVirtualValue
+from pypy.jit.metainterp.optimizeopt.virtualstate import VirtualStateAdder
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.jit.metainterp.compile import ResumeGuardDescr
 from pypy.jit.metainterp.resume import Snapshot
         self.snapshot_map[snapshot] = new_snapshot
         return new_snapshot
 
-class VirtualState(object):
-    def __init__(self, state):
-        self.state = state
-
-    def generalization_of(self, other):
-        assert len(self.state) == len(other.state)
-        for i in range(len(self.state)):
-            if not self.state[i].generalization_of(other.state[i]):
-                return False
-        return True
-
-    def generate_guards(self, other, args, cpu, extra_guards):        
-        assert len(self.state) == len(other.state) == len(args)
-        for i in range(len(self.state)):
-            self.state[i].generate_guards(other.state[i], args[i],
-                                          cpu, extra_guards)
-
-class VirtualStateAdder(resume.ResumeDataVirtualAdder):
-    def __init__(self, optimizer):
-        self.fieldboxes = {}
-        self.optimizer = optimizer
-        self.info = {}
-
-    def register_virtual_fields(self, keybox, fieldboxes):
-        self.fieldboxes[keybox] = fieldboxes
-        
-    def already_seen_virtual(self, keybox):
-        return keybox in self.fieldboxes
-
-    def getvalue(self, box):
-        return self.optimizer.getvalue(box)
-
-    def state(self, box):
-        value = self.getvalue(box)
-        box = value.get_key_box()
-        try:
-            info = self.info[box]
-        except KeyError:
-            if value.is_virtual():
-                self.info[box] = info = value.make_virtual_info(self, None)
-                flds = self.fieldboxes[box]
-                info.fieldstate = [self.state(b) for b in flds]
-            else:
-                self.info[box] = info = self.make_not_virtual(value)
-        return info
-
-    def get_virtual_state(self, jump_args):
-        for box in jump_args:
-            value = self.getvalue(box)
-            value.get_args_for_fail(self)
-        return VirtualState([self.state(box) for box in jump_args])
-
-
-    def make_not_virtual(self, value):
-        return NotVirtualInfo(value)
-
-class NotVirtualInfo(resume.AbstractVirtualInfo):
-    def __init__(self, value):
-        self.known_class = value.known_class
-        self.level = value.level
-        if value.intbound is None:
-            self.intbound = IntBound(MININT, MAXINT)
-        else:
-            self.intbound = value.intbound.clone()
-        if value.is_constant():
-            self.constbox = value.box
-        else:
-            self.constbox = None
-
-    def generalization_of(self, other):
-        # XXX This will always retrace instead of forcing anything which
-        # might be what we want sometimes?
-        if not isinstance(other, NotVirtualInfo):
-            return False
-        if other.level < self.level:
-            return False
-        if self.level == LEVEL_CONSTANT:
-            if not self.constbox.same_constant(other.constbox):
-                return False
-        elif self.level == LEVEL_KNOWNCLASS:
-            if self.known_class != other.known_class: # FIXME: use issubclass?
-                return False
-        return self.intbound.contains_bound(other.intbound)
-
-    def _generate_guards(self, other, box, cpu, extra_guards):
-        if not isinstance(other, NotVirtualInfo):
-            raise InvalidLoop
-        if self.level == LEVEL_KNOWNCLASS and \
-           box.nonnull() and \
-           self.known_class.same_constant(cpu.ts.cls_of_box(box)):
-            # Note: This is only a hint on what the class of box was
-            # during the trace. There are actually no guarentees that this
-            # box realy comes from a trace. The hint is used here to choose
-            # between either eimtting a guard_class and jumping to an
-            # excisting compiled loop or retracing the loop. Both
-            # alternatives will always generate correct behaviour, but
-            # performace will differ.
-            op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
-            extra_guards.append(op)
-            return
-        # Remaining cases are probably not interesting
-        raise InvalidLoop
-        if self.level == LEVEL_CONSTANT:
-            import pdb; pdb.set_trace()
-            raise NotImplementedError
-        
-
 class UnrollOptimizer(Optimization):
     """Unroll the loop into two iterations. The first one will
     become the preamble or entry bridge (don't think there is a
 
             try:
                 inputargs = self.inline(self.cloned_operations,
-                                        loop.inputargs, jump_args)
+                                        loop.inputargs, jump_args,
+                                        virtual_state)
             except KeyError:
                 debug_print("Unrolling failed.")
                 loop.preamble.operations = None
                     if op.result:
                         op.result.forget_value()
                 
-    def inline(self, loop_operations, loop_args, jump_args):
+    def inline(self, loop_operations, loop_args, jump_args, virtual_state):
         self.inliner = inliner = Inliner(loop_args, jump_args)
-           
+
+        # FIXME: Move this to reconstruct
         for v in self.optimizer.values.values():
             v.last_guard_index = -1 # FIXME: Are there any more indexes stored?
 
-        inputargs = []
-        seen_inputargs = {}
-        for arg in jump_args:
-            boxes = []
-            self.getvalue(arg).enum_forced_boxes(boxes, seen_inputargs)
-            for a in boxes:
-                if not isinstance(a, Const):
-                    inputargs.append(a)
+        values = [self.getvalue(arg) for arg in jump_args]
+        inputargs = virtual_state.make_inputargs(values)
 
         # This loop is equivalent to the main optimization loop in
         # Optimizer.propagate_all_forward
         for newop in loop_operations:
+            newop = inliner.inline_op(newop, clone=False)
             if newop.getopnum() == rop.JUMP:
-                newop.initarglist(inputargs)
-            newop = inliner.inline_op(newop, clone=False)
+                values = [self.getvalue(arg) for arg in newop.getarglist()]
+                newop.initarglist(virtual_state.make_inputargs(values))
 
-            self.optimizer.first_optimization.propagate_forward(newop)
+            #self.optimizer.first_optimization.propagate_forward(newop)
+            self.optimizer.send_extra_operation(newop)
 
         # Remove jump to make sure forced code are placed before it
         newoperations = self.optimizer.newoperations

File pypy/jit/metainterp/optimizeopt/virtualize.py

                 fieldvalue = self._fields[ofs]
                 fieldvalue.get_args_for_fail(modifier)
 
-    def enum_forced_boxes(self, boxes, already_seen):
-        key = self.get_key_box()
-        if key in already_seen:
-            return
-        already_seen[key] = None
-        if self.box is None:
-            lst = self._get_field_descr_list()
-            for ofs in lst:
-                self._fields[ofs].enum_forced_boxes(boxes, already_seen)
-        else:
-            boxes.append(self.box)
-
     def clone_for_next_iteration(self, optimizer):
         raise NotImplementedError
 
     def _make_virtual(self, modifier):
         return modifier.make_varray(self.arraydescr)
 
-    def enum_forced_boxes(self, boxes, already_seen):
-        key = self.get_key_box()
-        if key in already_seen:
-            return
-        already_seen[key] = None
-        if self.box is None:
-            for itemvalue in self._items:
-                itemvalue.enum_forced_boxes(boxes, already_seen)
-        else:
-            boxes.append(self.box)
-
     def clone_for_next_iteration(self, optimizer):
         new = VArrayValue(optimizer, self.arraydescr, len(self._items),
                           self.keybox, self.source_op)

File pypy/jit/metainterp/optimizeopt/virtualstate.py

+from pypy.jit.metainterp import resume
+from pypy.jit.metainterp.optimizeopt import virtualize
+from pypy.jit.metainterp.optimizeopt.optimizer import LEVEL_CONSTANT, \
+                                                      LEVEL_KNOWNCLASS, \
+                                                      MININT, MAXINT
+from pypy.jit.metainterp.optimizeutil import InvalidLoop
+from pypy.jit.metainterp.optimizeopt.intutils import IntBound
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+
+class AbstractVirtualStateInfo(resume.AbstractVirtualInfo):
+    def generalization_of(self, other):
+        raise NotImplementedError
+
+    def generate_guards(self, other, box, cpu, extra_guards):
+        if self.generalization_of(other):
+            return
+        self._generate_guards(other, box, cpu, extra_guards)
+
+    def _generate_guards(self, other, box, cpu, extra_guards):
+        raise InvalidLoop
+
+    def enum_forced_boxes(self, boxes, already_seen, value):
+        raise NotImplementedError
+    
+class AbstractVirtualStructStateInfo(AbstractVirtualStateInfo):
+    def __init__(self, fielddescrs):
+        self.fielddescrs = fielddescrs
+
+    def generalization_of(self, other):
+        if not self._generalization_of(other):
+            return False
+        assert len(self.fielddescrs) == len(self.fieldstate)
+        assert len(other.fielddescrs) == len(other.fieldstate)
+        if len(self.fielddescrs) != len(other.fielddescrs):
+            return False
+        
+        for i in range(len(self.fielddescrs)):
+            if other.fielddescrs[i] is not self.fielddescrs[i]:
+                return False
+            if not self.fieldstate[i].generalization_of(other.fieldstate[i]):
+                return False
+
+        return True
+
+    def _generalization_of(self, other):
+        raise NotImplementedError
+
+    def enum_forced_boxes(self, boxes, already_seen, value):
+        assert isinstance(value, virtualize.AbstractVirtualStructValue)
+        key = value.get_key_box()
+        if key in already_seen:
+            return
+        already_seen[key] = None
+        if value.box is None:
+            for i in range(len(self.fielddescrs)):
+                v = value._fields[self.fielddescrs[i]]
+                self.fieldstate[i].enum_forced_boxes(boxes, already_seen, v)
+        else:
+            boxes.append(value.box)
+        
+class VirtualStateInfo(AbstractVirtualStructStateInfo):
+    def __init__(self, known_class, fielddescrs):
+        AbstractVirtualStructStateInfo.__init__(self, fielddescrs)
+        self.known_class = known_class
+
+    def _generalization_of(self, other):        
+        if not isinstance(other, VirtualStateInfo):
+            return False
+        if not self.known_class.same_constant(other.known_class):
+            return False
+        return True
+        
+class VStructStateInfo(AbstractVirtualStructStateInfo):
+    def __init__(self, typedescr, fielddescrs):
+        AbstractVirtualStructStateInfo.__init__(self, fielddescrs)
+        self.typedescr = typedescr
+
+    def _generalization_of(self, other):        
+        if not isinstance(other, VStructStateInfo):
+            return False
+        if self.typedescr is not other.typedescr:
+            return False
+        return True
+        
+class VArrayStateInfo(AbstractVirtualStateInfo):
+    def __init__(self, arraydescr):
+        self.arraydescr = arraydescr
+
+    def generalization_of(self, other):
+        if self.arraydescr is not other.arraydescr:
+            return False
+        if len(self.fieldstate) != len(other.fieldstate):
+            return False
+        for i in range(len(self.fieldstate)):
+            if not self.fieldstate[i].generalization_of(other.fieldstate[i]):
+                return False
+        return True
+
+    def enum_forced_boxes(self, boxes, already_seen, value):
+        assert isinstance(value, virtualize.VArrayValue)
+        key = value.get_key_box()
+        if key in already_seen:
+            return
+        already_seen[key] = None
+        if value.box is None:
+            for i in range(len(self.fieldstate)):
+                v = value._items[i]
+                self.fieldstate[i].enum_forced_boxes(boxes, already_seen, v)
+        else:
+            boxes.append(value.box)
+
+class NotVirtualStateInfo(AbstractVirtualStateInfo):
+    def __init__(self, value):
+        self.known_class = value.known_class
+        self.level = value.level
+        if value.intbound is None:
+            self.intbound = IntBound(MININT, MAXINT)
+        else:
+            self.intbound = value.intbound.clone()
+        if value.is_constant():
+            self.constbox = value.box
+        else:
+            self.constbox = None
+
+    def generalization_of(self, other):
+        # XXX This will always retrace instead of forcing anything which
+        # might be what we want sometimes?
+        if not isinstance(other, NotVirtualStateInfo):
+            return False
+        if other.level < self.level:
+            return False
+        if self.level == LEVEL_CONSTANT:
+            if not self.constbox.same_constant(other.constbox):
+                return False
+        elif self.level == LEVEL_KNOWNCLASS:
+            if self.known_class != other.known_class: # FIXME: use issubclass?
+                return False
+        return self.intbound.contains_bound(other.intbound)
+
+    def _generate_guards(self, other, box, cpu, extra_guards):
+        if not isinstance(other, NotVirtualStateInfo):
+            raise InvalidLoop
+        if self.level == LEVEL_KNOWNCLASS and \
+           box.nonnull() and \
+           self.known_class.same_constant(cpu.ts.cls_of_box(box)):
+            # Note: This is only a hint on what the class of box was
+            # during the trace. There are actually no guarentees that this
+            # box realy comes from a trace. The hint is used here to choose
+            # between either eimtting a guard_class and jumping to an
+            # excisting compiled loop or retracing the loop. Both
+            # alternatives will always generate correct behaviour, but
+            # performace will differ.
+            op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
+            extra_guards.append(op)
+            return
+        # Remaining cases are probably not interesting
+        raise InvalidLoop
+        if self.level == LEVEL_CONSTANT:
+            import pdb; pdb.set_trace()
+            raise NotImplementedError
+
+    def enum_forced_boxes(self, boxes, already_seen, value):
+        if self.level == LEVEL_CONSTANT:
+            return
+        key = value.get_key_box()
+        if key not in already_seen:
+            boxes.append(value.force_box())
+            already_seen[value.get_key_box()] = None
+        
+
+class VirtualState(object):
+    def __init__(self, state):
+        self.state = state
+
+    def generalization_of(self, other):
+        assert len(self.state) == len(other.state)
+        for i in range(len(self.state)):
+            if not self.state[i].generalization_of(other.state[i]):
+                return False
+        return True
+
+    def generate_guards(self, other, args, cpu, extra_guards):        
+        assert len(self.state) == len(other.state) == len(args)
+        for i in range(len(self.state)):
+            self.state[i].generate_guards(other.state[i], args[i],
+                                          cpu, extra_guards)
+
+    def make_inputargs(self, values):
+        assert len(values) == len(self.state)
+        inputargs = []
+        seen_inputargs = {}
+        for i in range(len(values)):
+            self.state[i].enum_forced_boxes(inputargs, seen_inputargs,
+                                            values[i])
+        return inputargs
+        
+
+class VirtualStateAdder(resume.ResumeDataVirtualAdder):
+    def __init__(self, optimizer):
+        self.fieldboxes = {}
+        self.optimizer = optimizer
+        self.info = {}
+
+    def register_virtual_fields(self, keybox, fieldboxes):
+        self.fieldboxes[keybox] = fieldboxes
+        
+    def already_seen_virtual(self, keybox):
+        return keybox in self.fieldboxes
+
+    def getvalue(self, box):
+        return self.optimizer.getvalue(box)
+
+    def state(self, box):
+        value = self.getvalue(box)
+        box = value.get_key_box()
+        try:
+            info = self.info[box]
+        except KeyError:
+            if value.is_virtual():
+                self.info[box] = info = value.make_virtual_info(self, None)
+                flds = self.fieldboxes[box]
+                info.fieldstate = [self.state(b) for b in flds]
+            else:
+                self.info[box] = info = self.make_not_virtual(value)
+        return info
+
+    def get_virtual_state(self, jump_args):
+        for box in jump_args:
+            value = self.getvalue(box)
+            value.get_args_for_fail(self)
+        return VirtualState([self.state(box) for box in jump_args])
+
+
+    def make_not_virtual(self, value):
+        return NotVirtualStateInfo(value)
+
+    def make_virtual(self, known_class, fielddescrs):
+        return VirtualStateInfo(known_class, fielddescrs)
+
+    def make_vstruct(self, typedescr, fielddescrs):
+        return VStructStateInfo(typedescr, fielddescrs)
+
+    def make_varray(self, arraydescr):
+        return VArrayStateInfo(arraydescr)
+

File pypy/jit/metainterp/resume.py

 
     def debug_prints(self):
         raise NotImplementedError
-
-    def generalization_of(self, other):
-        raise NotImplementedError
-
-    def generate_guards(self, other, box, cpu, extra_guards):
-        if self.generalization_of(other):
-            return
-        self._generate_guards(other, box, cpu, extra_guards)
-
-    def _generate_guards(self, other, box, cpu, extra_guards):
-        raise InvalidLoop
         
 class AbstractVirtualStructInfo(AbstractVirtualInfo):
     def __init__(self, fielddescrs):
                         str(self.fielddescrs[i]),
                         str(untag(self.fieldnums[i])))
 
-    def generalization_of(self, other):
-        if not self._generalization_of(other):
-            return False
-        assert len(self.fielddescrs) == len(self.fieldstate)
-        assert len(other.fielddescrs) == len(other.fieldstate)
-        if len(self.fielddescrs) != len(other.fielddescrs):
-            return False
-        
-        for i in range(len(self.fielddescrs)):
-            if other.fielddescrs[i] is not self.fielddescrs[i]:
-                return False
-            if not self.fieldstate[i].generalization_of(other.fieldstate[i]):
-                return False
-
-        return True
-
-    def _generalization_of(self, other):
-        raise NotImplementedError
-
-
 class VirtualInfo(AbstractVirtualStructInfo):
     def __init__(self, known_class, fielddescrs):
         AbstractVirtualStructInfo.__init__(self, fielddescrs)
         debug_print("\tvirtualinfo", self.known_class.repr_rpython())
         AbstractVirtualStructInfo.debug_prints(self)
 
-    def _generalization_of(self, other):        
-        if not isinstance(other, VirtualInfo):
-            return False
-        if not self.known_class.same_constant(other.known_class):
-            return False
-        return True
-        
 
 class VStructInfo(AbstractVirtualStructInfo):
     def __init__(self, typedescr, fielddescrs):
         debug_print("\tvstructinfo", self.typedescr.repr_rpython())
         AbstractVirtualStructInfo.debug_prints(self)
 
-    def _generalization_of(self, other):        
-        if not isinstance(other, VStructInfo):
-            return False
-        if self.typedescr is not other.typedescr:
-            return False
-        return True
-        
-
 class VArrayInfo(AbstractVirtualInfo):
     def __init__(self, arraydescr):
         self.arraydescr = arraydescr
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
-    def generalization_of(self, other):
-        if self.arraydescr is not other.arraydescr:
-            return False
-        if len(self.fieldstate) != len(other.fieldstate):
-            return False
-        for i in range(len(self.fieldstate)):
-            if not self.fieldstate[i].generalization_of(other.fieldstate[i]):
-                return False
-        return True
-
-
 class VStrPlainInfo(AbstractVirtualInfo):
     """Stands for the string made out of the characters of all fieldnums."""
 

File pypy/jit/metainterp/test/test_optimizeopt.py

         i4 = int_sub(i2, i1)
         jump(p1, i1, i2, i4)
         """
-        #self.optimize_strunicode_loop(ops, expected, expected)
-        self.optimize_loop(ops, expected)
+        self.optimize_strunicode_loop(ops, expected, expected)
 
     def test_str_slice_len_surviving2(self):
         ops = """

File pypy/module/pypyjit/test_pypy_c/model.py

     @classmethod
     def is_const(cls, v1):
         return isinstance(v1, str) and v1.startswith('ConstClass(')
-    
+
     def match_var(self, v1, exp_v2):
         assert v1 != '_'
         if exp_v2 == '_':
         for arg, exp_arg in zip(op.args, exp_args):
             self._assert(self.match_var(arg, exp_arg), "variable mismatch: %r instead of %r" % (arg, exp_arg))
         self.match_descr(op.descr, exp_descr)
-        
+
 
     def _next_op(self, iter_ops, assert_raises=False):
         try:

File pypy/module/pypyjit/test_pypy_c/test_pypy_c_new.py

             --TICK--
             jump(p0, p1, p2, p3, p4, i21, i6, i7, p8, p9, descr=<Loop0>)
         """)
+
+    def test_unpack_iterable_non_list_tuple(self):
+        def main(n):
+            import array
+
+            items = [array.array("i", [1])] * n
+            total = 0
+            for a, in items:
+                total += a
+            return total
+
+        log = self.run(main, [1000000])
+        assert log.result == 1000000
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match("""
+            i16 = int_ge(i12, i13)
+            guard_false(i16, descr=<Guard3>)
+            p17 = getarrayitem_gc(p15, i12, descr=<GcPtrArrayDescr>)
+            i19 = int_add(i12, 1)
+            setfield_gc(p4, i19, descr=<SignedFieldDescr .*W_AbstractSeqIterObject.inst_index .*>)
+            guard_nonnull_class(p17, 146982464, descr=<Guard4>)
+            i21 = getfield_gc(p17, descr=<SignedFieldDescr .*W_ArrayTypei.inst_len .*>)
+            i23 = int_lt(0, i21)
+            guard_true(i23, descr=<Guard5>)
+            i24 = getfield_gc(p17, descr=<NonGcPtrFieldDescr .*W_ArrayTypei.inst_buffer .*>)
+            i25 = getarrayitem_raw(i24, 0, descr=<SignedArrayNoLengthDescr>)
+            i27 = int_lt(1, i21)
+            guard_false(i27, descr=<Guard6>)
+            i28 = int_add_ovf(i10, i25)
+            guard_no_overflow(descr=<Guard7>)
+            --TICK--
+            jump(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, i28, i25, i19, i13, p14, p15, descr=<Loop0>)
+        """)

File pypy/rpython/lltypesystem/module/ll_math.py

 from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.tool.sourcetools import func_with_new_name
 from pypy.tool.autopath import pypydir
-from pypy.rlib import rposix
+from pypy.rlib import jit, rposix
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
 from pypy.rlib.rfloat import isinf, isnan, INFINITY, NAN
 
 #
 # Custom implementations
 
-
+@jit.purefunction
 def ll_math_isnan(y):
     return bool(math_isnan(y))
 
-
+@jit.purefunction
 def ll_math_isinf(y):
     return bool(math_isinf(y))
 

File pypy/rpython/lltypesystem/rffi.py

     eci = ExternalCompilationInfo(post_include_bits=['#define PYPY_NO_OP()'])
     eci = eci.merge(compilation_info)
     return llexternal('PYPY_NO_OP', [], lltype.Void,
-                      compilation_info=eci, sandboxsafe=True, _nowrapper=True)
+                      compilation_info=eci, sandboxsafe=True, _nowrapper=True,
+                      _callable=lambda: None)
 
 # ____________________________________________________________
 # Few helpers for keeping callback arguments alive