Commits

Alex Gaynor committed bcd6eb9 Merge

Merged inline-virtualref-2: this means that if you force a virtualref (as is done in Python when an exception leaves a frame), if the frame doesn't escape, it still isn't forced to be allocated.

Comments (0)

Files changed (7)

rpython/jit/codewriter/effectinfo.py

     OS_RAW_MALLOC_VARSIZE       = 110
     OS_RAW_FREE                 = 111
 
+    OS_JIT_FORCE_VIRTUAL        = 120
+
     # for debugging:
     _OS_CANRAISE = set([OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL,
-                        OS_RAW_MALLOC_VARSIZE])
+                        OS_RAW_MALLOC_VARSIZE, OS_JIT_FORCE_VIRTUAL])
 
     def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays,
                 write_descrs_fields, write_descrs_arrays,

rpython/jit/codewriter/jtransform.py

         elif oopspec_name == 'jit.isvirtual':
             kind = getkind(args[0].concretetype)
             return SpaceOperation('%s_isvirtual' % kind, args, op.result)
+        elif oopspec_name == 'jit.force_virtual':
+            return self._handle_oopspec_call(op, args, EffectInfo.OS_JIT_FORCE_VIRTUAL, EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE)
         else:
             raise AssertionError("missing support for %r" % oopspec_name)
 

rpython/jit/codewriter/support.py

 import sys
-from rpython.rtyper.lltypesystem import lltype, rclass, rffi, llmemory
-from rpython.rtyper.ootypesystem import ootype
-from rpython.rtyper import rlist
-from rpython.rtyper.lltypesystem import rstr as ll_rstr, rdict as ll_rdict
-from rpython.rtyper.lltypesystem.module import ll_math
-from rpython.rtyper.lltypesystem.lloperation import llop
-from rpython.rtyper.ootypesystem import rdict as oo_rdict
-from rpython.rtyper.llinterp import LLInterpreter
-from rpython.rtyper.extregistry import ExtRegistryEntry
-from rpython.translator.simplify import get_funcobj
-from rpython.translator.unsimplify import split_block
+
+from rpython.annotator import model as annmodel
+from rpython.annotator.policy import AnnotatorPolicy
 from rpython.flowspace.model import Variable, Constant
-from rpython.translator.translator import TranslationContext
-from rpython.annotator.policy import AnnotatorPolicy
-from rpython.annotator import model as annmodel
-from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
 from rpython.jit.metainterp.typesystem import deref
 from rpython.rlib import rgc
-from rpython.rlib.jit import elidable
+from rpython.rlib.jit import elidable, oopspec
 from rpython.rlib.rarithmetic import r_longlong, r_ulonglong, r_uint, intmask
+from rpython.rtyper import rlist
+from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
+from rpython.rtyper.extregistry import ExtRegistryEntry
+from rpython.rtyper.llinterp import LLInterpreter
+from rpython.rtyper.lltypesystem import lltype, rclass, rffi, llmemory, rstr as ll_rstr, rdict as ll_rdict
+from rpython.rtyper.lltypesystem.lloperation import llop
+from rpython.rtyper.lltypesystem.module import ll_math
+from rpython.rtyper.ootypesystem import ootype, rdict as oo_rdict
+from rpython.translator.simplify import get_funcobj
+from rpython.translator.translator import TranslationContext
+from rpython.translator.unsimplify import split_block
+
 
 def getargtypes(annotator, values):
     if values is None:    # for backend tests producing stand-alone exe's
 
 _ll_5_list_ll_arraycopy = rgc.ll_arraycopy
 
+
 @elidable
 def _ll_1_gc_identityhash(x):
     return lltype.identityhash(x)
 
+
 # the following function should not be "@elidable": I can think of
 # a corner case in which id(const) is constant-folded, and then 'const'
 # disappears and is collected too early (possibly causing another object
 def _ll_1_gc_id(ptr):
     return llop.gc_id(lltype.Signed, ptr)
 
+
+@oopspec("jit.force_virtual(inst)")
 def _ll_1_jit_force_virtual(inst):
     return llop.jit_force_virtual(lltype.typeOf(inst), inst)
 

rpython/jit/metainterp/history.py

     def get_all_jitcell_tokens(self):
         tokens = [t() for t in self.jitcell_token_wrefs]
         if None in tokens:
-            assert False, "get_all_jitcell_tokens will not work as "+\
-                          "loops have been freed"
+            assert False, ("get_all_jitcell_tokens will not work as "
+                           "loops have been freed")
         return tokens
-            
-        
 
     def check_history(self, expected=None, **check):
         insns = {}

rpython/jit/metainterp/optimizeopt/virtualize.py

+from rpython.jit.codewriter.effectinfo import EffectInfo
+from rpython.jit.metainterp.executor import execute
 from rpython.jit.codewriter.heaptracker import vtable2descr
-from rpython.jit.metainterp.executor import execute
 from rpython.jit.metainterp.history import Const, ConstInt, BoxInt
 from rpython.jit.metainterp.optimizeopt import optimizer
+from rpython.jit.metainterp.optimizeopt.optimizer import OptValue, REMOVED
 from rpython.jit.metainterp.optimizeopt.util import (make_dispatcher_method,
     descrlist_dict, sort_descrs)
 from rpython.jit.metainterp.resoperation import rop, ResOperation
 from rpython.rlib.objectmodel import we_are_translated
-from rpython.jit.metainterp.optimizeopt.optimizer import OptValue
 
 
 class AbstractVirtualValue(optimizer.OptValue):
         self.make_equal_to(box, vvalue)
         return vvalue
 
+    def optimize_GUARD_NO_EXCEPTION(self, op):
+        if self.last_emitted_operation is REMOVED:
+            return
+        self.emit_operation(op)
+
+    def optimize_GUARD_NOT_FORCED(self, op):
+        if self.last_emitted_operation is REMOVED:
+            return
+        self.emit_operation(op)
+
+    def optimize_CALL_MAY_FORCE(self, op):
+        effectinfo = op.getdescr().get_extra_info()
+        oopspecindex = effectinfo.oopspecindex
+        if oopspecindex == EffectInfo.OS_JIT_FORCE_VIRTUAL:
+            if self._optimize_JIT_FORCE_VIRTUAL(op):
+                return
+        self.emit_operation(op)
+
     def optimize_VIRTUAL_REF(self, op):
         indexbox = op.getarg(1)
         #
         # - set 'virtual_token' to TOKEN_NONE
         args = [op.getarg(0), ConstInt(vrefinfo.TOKEN_NONE)]
         seo(ResOperation(rop.SETFIELD_GC, args, None,
-                         descr = vrefinfo.descr_virtual_token))
+                         descr=vrefinfo.descr_virtual_token))
         # Note that in some cases the virtual in op.getarg(1) has been forced
         # already.  This is fine.  In that case, and *if* a residual
         # CALL_MAY_FORCE suddenly turns out to access it, then it will
         # will work too (but just be a little pointless, as the structure
         # was already forced).
 
+    def _optimize_JIT_FORCE_VIRTUAL(self, op):
+        vref = self.getvalue(op.getarg(1))
+        vrefinfo = self.optimizer.metainterp_sd.virtualref_info
+        if vref.is_virtual():
+            tokenvalue = vref.getfield(vrefinfo.descr_virtual_token, None)
+            if (tokenvalue is not None and tokenvalue.is_constant() and
+                tokenvalue.box.getint() == vrefinfo.TOKEN_NONE):
+                forcedvalue = vref.getfield(vrefinfo.descr_forced, None)
+                if forcedvalue is not None and not forcedvalue.is_null():
+                    self.make_equal_to(op.result, forcedvalue)
+                    self.last_emitted_operation = REMOVED
+                    return True
+        return False
+
     def optimize_GETFIELD_GC(self, op):
         value = self.getvalue(op.getarg(0))
         # If this is an immutable field (as indicated by op.is_always_pure())

rpython/jit/metainterp/test/support.py

 
 class JitMixin:
     basic = True
+
     def check_resops(self, expected=None, **check):
         get_stats().check_resops(expected=expected, **check)
+
     def check_simple_loop(self, expected=None, **check):
         get_stats().check_simple_loop(expected=expected, **check)
 
-    
-
     def check_trace_count(self, count): # was check_loop_count
         # The number of traces compiled
         assert get_stats().compiled_count == count
+
     def check_trace_count_at_most(self, count):
         assert get_stats().compiled_count <= count
 
 
     def check_target_token_count(self, count):
         tokens = get_stats().get_all_jitcell_tokens()
-        n = sum ([len(t.target_tokens) for t in tokens])
+        n = sum([len(t.target_tokens) for t in tokens])
         assert n == count
 
     def check_enter_count(self, count):
         assert get_stats().enter_count == count
+
     def check_enter_count_at_most(self, count):
         assert get_stats().enter_count <= count
 
 
     def check_aborted_count(self, count):
         assert get_stats().aborted_count == count
+
     def check_aborted_count_at_least(self, count):
         assert get_stats().aborted_count >= count
 

rpython/jit/metainterp/test/test_virtualref.py

 import py
-from rpython.rtyper.lltypesystem import lltype, llmemory, lloperation
+
+from rpython.rtyper.lltypesystem import lltype, lloperation
 from rpython.rtyper.exceptiondata import UnknownException
 from rpython.rlib.jit import JitDriver, dont_look_inside, vref_None
 from rpython.rlib.jit import virtual_ref, virtual_ref_finish, InvalidVirtualRef
 from rpython.rlib.jit import non_virtual_ref
 from rpython.rlib.objectmodel import compute_unique_id
-from rpython.jit.metainterp.test.support import LLJitMixin, OOJitMixin, _get_jitcodes
+from rpython.jit.metainterp.test.support import LLJitMixin, _get_jitcodes
 from rpython.jit.metainterp.resoperation import rop
 from rpython.jit.metainterp.virtualref import VirtualRefInfo
 
+
 debug_print = lloperation.llop.debug_print
 
 
-class VRefTests:
-
+class VRefTests(object):
     def finish_setup_for_interp_operations(self):
         self.vrefinfo = VirtualRefInfo(self.warmrunnerstate)
         self.cw.setup_vrefinfo(self.vrefinfo)
         from rpython.jit.metainterp.resume import ResumeDataDirectReader
         cpu = self.metainterp.cpu
         cpu.get_latest_value_count = lambda df: len(guard_op.getfailargs())
-        cpu.get_latest_value_int = lambda df,i:guard_op.getfailargs()[i].getint()
-        cpu.get_latest_value_ref = lambda df,i:guard_op.getfailargs()[i].getref_base()
+        cpu.get_latest_value_int = lambda df, i: guard_op.getfailargs()[i].getint()
+        cpu.get_latest_value_ref = lambda df, i: guard_op.getfailargs()[i].getref_base()
         cpu.clear_latest_values = lambda count: None
         class FakeMetaInterpSd:
             callinfocollection = None
         self.check_aborted_count(0)
 
     def test_jit_force_virtual_seen(self):
-        myjitdriver = JitDriver(greens = [], reds = ['n'])
-        #
+        myjitdriver = JitDriver(greens=[], reds=['n'])
+
         A = lltype.GcArray(lltype.Signed)
-        class XY:
+
+        class XY(object):
             pass
-        class ExCtx:
+
+        class ExCtx(object):
             pass
         exctx = ExCtx()
-        #
+        escapes = []
+
         def f(n):
             while n > 0:
                 myjitdriver.can_enter_jit(n=n)
                 xy = XY()
                 xy.n = n
                 exctx.topframeref = vref = virtual_ref(xy)
+                escapes.append(xy)
                 xy.next1 = lltype.malloc(A, 0)
                 n = exctx.topframeref().n - 1
-                xy.next1 = lltype.nullptr(A)
                 exctx.topframeref = vref_None
                 virtual_ref_finish(vref, xy)
             return 1
         #
         res = self.meta_interp(f, [15])
         assert res == 1
-        self.check_resops(new_with_vtable=4,     # vref, xy
+        self.check_resops(new_with_vtable=2,     # xy
                           new_array=2)           # next1
         self.check_aborted_count(0)
 
         res = self.meta_interp(f, [10])
         assert res == 0
 
+    def test_force_virtual_vref(self):
+        myjitdriver = JitDriver(greens=[], reds=['n', 'ec'])
+
+        class ExecutionContext(object):
+            pass
+
+        class Frame(object):
+            def __init__(self, x):
+                self.x = x
+
+        def f(n):
+            ec = ExecutionContext()
+            while n > 0:
+                myjitdriver.jit_merge_point(n=n, ec=ec)
+                frame = Frame(1)
+                ec.topframeref = virtual_ref(frame)
+                n -= ec.topframeref().x
+                frame_vref = ec.topframeref
+                ec.topframeref = vref_None
+                virtual_ref_finish(frame_vref, frame)
+            return n
+        res = self.meta_interp(f, [10])
+        assert res == 0
+        self.check_resops({
+            'int_sub': 2, 'int_gt': 2, 'jump': 1, 'guard_true': 2,
+            'force_token': 2, 'setfield_gc': 1
+        })
+
 
 class TestLLtype(VRefTests, LLJitMixin):
     pass