Esempio n. 1
0
def call_final_function(translator, final_func, annhelper=None):
    """When the program finishes normally, call 'final_func()'."""
    from rpython.annotator import model as annmodel
    from rpython.rtyper.lltypesystem import lltype
    from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator

    own_annhelper = (annhelper is None)
    if own_annhelper:
        annhelper = MixLevelHelperAnnotator(translator.rtyper)
    c_final_func = annhelper.constfunc(final_func, [], annmodel.s_None)
    if own_annhelper:
        annhelper.finish()

    entry_point = translator.entry_point_graph
    v = copyvar(translator.annotator, entry_point.getreturnvar())
    extrablock = Block([v])
    v_none = varoftype(lltype.Void)
    newop = SpaceOperation('direct_call', [c_final_func], v_none)
    extrablock.operations = [newop]
    extrablock.closeblock(Link([v], entry_point.returnblock))
    for block in entry_point.iterblocks():
        if block is not extrablock:
            for link in block.exits:
                if link.target is entry_point.returnblock:
                    link.target = extrablock
    checkgraph(entry_point)
Esempio n. 2
0
 def make_driverhook_graphs(self):
     from rpython.rlib.jit import BaseJitCell
     bk = self.rtyper.annotator.bookkeeper
     classdef = bk.getuniqueclassdef(BaseJitCell)
     s_BaseJitCell_or_None = annmodel.SomeInstance(classdef,
                                                   can_be_None=True)
     s_BaseJitCell_not_None = annmodel.SomeInstance(classdef)
     s_Str = annmodel.SomeString()
     #
     annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
     for jd in self.jitdrivers_sd:
         jd._set_jitcell_at_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.set_jitcell_at, annmodel.s_None,
             s_BaseJitCell_not_None)
         jd._get_jitcell_at_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.get_jitcell_at, s_BaseJitCell_or_None)
         jd._get_printable_location_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.get_printable_location, s_Str)
         jd._confirm_enter_jit_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.confirm_enter_jit, annmodel.s_Bool,
             onlygreens=False)
         jd._can_never_inline_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.can_never_inline, annmodel.s_Bool)
         jd._should_unroll_one_iteration_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.should_unroll_one_iteration,
             annmodel.s_Bool)
     annhelper.finish()
Esempio n. 3
0
 def build_cpu(self,
               CPUClass,
               translate_support_code=False,
               no_stats=False,
               supports_floats=True,
               supports_longlong=True,
               supports_singlefloats=True,
               **kwds):
     assert CPUClass is not None
     self.opt = history.Options(**kwds)
     if no_stats:
         stats = history.NoStats()
     else:
         stats = history.Stats(None)
     self.stats = stats
     if translate_support_code:
         self.annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
     cpu = CPUClass(self.translator.rtyper,
                    self.stats,
                    self.opt,
                    translate_support_code,
                    gcdescr=self.gcdescr)
     if not supports_floats:
         cpu.supports_floats = False
     if not supports_longlong:
         cpu.supports_longlong = False
     if not supports_singlefloats:
         cpu.supports_singlefloats = False
     self.cpu = cpu
Esempio n. 4
0
def builtin_func_for_spec(rtyper, oopspec_name, ll_args, ll_res,
                          extra=None, extrakey=None):
    assert (extra is None) == (extrakey is None)
    key = (oopspec_name, tuple(ll_args), ll_res, extrakey)
    try:
        return rtyper._builtin_func_for_spec_cache[key]
    except (KeyError, AttributeError):
        pass
    args_s = [lltype_to_annotation(v) for v in ll_args]
    if '.' not in oopspec_name:    # 'newxxx' operations
        LIST_OR_DICT = ll_res
    else:
        LIST_OR_DICT = ll_args[0]
    s_result = lltype_to_annotation(ll_res)
    impl = setup_extra_builtin(rtyper, oopspec_name, len(args_s), extra)
    if getattr(impl, 'need_result_type', False):
        bk = rtyper.annotator.bookkeeper
        args_s.insert(0, annmodel.SomePBC([bk.getdesc(deref(ll_res))]))
    #
    if hasattr(rtyper, 'annotator'):  # regular case
        mixlevelann = MixLevelHelperAnnotator(rtyper)
        c_func = mixlevelann.constfunc(impl, args_s, s_result)
        mixlevelann.finish()
    else:
        # for testing only
        c_func = Constant(oopspec_name,
                          lltype.Ptr(lltype.FuncType(ll_args, ll_res)))
    #
    if not hasattr(rtyper, '_builtin_func_for_spec_cache'):
        rtyper._builtin_func_for_spec_cache = {}
    rtyper._builtin_func_for_spec_cache[key] = (c_func, LIST_OR_DICT)
    #
    return c_func, LIST_OR_DICT
Esempio n. 5
0
def call_final_function(translator, final_func, annhelper=None):
    """When the program finishes normally, call 'final_func()'."""
    from rpython.annotator import model as annmodel
    from rpython.rtyper.lltypesystem import lltype
    from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator

    own_annhelper = (annhelper is None)
    if own_annhelper:
        annhelper = MixLevelHelperAnnotator(translator.rtyper)
    c_final_func = annhelper.constfunc(final_func, [], annmodel.s_None)
    if own_annhelper:
        annhelper.finish()

    entry_point = translator.entry_point_graph
    v = entry_point.getreturnvar().copy()
    extrablock = Block([v])
    v_none = varoftype(lltype.Void)
    newop = SpaceOperation('direct_call', [c_final_func], v_none)
    extrablock.operations = [newop]
    extrablock.closeblock(Link([v], entry_point.returnblock))
    for block in entry_point.iterblocks():
        if block is not extrablock:
            for link in block.exits:
                if link.target is entry_point.returnblock:
                    link.target = extrablock
    checkgraph(entry_point)
def complete_destrptr(gctransformer):
    translator = gctransformer.translator
    mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
    args_s = [lltype_to_annotation(STACKLET_PTR)]
    s_result = annmodel.s_None
    destrptr = mixlevelannotator.delayedfunction(stacklet_destructor, args_s, s_result)
    mixlevelannotator.finish()
    lltype.attachRuntimeTypeInfo(STACKLET, destrptr=destrptr)
Esempio n. 7
0
    def _setup_frame_realloc(self, translate_support_code):
        FUNC_TP = lltype.Ptr(lltype.FuncType([llmemory.GCREF, lltype.Signed],
                                             llmemory.GCREF))
        base_ofs = self.get_baseofs_of_frame_field()

        def realloc_frame(frame, size):
            try:
                if not we_are_translated():
                    assert not self._exception_emulator[0]
                frame = lltype.cast_opaque_ptr(jitframe.JITFRAMEPTR, frame)
                if size > frame.jf_frame_info.jfi_frame_depth:
                    # update the frame_info size, which is for whatever reason
                    # not up to date
                    frame.jf_frame_info.update_frame_depth(base_ofs, size)
                new_frame = jitframe.JITFRAME.allocate(frame.jf_frame_info)
                frame.jf_forward = new_frame
                i = 0
                while i < len(frame.jf_frame):
                    new_frame.jf_frame[i] = frame.jf_frame[i]
                    frame.jf_frame[i] = 0
                    i += 1
                new_frame.jf_savedata = frame.jf_savedata
                new_frame.jf_guard_exc = frame.jf_guard_exc
                # all other fields are empty
                llop.gc_writebarrier(lltype.Void, new_frame)
                return lltype.cast_opaque_ptr(llmemory.GCREF, new_frame)
            except Exception as e:
                print "Unhandled exception", e, "in realloc_frame"
                return lltype.nullptr(llmemory.GCREF.TO)

        def realloc_frame_crash(frame, size):
            print "frame", frame, "size", size
            return lltype.nullptr(llmemory.GCREF.TO)

        if not translate_support_code:
            fptr = llhelper(FUNC_TP, realloc_frame)
        else:
            FUNC = FUNC_TP.TO
            args_s = [lltype_to_annotation(ARG) for ARG in FUNC.ARGS]
            s_result = lltype_to_annotation(FUNC.RESULT)
            mixlevelann = MixLevelHelperAnnotator(self.rtyper)
            graph = mixlevelann.getgraph(realloc_frame, args_s, s_result)
            fptr = mixlevelann.graph2delayed(graph, FUNC)
            mixlevelann.finish()
        self.realloc_frame = heaptracker.adr2int(llmemory.cast_ptr_to_adr(fptr))

        if not translate_support_code:
            fptr = llhelper(FUNC_TP, realloc_frame_crash)
        else:
            FUNC = FUNC_TP.TO
            args_s = [lltype_to_annotation(ARG) for ARG in FUNC.ARGS]
            s_result = lltype_to_annotation(FUNC.RESULT)
            mixlevelann = MixLevelHelperAnnotator(self.rtyper)
            graph = mixlevelann.getgraph(realloc_frame_crash, args_s, s_result)
            fptr = mixlevelann.graph2delayed(graph, FUNC)
            mixlevelann.finish()
        self.realloc_frame_crash = heaptracker.adr2int(llmemory.cast_ptr_to_adr(fptr))
Esempio n. 8
0
def complete_destrptr(gctransformer):
    translator = gctransformer.translator
    mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
    args_s = [lltype_to_annotation(STACKLET_PTR)]
    s_result = annmodel.s_None
    destrptr = mixlevelannotator.delayedfunction(stacklet_destructor, args_s,
                                                 s_result)
    mixlevelannotator.finish()
    lltype.attachRuntimeTypeInfo(STACKLET, destrptr=destrptr)
Esempio n. 9
0
 def test_conditional_call(self):
     def g():
         pass
     def f(n):
         conditional_call(n >= 0, g)
     def later(m):
         conditional_call(m, g)
     t = TranslationContext()
     t.buildannotator().build_types(f, [int])
     t.buildrtyper().specialize()
     mix = MixLevelHelperAnnotator(t.rtyper)
     mix.getgraph(later, [annmodel.s_Bool], annmodel.s_None)
     mix.finish()
Esempio n. 10
0
def test_enforced_args():
    from rpython.annotator.model import s_None
    from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
    from rpython.translator.interactive import Translation
    def f1():
        str2charp("hello")
    def f2():
        str2charp("world")
    t = Translation(f1, [])
    t.rtype()
    mixann = MixLevelHelperAnnotator(t.context.rtyper)
    mixann.getgraph(f2, [], s_None)
    mixann.finish()
Esempio n. 11
0
def test_delayed_fnptr():
    from rpython.flowspace.model import SpaceOperation
    from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
    from rpython.translator.translator import TranslationContext
    t = TranslationContext()
    t.buildannotator()
    t.buildrtyper()
    annhelper = MixLevelHelperAnnotator(t.rtyper)
    def f():
        pass
    c_f = annhelper.constfunc(f, [], None)
    op = SpaceOperation('direct_call', [c_f], None)
    analyzer = BoolGraphAnalyzer(t)
    assert analyzer.analyze(op)
Esempio n. 12
0
    def make_driverhook_graphs(self):
        #
        annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
        for jd in self.jitdrivers_sd:
            jd._get_printable_location_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.get_printable_location,
                annmodel.SomeString())
            jd._get_unique_id_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.get_unique_id,
                annmodel.SomeInteger())
            jd._confirm_enter_jit_ptr = self._make_hook_graph(
                jd,
                annhelper,
                jd.jitdriver.confirm_enter_jit,
                annmodel.s_Bool,
                onlygreens=False)
            jd._can_never_inline_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.can_never_inline, annmodel.s_Bool)
            jd._should_unroll_one_iteration_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.should_unroll_one_iteration,
                annmodel.s_Bool)
            #
            items = []
            types = ()
            pos = ()
            if jd.jitdriver.get_location:
                assert hasattr(jd.jitdriver.get_location, '_loc_types'), """
                You must decorate your get_location function:

                from rpython.rlib.rjitlog import rjitlog as jl
                @jl.returns(jl.MP_FILENAME, jl.MP_XXX, ...)
                def get_loc(your, green, keys):
                    name = "x.txt" # extract it from your green keys
                    return (name, ...)
                """
                types = jd.jitdriver.get_location._loc_types
                del jd.jitdriver.get_location._loc_types
                #
                for _, type in types:
                    if type == 's':
                        items.append(annmodel.SomeString())
                    elif type == 'i':
                        items.append(annmodel.SomeInteger())
                    else:
                        raise NotImplementedError
            s_Tuple = annmodel.SomeTuple(items)
            jd._get_location_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.get_location, s_Tuple)
            jd._get_loc_types = types
        annhelper.finish()
Esempio n. 13
0
def builtin_func_for_spec(rtyper,
                          oopspec_name,
                          ll_args,
                          ll_res,
                          extra=None,
                          extrakey=None):
    assert (extra is None) == (extrakey is None)
    key = (oopspec_name, tuple(ll_args), ll_res, extrakey)
    try:
        return rtyper._builtin_func_for_spec_cache[key]
    except (KeyError, AttributeError):
        pass
    args_s = [lltype_to_annotation(v) for v in ll_args]
    if '.' not in oopspec_name:  # 'newxxx' operations
        LIST_OR_DICT = ll_res
    else:
        LIST_OR_DICT = ll_args[0]
    s_result = lltype_to_annotation(ll_res)
    impl = setup_extra_builtin(rtyper, oopspec_name, len(args_s), extra)
    if getattr(impl, 'need_result_type', False):
        if hasattr(rtyper, 'annotator'):
            bk = rtyper.annotator.bookkeeper
            ll_restype = ll_res
            if impl.need_result_type != 'exact':
                ll_restype = ll_restype.TO
            desc = bk.getdesc(ll_restype)
        else:

            class TestingDesc(object):
                knowntype = int
                pyobj = None

            desc = TestingDesc()
        args_s.insert(0, annmodel.SomePBC([desc]))
    #
    if hasattr(rtyper, 'annotator'):  # regular case
        mixlevelann = MixLevelHelperAnnotator(rtyper)
        c_func = mixlevelann.constfunc(impl, args_s, s_result)
        mixlevelann.finish()
    else:
        # for testing only
        c_func = Constant(oopspec_name,
                          lltype.Ptr(lltype.FuncType(ll_args, ll_res)))
    #
    if not hasattr(rtyper, '_builtin_func_for_spec_cache'):
        rtyper._builtin_func_for_spec_cache = {}
    rtyper._builtin_func_for_spec_cache[key] = (c_func, LIST_OR_DICT)
    #
    return c_func, LIST_OR_DICT
Esempio n. 14
0
 def make_driverhook_graphs(self):
     s_Str = annmodel.SomeString()
     #
     annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
     for jd in self.jitdrivers_sd:
         jd._get_printable_location_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.get_printable_location, s_Str)
         jd._confirm_enter_jit_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.confirm_enter_jit, annmodel.s_Bool,
             onlygreens=False)
         jd._can_never_inline_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.can_never_inline, annmodel.s_Bool)
         jd._should_unroll_one_iteration_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.should_unroll_one_iteration,
             annmodel.s_Bool)
     annhelper.finish()
Esempio n. 15
0
def test_delayed_fnptr():
    from rpython.flowspace.model import SpaceOperation
    from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
    from rpython.translator.translator import TranslationContext
    t = TranslationContext()
    t.buildannotator()
    t.buildrtyper()
    annhelper = MixLevelHelperAnnotator(t.rtyper)

    def f():
        pass

    c_f = annhelper.constfunc(f, [], None)
    op = SpaceOperation('direct_call', [c_f], None)
    analyzer = BoolGraphAnalyzer(t)
    assert analyzer.analyze(op)
Esempio n. 16
0
 def make_driverhook_graphs(self):
     s_Str = annmodel.SomeString()
     #
     annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
     for jd in self.jitdrivers_sd:
         jd._get_printable_location_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.get_printable_location, s_Str)
         jd._confirm_enter_jit_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.confirm_enter_jit, annmodel.s_Bool,
             onlygreens=False)
         jd._can_never_inline_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.can_never_inline, annmodel.s_Bool)
         jd._should_unroll_one_iteration_ptr = self._make_hook_graph(jd,
             annhelper, jd.jitdriver.should_unroll_one_iteration,
             annmodel.s_Bool)
     annhelper.finish()
Esempio n. 17
0
    def make_driverhook_graphs(self):
        #
        annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
        for jd in self.jitdrivers_sd:
            jd._get_printable_location_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.get_printable_location,
                annmodel.SomeString())
            jd._get_unique_id_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.get_unique_id, annmodel.SomeInteger())
            jd._confirm_enter_jit_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.confirm_enter_jit, annmodel.s_Bool,
                onlygreens=False)
            jd._can_never_inline_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.can_never_inline, annmodel.s_Bool)
            jd._should_unroll_one_iteration_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.should_unroll_one_iteration,
                annmodel.s_Bool)
            #
            items = []
            types = ()
            pos = ()
            if jd.jitdriver.get_location:
                assert hasattr(jd.jitdriver.get_location, '_loc_types'), """
                You must decorate your get_location function:

                from rpython.rlib.rjitlog import rjitlog as jl
                @jl.returns(jl.MP_FILENAME, jl.MP_XXX, ...)
                def get_loc(your, green, keys):
                    name = "x.txt" # extract it from your green keys
                    return (name, ...)
                """
                types = jd.jitdriver.get_location._loc_types
                del jd.jitdriver.get_location._loc_types
                #
                for _,type in types:
                    if type == 's':
                        items.append(annmodel.SomeString())
                    elif type == 'i':
                        items.append(annmodel.SomeInteger())
                    else:
                        raise NotImplementedError
            s_Tuple = annmodel.SomeTuple(items)
            jd._get_location_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.get_location, s_Tuple)
            jd._get_loc_types = types
        annhelper.finish()
Esempio n. 18
0
def test_pseudohighlevelcallable():
    t = TranslationContext()
    t.buildannotator()
    rtyper = t.buildrtyper()
    rtyper.specialize()
    a = MixLevelHelperAnnotator(rtyper)

    class A:
        value = 5

        def double(self):
            return self.value * 2

    def fn1(a):
        a2 = A()
        a2.value = a.double()
        return a2

    s_A, r_A = a.s_r_instanceof(A)
    fn1ptr = a.delayedfunction(fn1, [s_A], s_A)
    pseudo = PseudoHighLevelCallable(fn1ptr, [s_A], s_A)

    def fn2(n):
        a = A()
        a.value = n
        a2 = pseudo(a)
        return a2.value

    graph = a.getgraph(fn2, [annmodel.SomeInteger()], annmodel.SomeInteger())
    a.finish()

    llinterp = LLInterpreter(rtyper)
    res = llinterp.eval_graph(graph, [21])
    assert res == 42
Esempio n. 19
0
    def replace_force_virtualizable(self, rtyper, graphs):
        from rpython.annotator import model as annmodel
        from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
        graph = graphs[0]

        for block, op in graph.iterblockops():
            if op.opname == 'jit_force_virtualizable':
                v_inst_ll_type = op.args[0].concretetype
                break

        def mycall(vinst_ll):
            if vinst_ll.vable_token:
                raise ValueError
        annhelper = MixLevelHelperAnnotator(rtyper)
        s_vinst = SomePtr(v_inst_ll_type)
        funcptr = annhelper.delayedfunction(mycall, [s_vinst], annmodel.s_None)
        annhelper.finish()
        replace_force_virtualizable_with_call(graphs, v_inst_ll_type, funcptr)
        return funcptr
Esempio n. 20
0
 def __init__(self, translator, inline=False):
     self.translator = translator
     self.seen_graphs = set()
     self.prepared = False
     self.minimal_transform = set()
     if translator:
         self.mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
     else:
         self.mixlevelannotator = None
     self.inline = inline
     if translator and inline:
         self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping()
         self.raise_analyzer = RaiseAnalyzer(translator)
     self.graphs_to_inline = {}
     self.graph_dependencies = {}
     self.ll_finalizers_ptrs = []
     if self.MinimalGCTransformer:
         self.minimalgctransformer = self.MinimalGCTransformer(self)
     else:
         self.minimalgctransformer = None
Esempio n. 21
0
    def replace_force_virtualizable(self, rtyper, graphs):
        from rpython.annotator import model as annmodel
        from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
        graph = graphs[0]

        for block, op in graph.iterblockops():
            if op.opname == 'jit_force_virtualizable':
                v_inst_ll_type = op.args[0].concretetype
                break

        def mycall(vinst_ll):
            if vinst_ll.vable_token:
                raise ValueError

        annhelper = MixLevelHelperAnnotator(rtyper)
        s_vinst = SomePtr(v_inst_ll_type)
        funcptr = annhelper.delayedfunction(mycall, [s_vinst], annmodel.s_None)
        annhelper.finish()
        replace_force_virtualizable_with_call(graphs, v_inst_ll_type, funcptr)
        return funcptr
Esempio n. 22
0
def test_pseudohighlevelcallable():
    t = TranslationContext()
    t.buildannotator()
    rtyper = t.buildrtyper()
    rtyper.specialize()
    a = MixLevelHelperAnnotator(rtyper)

    class A:
        value = 5
        def double(self):
            return self.value * 2

    def fn1(a):
        a2 = A()
        a2.value = a.double()
        return a2

    s_A, r_A = a.s_r_instanceof(A)
    fn1ptr = a.delayedfunction(fn1, [s_A], s_A)
    pseudo = PseudoHighLevelCallable(fn1ptr, [s_A], s_A)

    def fn2(n):
        a = A()
        a.value = n
        a2 = pseudo(a)
        return a2.value

    graph = a.getgraph(fn2, [annmodel.SomeInteger()], annmodel.SomeInteger())
    a.finish()

    llinterp = LLInterpreter(rtyper)
    res = llinterp.eval_graph(graph, [21])
    assert res == 42
Esempio n. 23
0
def call_initial_function(translator, initial_func, annhelper=None):
    """Before the program starts, call 'initial_func()'."""
    from rpython.annotator import model as annmodel
    from rpython.rtyper.lltypesystem import lltype
    from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator

    own_annhelper = (annhelper is None)
    if own_annhelper:
        annhelper = MixLevelHelperAnnotator(translator.rtyper)
    c_initial_func = annhelper.constfunc(initial_func, [], annmodel.s_None)
    if own_annhelper:
        annhelper.finish()

    entry_point = translator.entry_point_graph
    args = [copyvar(translator.annotator, v) for v in entry_point.getargs()]
    extrablock = Block(args)
    v_none = varoftype(lltype.Void)
    newop = SpaceOperation('direct_call', [c_initial_func], v_none)
    extrablock.operations = [newop]
    extrablock.closeblock(Link(args, entry_point.startblock))
    entry_point.startblock = extrablock
    checkgraph(entry_point)
Esempio n. 24
0
def call_initial_function(translator, initial_func, annhelper=None):
    """Before the program starts, call 'initial_func()'."""
    from rpython.annotator import model as annmodel
    from rpython.rtyper.lltypesystem import lltype
    from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator

    own_annhelper = (annhelper is None)
    if own_annhelper:
        annhelper = MixLevelHelperAnnotator(translator.rtyper)
    c_initial_func = annhelper.constfunc(initial_func, [], annmodel.s_None)
    if own_annhelper:
        annhelper.finish()

    entry_point = translator.entry_point_graph
    args = [v.copy() for v in entry_point.getargs()]
    extrablock = Block(args)
    v_none = varoftype(lltype.Void)
    newop = SpaceOperation('direct_call', [c_initial_func], v_none)
    extrablock.operations = [newop]
    extrablock.closeblock(Link(args, entry_point.startblock))
    entry_point.startblock = extrablock
    checkgraph(entry_point)
Esempio n. 25
0
    def getentrypointptr(self):
        # XXX check that the entrypoint has the correct
        # signature:  list-of-strings -> int
        if not self.make_entrypoint_wrapper:
            bk = self.translator.annotator.bookkeeper
            return getfunctionptr(bk.getdesc(self.entrypoint).getuniquegraph())
        if self._entrypoint_wrapper is not None:
            return self._entrypoint_wrapper
        #
        from rpython.annotator import model as annmodel
        from rpython.rtyper.lltypesystem import rffi
        from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
        from rpython.rtyper.llannotation import lltype_to_annotation
        entrypoint = self.entrypoint

        #
        def entrypoint_wrapper(argc, argv):
            """This is a wrapper that takes "Signed argc" and "char **argv"
            like the C main function, and puts them inside an RPython list
            of strings before invoking the real entrypoint() function.
            """
            list = [""] * argc
            i = 0
            while i < argc:
                list[i] = rffi.charp2str(argv[i])
                i += 1
            return entrypoint(list)

        #
        mix = MixLevelHelperAnnotator(self.translator.rtyper)
        args_s = [annmodel.SomeInteger(), lltype_to_annotation(rffi.CCHARPP)]
        s_result = annmodel.SomeInteger()
        graph = mix.getgraph(entrypoint_wrapper, args_s, s_result)
        mix.finish()
        res = getfunctionptr(graph)
        self._entrypoint_wrapper = res
        return res
Esempio n. 26
0
 def getentrypointptr(self):
     # XXX check that the entrypoint has the correct
     # signature:  list-of-strings -> int
     if not self.make_entrypoint_wrapper:
         bk = self.translator.annotator.bookkeeper
         return getfunctionptr(bk.getdesc(self.entrypoint).getuniquegraph())
     if self._entrypoint_wrapper is not None:
         return self._entrypoint_wrapper
     #
     from rpython.annotator import model as annmodel
     from rpython.rtyper.lltypesystem import rffi
     from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
     from rpython.rtyper.llannotation import lltype_to_annotation
     entrypoint = self.entrypoint
     #
     def entrypoint_wrapper(argc, argv):
         """This is a wrapper that takes "Signed argc" and "char **argv"
         like the C main function, and puts them inside an RPython list
         of strings before invoking the real entrypoint() function.
         """
         list = [""] * argc
         i = 0
         while i < argc:
             list[i] = rffi.charp2str(argv[i])
             i += 1
         return entrypoint(list)
     #
     mix = MixLevelHelperAnnotator(self.translator.rtyper)
     args_s = [annmodel.SomeInteger(),
               lltype_to_annotation(rffi.CCHARPP)]
     s_result = annmodel.SomeInteger()
     graph = mix.getgraph(entrypoint_wrapper, args_s, s_result)
     mix.finish()
     res = getfunctionptr(graph)
     self._entrypoint_wrapper = res
     return res
Esempio n. 27
0
    def test_secondary_backendopt(self):
        # checks an issue with a newly added graph that calls an
        # already-exception-transformed graph.  This can occur e.g.
        # from a late-seen destructor added by the GC transformer
        # which ends up calling existing code.
        def common(n):
            if n > 5:
                raise ValueError

        def main(n):
            common(n)

        def later(n):
            try:
                common(n)
                return 0
            except ValueError:
                return 1

        t = TranslationContext()
        t.buildannotator().build_types(main, [int])
        t.buildrtyper().specialize()
        exctransformer = t.getexceptiontransformer()
        exctransformer.create_exception_handling(graphof(t, common))
        from rpython.annotator import model as annmodel
        from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
        annhelper = MixLevelHelperAnnotator(t.rtyper)
        later_graph = annhelper.getgraph(later, [annmodel.SomeInteger()],
                                         annmodel.SomeInteger())
        annhelper.finish()
        annhelper.backend_optimize()
        # ^^^ as the inliner can't handle exception-transformed graphs,
        # this should *not* inline common() into later().
        if option.view:
            later_graph.show()
        common_graph = graphof(t, common)
        found = False
        for block in later_graph.iterblocks():
            for op in block.operations:
                if (op.opname == 'direct_call'
                        and op.args[0].value._obj.graph is common_graph):
                    found = True
        assert found, "cannot find the call (buggily inlined?)"
        from rpython.rtyper.llinterp import LLInterpreter
        llinterp = LLInterpreter(t.rtyper)
        res = llinterp.eval_graph(later_graph, [10])
        assert res == 1
Esempio n. 28
0
def test_enforced_args():
    from rpython.annotator.model import s_None
    from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
    from rpython.translator.interactive import Translation
    def f1():
        str2charp("hello")
    def f2():
        str2charp("world")
    t = Translation(f1, [])
    t.rtype()
    mixann = MixLevelHelperAnnotator(t.context.rtyper)
    mixann.getgraph(f2, [], s_None)
    mixann.finish()
Esempio n. 29
0
 def test_conditional_call(self):
     def g():
         pass
     def f(n):
         conditional_call(n >= 0, g)
     def later(m):
         conditional_call(m, g)
     t = TranslationContext()
     t.buildannotator().build_types(f, [int])
     t.buildrtyper().specialize()
     mix = MixLevelHelperAnnotator(t.rtyper)
     mix.getgraph(later, [annmodel.s_Bool], annmodel.s_None)
     mix.finish()
Esempio n. 30
0
 def __init__(self, translator, inline=False):
     self.translator = translator
     self.seen_graphs = set()
     self.prepared = False
     self.minimal_transform = set()
     if translator:
         self.mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
     else:
         self.mixlevelannotator = None
     self.inline = inline
     if translator and inline:
         self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping()
     self.graphs_to_inline = {}
     self.graph_dependencies = {}
     self.ll_finalizers_ptrs = []
     if self.MinimalGCTransformer:
         self.minimalgctransformer = self.MinimalGCTransformer(self)
     else:
         self.minimalgctransformer = None
Esempio n. 31
0
    def test_secondary_backendopt(self):
        # checks an issue with a newly added graph that calls an
        # already-exception-transformed graph.  This can occur e.g.
        # from a late-seen destructor added by the GC transformer
        # which ends up calling existing code.
        def common(n):
            if n > 5:
                raise ValueError

        def main(n):
            common(n)

        def later(n):
            try:
                common(n)
                return 0
            except ValueError:
                return 1

        t = TranslationContext()
        t.buildannotator().build_types(main, [int])
        t.buildrtyper().specialize()
        exctransformer = t.getexceptiontransformer()
        exctransformer.create_exception_handling(graphof(t, common))
        from rpython.annotator import model as annmodel
        from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator

        annhelper = MixLevelHelperAnnotator(t.rtyper)
        later_graph = annhelper.getgraph(later, [annmodel.SomeInteger()], annmodel.SomeInteger())
        annhelper.finish()
        annhelper.backend_optimize()
        # ^^^ as the inliner can't handle exception-transformed graphs,
        # this should *not* inline common() into later().
        if option.view:
            later_graph.show()
        common_graph = graphof(t, common)
        found = False
        for block in later_graph.iterblocks():
            for op in block.operations:
                if op.opname == "direct_call" and op.args[0].value._obj.graph is common_graph:
                    found = True
        assert found, "cannot find the call (buggily inlined?)"
        from rpython.rtyper.llinterp import LLInterpreter

        llinterp = LLInterpreter(t.rtyper)
        res = llinterp.eval_graph(later_graph, [10])
        assert res == 1
Esempio n. 32
0
 def build_cpu(self, CPUClass, translate_support_code=False,
               no_stats=False, supports_floats=True,
               supports_longlong=True, supports_singlefloats=True,
               **kwds):
     assert CPUClass is not None
     self.opt = history.Options(**kwds)
     if no_stats:
         stats = history.NoStats()
     else:
         stats = history.Stats(None)
     self.stats = stats
     if translate_support_code:
         self.annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
     cpu = CPUClass(self.translator.rtyper, self.stats, self.opt,
                    translate_support_code, gcdescr=self.gcdescr)
     if not supports_floats:
         cpu.supports_floats = False
     if not supports_longlong:
         cpu.supports_longlong = False
     if not supports_singlefloats:
         cpu.supports_singlefloats = False
     self.cpu = cpu
Esempio n. 33
0
        dump_arguments = rmarshal.get_marshaller(tuple(args_s))
        load_result = rmarshal.get_loader(s_result)

    except (NotImplementedError,
            rmarshal.CannotMarshal,
            rmarshal.CannotUnmarshall), e:
        msg = 'Not Implemented: %s' % (e,)
        log.WARNING(msg)
        def execute(*args):
            not_implemented_stub(msg)

    else:
        def execute(*args):
            # marshal the function name and input arguments
            buf = []
            dump_string(buf, fnname)
            dump_arguments(buf, args)
            # send the buffer and wait for the answer
            loader = sandboxed_io(buf)
            # decode the answer
            result = load_result(loader)
            loader.check_finished()
            return result
    execute = func_with_new_name(execute, 'sandboxed_' + fnname)

    ann = MixLevelHelperAnnotator(db.translator.rtyper)
    graph = ann.getgraph(execute, args_s, s_result)
    ann.finish()
    return graph
Esempio n. 34
0
        load_result = rmarshal.get_loader(s_result)

    except (NotImplementedError, rmarshal.CannotMarshal,
            rmarshal.CannotUnmarshall), e:
        msg = 'Not Implemented: %s' % (e, )
        log.WARNING(msg)

        def execute(*args):
            not_implemented_stub(msg)

    else:

        def execute(*args):
            # marshal the function name and input arguments
            buf = []
            dump_string(buf, fnname)
            dump_arguments(buf, args)
            # send the buffer and wait for the answer
            loader = sandboxed_io(buf)
            # decode the answer
            result = load_result(loader)
            loader.check_finished()
            return result

    execute = func_with_new_name(execute, 'sandboxed_' + fnname)

    ann = MixLevelHelperAnnotator(db.translator.rtyper)
    graph = ann.getgraph(execute, args_s, s_result)
    ann.finish()
    return graph
Esempio n. 35
0
 def getannmixlevel(self):
     if self.annmixlevel is not None:
         return self.annmixlevel
     from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
     self.annmixlevel = MixLevelHelperAnnotator(self)
     return self.annmixlevel
Esempio n. 36
0
class WarmRunnerDesc(object):
    def __init__(self,
                 translator,
                 policy=None,
                 backendopt=True,
                 CPUClass=None,
                 ProfilerClass=EmptyProfiler,
                 **kwds):
        pyjitpl._warmrunnerdesc = self  # this is a global for debugging only!
        self.set_translator(translator)
        self.memory_manager = memmgr.MemoryManager()
        self.build_cpu(CPUClass, **kwds)
        self.inline_inlineable_portals()
        self.find_portals()
        self.codewriter = codewriter.CodeWriter(self.cpu, self.jitdrivers_sd)
        if policy is None:
            policy = JitPolicy()
        policy.set_supports_floats(self.cpu.supports_floats)
        policy.set_supports_longlong(self.cpu.supports_longlong)
        policy.set_supports_singlefloats(self.cpu.supports_singlefloats)
        graphs = self.codewriter.find_all_graphs(policy)
        policy.dump_unsafe_loops()
        self.check_access_directly_sanity(graphs)
        if backendopt:
            self.prejit_optimizations(policy, graphs)
        elif self.opt.listops:
            self.prejit_optimizations_minimal_inline(policy, graphs)

        self.build_meta_interp(
            ProfilerClass, translator.config.translation.jit_opencoder_model)
        self.make_args_specifications()
        #
        from rpython.jit.metainterp.virtualref import VirtualRefInfo
        vrefinfo = VirtualRefInfo(self)
        self.codewriter.setup_vrefinfo(vrefinfo)
        #
        from rpython.jit.metainterp import counter
        if self.cpu.translate_support_code:
            self.jitcounter = counter.JitCounter(translator=translator)
        else:
            self.jitcounter = counter.DeterministicJitCounter()
        #
        self.hooks = policy.jithookiface
        self.make_virtualizable_infos()
        self.make_driverhook_graphs()
        self.make_enter_functions()
        self.rewrite_jit_merge_points(policy)

        verbose = False  # not self.cpu.translate_support_code
        self.rewrite_access_helpers()
        self.create_jit_entry_points()
        jitcodes = self.codewriter.make_jitcodes(verbose=verbose)
        self.metainterp_sd.jitcodes = jitcodes
        self.rewrite_can_enter_jits()
        self.rewrite_set_param_and_get_stats()
        self.rewrite_force_virtual(vrefinfo)
        self.rewrite_jitcell_accesses()
        self.rewrite_force_quasi_immutable()
        self.add_finish()
        self.metainterp_sd.finish_setup(self.codewriter)

    def finish(self):
        vinfos = set([jd.virtualizable_info for jd in self.jitdrivers_sd])
        for vinfo in vinfos:
            if vinfo is not None:
                vinfo.finish()
        self.metainterp_sd.finish_setup_descrs()
        if self.cpu.translate_support_code:
            self.annhelper.finish()

    def _freeze_(self):
        return True

    def set_translator(self, translator):
        self.translator = translator
        self.rtyper = translator.rtyper
        self.gcdescr = gc.get_description(translator.config)

    def inline_inlineable_portals(self):
        """
        Find all the graphs which have been decorated with @jitdriver.inline
        and inline them in the callers, making them JIT portals. Then, create
        a fresh copy of the jitdriver for each of those new portals, because
        they cannot share the same one.  See
        test_ajit::test_inline_jit_merge_point
        """
        from rpython.translator.backendopt.inline import (
            inlinable_static_callers, auto_inlining)

        jmp_calls = {}

        def get_jmp_call(graph, _inline_jit_merge_point_):
            # there might be multiple calls to the @inlined function: the
            # first time we see it, we remove the call to the jit_merge_point
            # and we remember the corresponding op. Then, we create a new call
            # to it every time we need a new one (i.e., for each callsite
            # which becomes a new portal)
            try:
                op, jmp_graph = jmp_calls[graph]
            except KeyError:
                op, jmp_graph = fish_jmp_call(graph, _inline_jit_merge_point_)
                jmp_calls[graph] = op, jmp_graph
            #
            # clone the op
            newargs = op.args[:]
            newresult = Variable()
            newresult.concretetype = op.result.concretetype
            op = SpaceOperation(op.opname, newargs, newresult)
            return op, jmp_graph

        def fish_jmp_call(graph, _inline_jit_merge_point_):
            # graph is function which has been decorated with
            # @jitdriver.inline, so its very first op is a call to the
            # function which contains the actual jit_merge_point: fish it!
            jmp_block, op_jmp_call = next(callee.iterblockops())
            msg = (
                "The first operation of an _inline_jit_merge_point_ graph must be "
                "a direct_call to the function passed to @jitdriver.inline()")
            assert op_jmp_call.opname == 'direct_call', msg
            jmp_funcobj = op_jmp_call.args[0].value._obj
            assert jmp_funcobj._callable is _inline_jit_merge_point_, msg
            jmp_block.operations.remove(op_jmp_call)
            return op_jmp_call, jmp_funcobj.graph

        # find all the graphs which call an @inline_in_portal function
        callgraph = inlinable_static_callers(self.translator.graphs,
                                             store_calls=True)
        new_callgraph = []
        new_portals = set()
        inlined_jit_merge_points = set()
        for caller, block, op_call, callee in callgraph:
            func = getattr(callee, 'func', None)
            _inline_jit_merge_point_ = getattr(func,
                                               '_inline_jit_merge_point_',
                                               None)
            if _inline_jit_merge_point_:
                _inline_jit_merge_point_._always_inline_ = True
                inlined_jit_merge_points.add(_inline_jit_merge_point_)
                op_jmp_call, jmp_graph = get_jmp_call(
                    callee, _inline_jit_merge_point_)
                #
                # now we move the op_jmp_call from callee to caller, just
                # before op_call. We assume that the args passed to
                # op_jmp_call are the very same which are received by callee
                # (i.e., the one passed to op_call)
                assert len(op_call.args) == len(op_jmp_call.args)
                op_jmp_call.args[1:] = op_call.args[1:]
                idx = block.operations.index(op_call)
                block.operations.insert(idx, op_jmp_call)
                #
                # finally, we signal that we want to inline op_jmp_call into
                # caller, so that finally the actuall call to
                # driver.jit_merge_point will be seen there
                new_callgraph.append((caller, jmp_graph))
                new_portals.add(caller)

        # inline them!
        inline_threshold = 0.1  # we rely on the _always_inline_ set above
        auto_inlining(self.translator, inline_threshold, new_callgraph)
        # clean up _always_inline_ = True, it can explode later
        for item in inlined_jit_merge_points:
            del item._always_inline_

        # make a fresh copy of the JitDriver in all newly created
        # jit_merge_points
        self.clone_inlined_jit_merge_points(new_portals)

    def clone_inlined_jit_merge_points(self, graphs):
        """
        Find all the jit_merge_points in the given graphs, and replace the
        original JitDriver with a fresh clone.
        """
        if not graphs:
            return
        for graph, block, pos in find_jit_merge_points(graphs):
            op = block.operations[pos]
            v_driver = op.args[1]
            driver = v_driver.value
            if not driver.inline_jit_merge_point:
                continue
            new_driver = driver.clone()
            c_new_driver = Constant(new_driver, v_driver.concretetype)
            op.args[1] = c_new_driver

    def find_portals(self):
        self.jitdrivers_sd = []
        graphs = self.translator.graphs
        for graph, block, pos in find_jit_merge_points(graphs):
            support.autodetect_jit_markers_redvars(graph)
            self.split_graph_and_record_jitdriver(graph, block, pos)
        #
        assert (len(set([jd.jitdriver for jd in self.jitdrivers_sd])) ==
                len(self.jitdrivers_sd)), \
                "there are multiple jit_merge_points with the same jitdriver"

    def split_graph_and_record_jitdriver(self, graph, block, pos):
        op = block.operations[pos]
        jd = JitDriverStaticData()
        jd._jit_merge_point_in = graph
        args = op.args[2:]
        s_binding = self.translator.annotator.binding
        jd._portal_args_s = [s_binding(v) for v in args]
        graph = copygraph(graph)
        [jmpp] = find_jit_merge_points([graph])
        graph.startblock = support.split_before_jit_merge_point(*jmpp)
        # XXX this is incredibly obscure, but this is sometiems necessary
        #     so we don't explode in checkgraph. for reasons unknown this
        #     is not contanied within simplify_graph
        removenoops.remove_same_as(graph)
        # a crash in the following checkgraph() means that you forgot
        # to list some variable in greens=[] or reds=[] in JitDriver,
        # or that a jit_merge_point() takes a constant as an argument.
        checkgraph(graph)
        for v in graph.getargs():
            assert isinstance(v, Variable)
        assert len(dict.fromkeys(graph.getargs())) == len(graph.getargs())
        self.translator.graphs.append(graph)
        jd.portal_graph = graph
        # it's a bit unbelievable to have a portal without func
        assert hasattr(graph, "func")
        graph.func._dont_inline_ = True
        graph.func._jit_unroll_safe_ = True
        jd.jitdriver = block.operations[pos].args[1].value
        jd.vec = jd.jitdriver.vec
        jd.portal_runner_ptr = "<not set so far>"
        jd.result_type = history.getkind(
            jd.portal_graph.getreturnvar().concretetype)[0]
        self.jitdrivers_sd.append(jd)

    def check_access_directly_sanity(self, graphs):
        from rpython.translator.backendopt.inline import collect_called_graphs
        jit_graphs = set(graphs)
        for graph in collect_called_graphs(self.translator.entry_point_graph,
                                           self.translator):
            if graph in jit_graphs:
                continue
            assert not getattr(graph, 'access_directly', False)

    def prejit_optimizations(self, policy, graphs):
        from rpython.translator.backendopt.all import backend_optimizations
        backend_optimizations(self.translator,
                              graphs=graphs,
                              merge_if_blocks=True,
                              constfold=True,
                              remove_asserts=True,
                              really_remove_asserts=True,
                              replace_we_are_jitted=False)

    def prejit_optimizations_minimal_inline(self, policy, graphs):
        from rpython.translator.backendopt.inline import auto_inline_graphs
        auto_inline_graphs(self.translator, graphs, 0.01)

    def build_cpu(self,
                  CPUClass,
                  translate_support_code=False,
                  no_stats=False,
                  supports_floats=True,
                  supports_longlong=True,
                  supports_singlefloats=True,
                  **kwds):
        assert CPUClass is not None
        self.opt = history.Options(**kwds)
        if no_stats:
            stats = history.NoStats()
        else:
            stats = history.Stats(None)
        self.stats = stats
        if translate_support_code:
            self.annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
        cpu = CPUClass(self.translator.rtyper,
                       self.stats,
                       self.opt,
                       translate_support_code,
                       gcdescr=self.gcdescr)
        if not supports_floats:
            cpu.supports_floats = False
        if not supports_longlong:
            cpu.supports_longlong = False
        if not supports_singlefloats:
            cpu.supports_singlefloats = False
        self.cpu = cpu

    def build_meta_interp(self, ProfilerClass, opencoder_model):
        from rpython.jit.metainterp.opencoder import Model, BigModel
        self.metainterp_sd = MetaInterpStaticData(self.cpu,
                                                  self.opt,
                                                  ProfilerClass=ProfilerClass,
                                                  warmrunnerdesc=self)
        if opencoder_model == 'big':
            self.metainterp_sd.opencoder_model = BigModel
        else:
            self.metainterp_sd.opencoder_model = Model
        self.stats.metainterp_sd = self.metainterp_sd

    def make_virtualizable_infos(self):
        vinfos = {}
        for jd in self.jitdrivers_sd:
            #
            jd.greenfield_info = None
            for name in jd.jitdriver.greens:
                if '.' in name:
                    from rpython.jit.metainterp.greenfield import GreenFieldInfo
                    jd.greenfield_info = GreenFieldInfo(self.cpu, jd)
                    break
            #
            if not jd.jitdriver.virtualizables:
                jd.virtualizable_info = None
                jd.index_of_virtualizable = -1
                continue
            else:
                assert jd.greenfield_info is None, "XXX not supported yet"
            #
            jitdriver = jd.jitdriver
            assert len(jitdriver.virtualizables) == 1  # for now
            [vname] = jitdriver.virtualizables
            # XXX skip the Voids here too
            jd.index_of_virtualizable = jitdriver.reds.index(vname)
            #
            index = jd.num_green_args + jd.index_of_virtualizable
            VTYPEPTR = jd._JIT_ENTER_FUNCTYPE.ARGS[index]
            if VTYPEPTR not in vinfos:
                from rpython.jit.metainterp.virtualizable import VirtualizableInfo
                vinfos[VTYPEPTR] = VirtualizableInfo(self, VTYPEPTR)
            jd.virtualizable_info = vinfos[VTYPEPTR]

    def make_enter_functions(self):
        for jd in self.jitdrivers_sd:
            self.make_enter_function(jd)

    def make_enter_function(self, jd):
        from rpython.jit.metainterp.warmstate import WarmEnterState
        state = WarmEnterState(self, jd)
        maybe_compile_and_run, EnterJitAssembler = state.make_entry_point()
        jd.warmstate = state

        def crash_in_jit(e):
            tb = not we_are_translated() and sys.exc_info()[2]
            try:
                raise e
            except jitexc.JitException:
                raise  # go through
            except MemoryError:
                raise  # go through
            except StackOverflow:
                raise  # go through
            except Exception as e:
                if not we_are_translated():
                    print "~~~ Crash in JIT!"
                    print '~~~ %s: %s' % (e.__class__, e)
                    if sys.stdout == sys.__stdout__:
                        import pdb
                        pdb.post_mortem(tb)
                    raise e.__class__, e, tb
                fatalerror('~~~ Crash in JIT! %s' % (e, ))

        crash_in_jit._dont_inline_ = True

        def maybe_enter_jit(*args):
            try:
                maybe_compile_and_run(state.increment_threshold, *args)
            except Exception as e:
                crash_in_jit(e)

        maybe_enter_jit._always_inline_ = True
        jd._maybe_enter_jit_fn = maybe_enter_jit
        jd._maybe_compile_and_run_fn = maybe_compile_and_run
        jd._EnterJitAssembler = EnterJitAssembler

    def make_driverhook_graphs(self):
        #
        annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
        for jd in self.jitdrivers_sd:
            jd._get_printable_location_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.get_printable_location,
                annmodel.SomeString())
            jd._get_unique_id_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.get_unique_id,
                annmodel.SomeInteger())
            jd._confirm_enter_jit_ptr = self._make_hook_graph(
                jd,
                annhelper,
                jd.jitdriver.confirm_enter_jit,
                annmodel.s_Bool,
                onlygreens=False)
            jd._can_never_inline_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.can_never_inline, annmodel.s_Bool)
            jd._should_unroll_one_iteration_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.should_unroll_one_iteration,
                annmodel.s_Bool)
            #
            items = []
            types = ()
            pos = ()
            if jd.jitdriver.get_location:
                assert hasattr(jd.jitdriver.get_location, '_loc_types'), """
                You must decorate your get_location function:

                from rpython.rlib.rjitlog import rjitlog as jl
                @jl.returns(jl.MP_FILENAME, jl.MP_XXX, ...)
                def get_loc(your, green, keys):
                    name = "x.txt" # extract it from your green keys
                    return (name, ...)
                """
                types = jd.jitdriver.get_location._loc_types
                del jd.jitdriver.get_location._loc_types
                #
                for _, type in types:
                    if type == 's':
                        items.append(annmodel.SomeString())
                    elif type == 'i':
                        items.append(annmodel.SomeInteger())
                    else:
                        raise NotImplementedError
            s_Tuple = annmodel.SomeTuple(items)
            jd._get_location_ptr = self._make_hook_graph(
                jd, annhelper, jd.jitdriver.get_location, s_Tuple)
            jd._get_loc_types = types
        annhelper.finish()

    def _make_hook_graph(self,
                         jitdriver_sd,
                         annhelper,
                         func,
                         s_result,
                         s_first_arg=None,
                         onlygreens=True):
        if func is None:
            return None
        #
        if not onlygreens:
            assert not jitdriver_sd.jitdriver.autoreds, (
                "reds='auto' is not compatible with JitDriver hooks such as "
                "confirm_enter_jit")
        extra_args_s = []
        if s_first_arg is not None:
            extra_args_s.append(s_first_arg)
        #
        args_s = jitdriver_sd._portal_args_s
        if onlygreens:
            args_s = args_s[:len(jitdriver_sd._green_args_spec)]
        graph = annhelper.getgraph(func, extra_args_s + args_s, s_result)
        funcptr = annhelper.graph2delayed(graph)
        return funcptr

    def make_args_specifications(self):
        for jd in self.jitdrivers_sd:
            self.make_args_specification(jd)

    def make_args_specification(self, jd):
        graph = jd._jit_merge_point_in
        _, _, op = locate_jit_merge_point(graph)
        greens_v, reds_v = support.decode_hp_hint_args(op)
        ALLARGS = [v.concretetype for v in (greens_v + reds_v)]
        jd._green_args_spec = [v.concretetype for v in greens_v]
        jd.red_args_types = [history.getkind(v.concretetype) for v in reds_v]
        jd.num_green_args = len(jd._green_args_spec)
        jd.num_red_args = len(jd.red_args_types)
        RESTYPE = graph.getreturnvar().concretetype
        (jd._JIT_ENTER_FUNCTYPE,
         jd._PTR_JIT_ENTER_FUNCTYPE) = self.cpu.ts.get_FuncType(
             ALLARGS, lltype.Void)
        (jd._PORTAL_FUNCTYPE,
         jd._PTR_PORTAL_FUNCTYPE) = self.cpu.ts.get_FuncType(ALLARGS, RESTYPE)
        #
        if jd.result_type == 'v':
            ASMRESTYPE = lltype.Void
        elif jd.result_type == history.INT:
            ASMRESTYPE = lltype.Signed
        elif jd.result_type == history.REF:
            ASMRESTYPE = llmemory.GCREF
        elif jd.result_type == history.FLOAT:
            ASMRESTYPE = lltype.Float
        else:
            assert False
        (_, jd._PTR_ASSEMBLER_HELPER_FUNCTYPE) = self.cpu.ts.get_FuncType(
            [llmemory.GCREF, llmemory.GCREF], ASMRESTYPE)

    def rewrite_jitcell_accesses(self):
        jitdrivers_by_name = {}
        for jd in self.jitdrivers_sd:
            name = jd.jitdriver.name
            if name != 'jitdriver':
                jitdrivers_by_name[name] = jd
        m = _find_jit_markers(self.translator.graphs,
                              ('get_jitcell_at_key', 'trace_next_iteration',
                               'dont_trace_here', 'trace_next_iteration_hash'))
        accessors = {}

        def get_accessor(name, jitdriver_name, function, ARGS, green_arg_spec):
            a = accessors.get((name, jitdriver_name))
            if a:
                return a
            d = {
                'function': function,
                'cast_instance_to_gcref': cast_instance_to_gcref,
                'lltype': lltype
            }
            arg_spec = ", ".join([("arg%d" % i) for i in range(len(ARGS))])
            arg_converters = []
            for i, spec in enumerate(green_arg_spec):
                if isinstance(spec, lltype.Ptr):
                    arg_converters.append(
                        "arg%d = lltype.cast_opaque_ptr(type%d, arg%d)" %
                        (i, i, i))
                    d['type%d' % i] = spec
            convert = ";".join(arg_converters)
            if name == 'get_jitcell_at_key':
                exec py.code.Source("""
                def accessor(%s):
                    %s
                    return cast_instance_to_gcref(function(%s))
                """ % (arg_spec, convert, arg_spec)).compile() in d
                FUNC = lltype.Ptr(lltype.FuncType(ARGS, llmemory.GCREF))
            elif name == "trace_next_iteration_hash":
                exec py.code.Source("""
                def accessor(arg0):
                    function(arg0)
                """).compile() in d
                FUNC = lltype.Ptr(
                    lltype.FuncType([lltype.Unsigned], lltype.Void))
            else:
                exec py.code.Source("""
                def accessor(%s):
                    %s
                    function(%s)
                """ % (arg_spec, convert, arg_spec)).compile() in d
                FUNC = lltype.Ptr(lltype.FuncType(ARGS, lltype.Void))
            func = d['accessor']
            ll_ptr = self.helper_func(FUNC, func)
            accessors[(name, jitdriver_name)] = ll_ptr
            return ll_ptr

        for graph, block, index in m:
            op = block.operations[index]
            jitdriver_name = op.args[1].value
            JitCell = jitdrivers_by_name[jitdriver_name].warmstate.JitCell
            ARGS = [x.concretetype for x in op.args[2:]]
            if op.args[0].value == 'get_jitcell_at_key':
                func = JitCell.get_jitcell
            elif op.args[0].value == 'dont_trace_here':
                func = JitCell.dont_trace_here
            elif op.args[0].value == 'trace_next_iteration_hash':
                func = JitCell.trace_next_iteration_hash
            else:
                func = JitCell._trace_next_iteration
            argspec = jitdrivers_by_name[jitdriver_name]._green_args_spec
            accessor = get_accessor(op.args[0].value, jitdriver_name, func,
                                    ARGS, argspec)
            v_result = op.result
            c_accessor = Constant(accessor, concretetype=lltype.Void)
            newop = SpaceOperation('direct_call', [c_accessor] + op.args[2:],
                                   v_result)
            block.operations[index] = newop

    def rewrite_can_enter_jits(self):
        sublists = {}
        for jd in self.jitdrivers_sd:
            sublists[jd.jitdriver] = jd, []
            jd.no_loop_header = True
        #
        loop_headers = find_loop_headers(self.translator.graphs)
        for graph, block, index in loop_headers:
            op = block.operations[index]
            jitdriver = op.args[1].value
            assert jitdriver in sublists, \
                   "loop_header with no matching jit_merge_point"
            jd, sublist = sublists[jitdriver]
            jd.no_loop_header = False
        #
        can_enter_jits = find_can_enter_jit(self.translator.graphs)
        for graph, block, index in can_enter_jits:
            op = block.operations[index]
            jitdriver = op.args[1].value
            assert jitdriver in sublists, \
                   "can_enter_jit with no matching jit_merge_point"
            assert not jitdriver.autoreds, (
                "can_enter_jit not supported with a jitdriver that "
                "has reds='auto'")
            jd, sublist = sublists[jitdriver]
            origportalgraph = jd._jit_merge_point_in
            if graph is not origportalgraph:
                sublist.append((graph, block, index))
                jd.no_loop_header = False
            else:
                pass  # a 'can_enter_jit' before the 'jit-merge_point', but
                # originally in the same function: we ignore it here
                # see e.g. test_jitdriver.test_simple
        for jd in self.jitdrivers_sd:
            _, sublist = sublists[jd.jitdriver]
            self.rewrite_can_enter_jit(jd, sublist)

    def rewrite_can_enter_jit(self, jd, can_enter_jits):
        FUNCPTR = jd._PTR_JIT_ENTER_FUNCTYPE
        jit_enter_fnptr = self.helper_func(FUNCPTR, jd._maybe_enter_jit_fn)

        if len(can_enter_jits) == 0:
            # see test_warmspot.test_no_loop_at_all
            operations = jd.portal_graph.startblock.operations
            op1 = operations[0]
            assert (op1.opname == 'jit_marker'
                    and op1.args[0].value == 'jit_merge_point')
            op0 = SpaceOperation('jit_marker',
                                 [Constant('can_enter_jit', lltype.Void)] +
                                 op1.args[1:], None)
            operations.insert(0, op0)
            can_enter_jits = [(jd.portal_graph, jd.portal_graph.startblock, 0)]

        for graph, block, index in can_enter_jits:
            if graph is jd._jit_merge_point_in:
                continue

            op = block.operations[index]
            greens_v, reds_v = support.decode_hp_hint_args(op)
            args_v = greens_v + reds_v

            vlist = [Constant(jit_enter_fnptr, FUNCPTR)] + args_v

            v_result = Variable()
            v_result.concretetype = lltype.Void
            newop = SpaceOperation('direct_call', vlist, v_result)
            block.operations[index] = newop

    def helper_func(self, FUNCPTR, func):
        if not self.cpu.translate_support_code:
            return llhelper(FUNCPTR, func)
        FUNC = FUNCPTR.TO
        args_s = [lltype_to_annotation(ARG) for ARG in FUNC.ARGS]
        s_result = lltype_to_annotation(FUNC.RESULT)
        graph = self.annhelper.getgraph(func, args_s, s_result)
        return self.annhelper.graph2delayed(graph, FUNC)

    def rewrite_access_helpers(self):
        ah = find_access_helpers(self.translator.graphs)
        for graph, block, index in ah:
            op = block.operations[index]
            self.rewrite_access_helper(op)

    def create_jit_entry_points(self):
        for func, args, result in all_jit_entrypoints:
            self.helper_func(lltype.Ptr(lltype.FuncType(args, result)), func)
            annotated_jit_entrypoints.append((func, None))

    def rewrite_access_helper(self, op):
        # make sure we make a copy of function so it no longer belongs
        # to extregistry
        func = op.args[1].value
        if func.func_name.startswith('stats_'):
            # get special treatment since we rewrite it to a call that accepts
            # jit driver
            assert len(op.args) >= 3, ("%r must have a first argument "
                                       "(which is None)" % (func, ))
            func = func_with_new_name(func, func.func_name + '_compiled')

            def new_func(ignored, *args):
                return func(self, *args)

            ARGS = [lltype.Void] + [arg.concretetype for arg in op.args[3:]]
        else:
            ARGS = [arg.concretetype for arg in op.args[2:]]
            new_func = func_with_new_name(func, func.func_name + '_compiled')
        RESULT = op.result.concretetype
        FUNCPTR = lltype.Ptr(lltype.FuncType(ARGS, RESULT))
        ptr = self.helper_func(FUNCPTR, new_func)
        op.opname = 'direct_call'
        op.args = [Constant(ptr, FUNCPTR)] + op.args[2:]

    def rewrite_jit_merge_points(self, policy):
        for jd in self.jitdrivers_sd:
            self.rewrite_jit_merge_point(jd, policy)

    def rewrite_jit_merge_point(self, jd, policy):
        #
        # Mutate the original portal graph from this:
        #
        #       def original_portal(..):
        #           stuff
        #           while 1:
        #               jit_merge_point(*args)
        #               more stuff
        #
        # to that:
        #
        #       def original_portal(..):
        #           stuff
        #           return portal_runner(*args)
        #
        #       def portal_runner(*args):
        #           while 1:
        #               try:
        #                   return portal(*args)
        #               except JitException, e:
        #                   return handle_jitexception(e)
        #
        #       def portal(*args):
        #           while 1:
        #               more stuff
        #
        origportalgraph = jd._jit_merge_point_in
        portalgraph = jd.portal_graph
        PORTALFUNC = jd._PORTAL_FUNCTYPE

        # ____________________________________________________________
        # Prepare the portal_runner() helper
        #
        from rpython.jit.metainterp.warmstate import specialize_value
        from rpython.jit.metainterp.warmstate import unspecialize_value
        portal_ptr = self.cpu.ts.functionptr(PORTALFUNC,
                                             'portal',
                                             graph=portalgraph)
        jd._portal_ptr = portal_ptr
        #
        portalfunc_ARGS = []
        nums = {}
        for i, ARG in enumerate(PORTALFUNC.ARGS):
            kind = history.getkind(ARG)
            assert kind != 'void'
            if i < len(jd.jitdriver.greens):
                color = 'green'
            else:
                color = 'red'
            attrname = '%s_%s' % (color, kind)
            count = nums.get(attrname, 0)
            nums[attrname] = count + 1
            portalfunc_ARGS.append((ARG, attrname, count))
        portalfunc_ARGS = unrolling_iterable(portalfunc_ARGS)
        #
        rtyper = self.translator.rtyper
        RESULT = PORTALFUNC.RESULT
        result_kind = history.getkind(RESULT)
        assert result_kind.startswith(jd.result_type)
        ts = self.cpu.ts
        state = jd.warmstate
        maybe_compile_and_run = jd._maybe_compile_and_run_fn
        EnterJitAssembler = jd._EnterJitAssembler

        def ll_portal_runner(*args):
            try:
                # maybe enter from the function's start.
                maybe_compile_and_run(state.increment_function_threshold,
                                      *args)
                #
                # then run the normal portal function, i.e. the
                # interpreter's main loop.  It might enter the jit
                # via maybe_enter_jit(), which typically ends with
                # handle_fail() being called, which raises on the
                # following exceptions --- catched here, because we
                # want to interrupt the whole interpreter loop.
                return support.maybe_on_top_of_llinterp(rtyper,
                                                        portal_ptr)(*args)
            except jitexc.JitException as e:
                result = handle_jitexception(e)
                if result_kind != 'void':
                    result = specialize_value(RESULT, result)
                return result

        def handle_jitexception(e):
            # XXX there are too many exceptions all around...
            while True:
                if isinstance(e, EnterJitAssembler):
                    try:
                        return e.execute()
                    except jitexc.JitException as e:
                        continue
                #
                if isinstance(e, jitexc.ContinueRunningNormally):
                    args = ()
                    for ARGTYPE, attrname, count in portalfunc_ARGS:
                        x = getattr(e, attrname)[count]
                        x = specialize_value(ARGTYPE, x)
                        args = args + (x, )
                    try:
                        result = support.maybe_on_top_of_llinterp(
                            rtyper, portal_ptr)(*args)
                    except jitexc.JitException as e:
                        continue
                    if result_kind != 'void':
                        result = unspecialize_value(result)
                    return result
                #
                if result_kind == 'void':
                    if isinstance(e, jitexc.DoneWithThisFrameVoid):
                        return None
                if result_kind == 'int':
                    if isinstance(e, jitexc.DoneWithThisFrameInt):
                        return e.result
                if result_kind == 'ref':
                    if isinstance(e, jitexc.DoneWithThisFrameRef):
                        return e.result
                if result_kind == 'float':
                    if isinstance(e, jitexc.DoneWithThisFrameFloat):
                        return e.result
                #
                if isinstance(e, jitexc.ExitFrameWithExceptionRef):
                    value = ts.cast_to_baseclass(e.value)
                    if not we_are_translated():
                        raise LLException(ts.get_typeptr(value), value)
                    else:
                        value = cast_base_ptr_to_instance(Exception, value)
                        assert value is not None
                        raise value
                #
                raise AssertionError("all cases should have been handled")

        jd._ll_portal_runner = ll_portal_runner  # for debugging
        jd.portal_runner_ptr = self.helper_func(jd._PTR_PORTAL_FUNCTYPE,
                                                ll_portal_runner)
        jd.portal_runner_adr = llmemory.cast_ptr_to_adr(jd.portal_runner_ptr)
        jd.portal_calldescr = self.cpu.calldescrof(
            jd._PTR_PORTAL_FUNCTYPE.TO, jd._PTR_PORTAL_FUNCTYPE.TO.ARGS,
            jd._PTR_PORTAL_FUNCTYPE.TO.RESULT, EffectInfo.MOST_GENERAL)

        vinfo = jd.virtualizable_info

        def assembler_call_helper(deadframe, virtualizableref):
            fail_descr = self.cpu.get_latest_descr(deadframe)
            try:
                fail_descr.handle_fail(deadframe, self.metainterp_sd, jd)
            except jitexc.JitException as e:
                return handle_jitexception(e)
            else:
                assert 0, "should have raised"

        jd._assembler_call_helper = assembler_call_helper  # for debugging
        jd._assembler_helper_ptr = self.helper_func(
            jd._PTR_ASSEMBLER_HELPER_FUNCTYPE, assembler_call_helper)
        jd.assembler_helper_adr = llmemory.cast_ptr_to_adr(
            jd._assembler_helper_ptr)
        if vinfo is not None:
            jd.vable_token_descr = vinfo.vable_token_descr

        def handle_jitexception_from_blackhole(bhcaller, e):
            result = handle_jitexception(e)
            if result_kind == 'void':
                pass
            elif result_kind == 'int':
                bhcaller._setup_return_value_i(result)
            elif result_kind == 'ref':
                bhcaller._setup_return_value_r(result)
            elif result_kind == 'float':
                bhcaller._setup_return_value_f(result)
            else:
                assert False

        jd.handle_jitexc_from_bh = handle_jitexception_from_blackhole

        # ____________________________________________________________
        # Now mutate origportalgraph to end with a call to portal_runner_ptr
        #
        origblock, origindex, op = locate_jit_merge_point(origportalgraph)
        assert op.opname == 'jit_marker'
        assert op.args[0].value == 'jit_merge_point'
        greens_v, reds_v = support.decode_hp_hint_args(op)
        vlist = [Constant(jd.portal_runner_ptr, jd._PTR_PORTAL_FUNCTYPE)]
        vlist += greens_v
        vlist += reds_v
        v_result = Variable()
        v_result.concretetype = PORTALFUNC.RESULT
        newop = SpaceOperation('direct_call', vlist, v_result)
        del origblock.operations[origindex:]
        origblock.operations.append(newop)
        origblock.exitswitch = None
        origblock.recloseblock(Link([v_result], origportalgraph.returnblock))
        # the origportal now can raise (even if it did not raise before),
        # which means that we cannot inline it anywhere any more, but that's
        # fine since any forced inlining has been done before
        #
        checkgraph(origportalgraph)

    def add_finish(self):
        def finish():
            if self.metainterp_sd.profiler.initialized:
                self.metainterp_sd.profiler.finish()
            self.metainterp_sd.cpu.finish_once()

        if self.cpu.translate_support_code:
            call_final_function(self.translator,
                                finish,
                                annhelper=self.annhelper)

    def rewrite_set_param_and_get_stats(self):
        from rpython.rtyper.lltypesystem.rstr import STR

        closures = {}
        graphs = self.translator.graphs
        _, PTR_SET_PARAM_FUNCTYPE = self.cpu.ts.get_FuncType([lltype.Signed],
                                                             lltype.Void)
        _, PTR_SET_PARAM_STR_FUNCTYPE = self.cpu.ts.get_FuncType(
            [lltype.Ptr(STR)], lltype.Void)

        def make_closure(jd, fullfuncname, is_string):
            if jd is None:

                def closure(i):
                    if is_string:
                        i = hlstr(i)
                    for jd in self.jitdrivers_sd:
                        getattr(jd.warmstate, fullfuncname)(i)
            else:
                state = jd.warmstate

                def closure(i):
                    if is_string:
                        i = hlstr(i)
                    getattr(state, fullfuncname)(i)

            if is_string:
                TP = PTR_SET_PARAM_STR_FUNCTYPE
            else:
                TP = PTR_SET_PARAM_FUNCTYPE
            funcptr = self.helper_func(TP, closure)
            return Constant(funcptr, TP)

        #
        for graph, block, i in find_set_param(graphs):

            op = block.operations[i]
            if op.args[1].value is not None:
                for jd in self.jitdrivers_sd:
                    if jd.jitdriver is op.args[1].value:
                        break
                else:
                    assert 0, "jitdriver of set_param() not found"
            else:
                jd = None
            funcname = op.args[2].value
            key = jd, funcname
            if key not in closures:
                closures[key] = make_closure(jd, 'set_param_' + funcname,
                                             funcname == 'enable_opts')
            op.opname = 'direct_call'
            op.args[:3] = [closures[key]]

    def rewrite_force_virtual(self, vrefinfo):
        all_graphs = self.translator.graphs
        vrefinfo.replace_force_virtual_with_call(all_graphs)

    def replace_force_quasiimmut_with_direct_call(self, op):
        ARG = op.args[0].concretetype
        mutatefieldname = op.args[1].value
        key = (ARG, mutatefieldname)
        if key in self._cache_force_quasiimmed_funcs:
            cptr = self._cache_force_quasiimmed_funcs[key]
        else:
            from rpython.jit.metainterp import quasiimmut
            func = quasiimmut.make_invalidation_function(ARG, mutatefieldname)
            FUNC = lltype.Ptr(lltype.FuncType([ARG], lltype.Void))
            llptr = self.helper_func(FUNC, func)
            cptr = Constant(llptr, FUNC)
            self._cache_force_quasiimmed_funcs[key] = cptr
        op.opname = 'direct_call'
        op.args = [cptr, op.args[0]]

    def rewrite_force_quasi_immutable(self):
        self._cache_force_quasiimmed_funcs = {}
        graphs = self.translator.graphs
        for graph, block, i in find_force_quasi_immutable(graphs):
            self.replace_force_quasiimmut_with_direct_call(block.operations[i])
Esempio n. 37
0
class ExceptionTransformer(object):
    def __init__(self, translator):
        self.translator = translator
        self.raise_analyzer = canraise.RaiseAnalyzer(translator)
        edata = translator.rtyper.exceptiondata
        self.lltype_of_exception_value = edata.lltype_of_exception_value
        self.lltype_of_exception_type = edata.lltype_of_exception_type
        self.mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
        exc_data, null_type, null_value = self.setup_excdata()

        (assertion_error_ll_exc_type,
         assertion_error_ll_exc) = self.get_builtin_exception(AssertionError)
        (n_i_error_ll_exc_type,
         n_i_error_ll_exc) = self.get_builtin_exception(NotImplementedError)

        self.c_assertion_error_ll_exc_type = constant_value(
            assertion_error_ll_exc_type)
        self.c_n_i_error_ll_exc_type = constant_value(n_i_error_ll_exc_type)

        def rpyexc_occured():
            exc_type = exc_data.exc_type
            return bool(exc_type)

        def rpyexc_fetch_type():
            return exc_data.exc_type

        def rpyexc_fetch_value():
            return exc_data.exc_value

        def rpyexc_clear():
            exc_data.exc_type = null_type
            exc_data.exc_value = null_value

        def rpyexc_raise(etype, evalue):
            # When compiling in debug mode, the following ll_asserts will
            # crash the program as soon as it raises AssertionError or
            # NotImplementedError.  Useful when you are in a debugger.
            # When compiling in release mode, AssertionErrors and
            # NotImplementedErrors are raised normally, and only later
            # caught by debug_catch_exception and printed, which allows
            # us to see at least part of the traceback for them.
            ll_assert(etype != assertion_error_ll_exc_type, "AssertionError")
            ll_assert(etype != n_i_error_ll_exc_type, "NotImplementedError")
            exc_data.exc_type = etype
            exc_data.exc_value = evalue
            lloperation.llop.debug_start_traceback(lltype.Void, etype)

        def rpyexc_reraise(etype, evalue):
            exc_data.exc_type = etype
            exc_data.exc_value = evalue
            lloperation.llop.debug_reraise_traceback(lltype.Void, etype)

        def rpyexc_fetch_exception():
            evalue = rpyexc_fetch_value()
            rpyexc_clear()
            return evalue

        def rpyexc_restore_exception(evalue):
            if evalue:
                exc_data.exc_type = ll_inst_type(evalue)
                exc_data.exc_value = evalue

        self.rpyexc_occured_ptr = self.build_func("RPyExceptionOccurred",
                                                  rpyexc_occured, [],
                                                  lltype.Bool)

        self.rpyexc_fetch_type_ptr = self.build_func(
            "RPyFetchExceptionType", rpyexc_fetch_type, [],
            self.lltype_of_exception_type)

        self.rpyexc_fetch_value_ptr = self.build_func(
            "RPyFetchExceptionValue", rpyexc_fetch_value, [],
            self.lltype_of_exception_value)

        self.rpyexc_clear_ptr = self.build_func("RPyClearException",
                                                rpyexc_clear, [], lltype.Void)

        self.rpyexc_raise_ptr = self.build_func(
            "RPyRaiseException",
            self.noinline(rpyexc_raise),
            [self.lltype_of_exception_type, self.lltype_of_exception_value],
            lltype.Void,
            jitcallkind='rpyexc_raise')  # for the JIT

        self.rpyexc_reraise_ptr = self.build_func(
            "RPyReRaiseException",
            rpyexc_reraise,
            [self.lltype_of_exception_type, self.lltype_of_exception_value],
            lltype.Void,
            jitcallkind='rpyexc_raise')  # for the JIT

        self.rpyexc_fetch_exception_ptr = self.build_func(
            "RPyFetchException", rpyexc_fetch_exception, [],
            self.lltype_of_exception_value)

        self.rpyexc_restore_exception_ptr = self.build_func(
            "RPyRestoreException", self.noinline(rpyexc_restore_exception),
            [self.lltype_of_exception_value], lltype.Void)

        self.build_extra_funcs()

        self.mixlevelannotator.finish()
        self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping(
        )

    def noinline(self, fn):
        fn = func_with_new_name(fn, fn.__name__)
        fn._dont_inline_ = True
        return fn

    def build_func(self, name, fn, inputtypes, rettype, **kwds):
        l2a = lltype_to_annotation
        graph = self.mixlevelannotator.getgraph(fn, map(l2a, inputtypes),
                                                l2a(rettype))
        return self.constant_func(name,
                                  inputtypes,
                                  rettype,
                                  graph,
                                  exception_policy="exc_helper",
                                  **kwds)

    def get_builtin_exception(self, Class):
        edata = self.translator.rtyper.exceptiondata
        bk = self.translator.annotator.bookkeeper
        error_def = bk.getuniqueclassdef(Class)
        error_ll_exc = edata.get_standard_ll_exc_instance(
            self.translator.rtyper, error_def)
        error_ll_exc_type = ll_inst_type(error_ll_exc)
        return error_ll_exc_type, error_ll_exc

    def transform_completely(self):
        for graph in self.translator.graphs:
            self.create_exception_handling(graph)

    def create_exception_handling(self, graph):
        """After an exception in a direct_call (or indirect_call), that is not caught
        by an explicit
        except statement, we need to reraise the exception. So after this
        direct_call we need to test if an exception had occurred. If so, we return
        from the current graph with a special value (False/-1/-1.0/null).
        Because of the added exitswitch we need an additional block.
        """
        if hasattr(graph, 'exceptiontransformed'):
            assert self.same_obj(self.exc_data_ptr, graph.exceptiontransformed)
            return
        else:
            self.raise_analyzer.analyze_direct_call(graph)
            graph.exceptiontransformed = self.exc_data_ptr

        join_blocks(graph)
        # collect the blocks before changing them
        n_need_exc_matching_blocks = 0
        n_gen_exc_checks = 0
        #
        entrymap = mkentrymap(graph)
        if graph.exceptblock in entrymap:
            for link in entrymap[graph.exceptblock]:
                self.transform_jump_to_except_block(graph, entrymap, link)
        #
        for block in list(graph.iterblocks()):
            self.replace_fetch_restore_operations(block)
            need_exc_matching, gen_exc_checks = self.transform_block(
                graph, block)
            n_need_exc_matching_blocks += need_exc_matching
            n_gen_exc_checks += gen_exc_checks
        cleanup_graph(graph)
        return n_need_exc_matching_blocks, n_gen_exc_checks

    def replace_fetch_restore_operations(self, block):
        # the gctransformer will create these operations.  It looks as if the
        # order of transformations is important - but the gctransformer will
        # put them in a new graph, so all transformations will run again.
        for i in range(len(block.operations)):
            opname = block.operations[i].opname
            if opname == 'gc_fetch_exception':
                block.operations[i].opname = "direct_call"
                block.operations[i].args = [self.rpyexc_fetch_exception_ptr]

            elif opname == 'gc_restore_exception':
                block.operations[i].opname = "direct_call"
                block.operations[i].args.insert(
                    0, self.rpyexc_restore_exception_ptr)
            elif opname == 'get_exception_addr':  # only for lltype
                block.operations[i].opname = "direct_call"
                block.operations[i].args.insert(
                    0, self.rpyexc_get_exception_addr_ptr)
            elif opname == 'get_exc_value_addr':  # only for lltype
                block.operations[i].opname = "direct_call"
                block.operations[i].args.insert(
                    0, self.rpyexc_get_exc_value_addr_ptr)

    def transform_block(self, graph, block):
        need_exc_matching = False
        n_gen_exc_checks = 0
        if block is graph.exceptblock:
            return need_exc_matching, n_gen_exc_checks
        elif block is graph.returnblock:
            return need_exc_matching, n_gen_exc_checks
        last_operation = len(block.operations) - 1
        if block.exitswitch == c_last_exception:
            need_exc_matching = True
            last_operation -= 1
        elif (len(block.exits) == 1
              and block.exits[0].target is graph.returnblock
              and len(block.operations)
              and (block.exits[0].args[0].concretetype is lltype.Void
                   or block.exits[0].args[0] is block.operations[-1].result)
              and block.operations[-1].opname
              not in ('malloc', 'malloc_varsize')):  # special cases
            last_operation -= 1
        lastblock = block
        for i in range(last_operation, -1, -1):
            op = block.operations[i]
            if not self.raise_analyzer.can_raise(op):
                continue

            splitlink = split_block(None, block, i + 1)
            afterblock = splitlink.target
            if lastblock is block:
                lastblock = afterblock

            self.gen_exc_check(block, graph.returnblock, afterblock)
            n_gen_exc_checks += 1
        if need_exc_matching:
            assert lastblock.exitswitch == c_last_exception
            if not self.raise_analyzer.can_raise(lastblock.operations[-1]):
                lastblock.exitswitch = None
                lastblock.recloseblock(lastblock.exits[0])
                lastblock.exits[0].exitcase = None
            else:
                self.insert_matching(lastblock, graph)
        return need_exc_matching, n_gen_exc_checks

    def comes_from_last_exception(self, entrymap, link):
        seen = set()
        pending = [(link, link.args[1])]
        while pending:
            link, v = pending.pop()
            if (link, v) in seen:
                continue
            seen.add((link, v))
            if link.last_exc_value is not None and v is link.last_exc_value:
                return True
            block = link.prevblock
            if block is None:
                continue
            for op in block.operations[::-1]:
                if v is op.result:
                    if op.opname == 'cast_pointer':
                        v = op.args[0]
                    else:
                        break
            for link in entrymap.get(block, ()):
                for v1, v2 in zip(link.args, block.inputargs):
                    if v2 is v:
                        pending.append((link, v1))
        return False

    def transform_jump_to_except_block(self, graph, entrymap, link):
        reraise = self.comes_from_last_exception(entrymap, link)
        result = Variable()
        result.concretetype = lltype.Void
        block = Block([v.copy() for v in graph.exceptblock.inputargs])
        if reraise:
            block.operations = [
                SpaceOperation("direct_call",
                               [self.rpyexc_reraise_ptr] + block.inputargs,
                               result),
            ]
        else:
            block.operations = [
                SpaceOperation("direct_call",
                               [self.rpyexc_raise_ptr] + block.inputargs,
                               result),
                SpaceOperation('debug_record_traceback', [],
                               varoftype(lltype.Void)),
            ]
        link.target = block
        RETTYPE = graph.returnblock.inputargs[0].concretetype
        l = Link([error_constant(RETTYPE)], graph.returnblock)
        block.recloseblock(l)

    def insert_matching(self, block, graph):
        proxygraph, op = self.create_proxy_graph(block.operations[-1])
        block.operations[-1] = op
        #non-exception case
        block.exits[0].exitcase = block.exits[0].llexitcase = None
        # use the dangerous second True flag :-)
        inliner = inline.OneShotInliner(
            self.translator,
            graph,
            self.lltype_to_classdef,
            inline_guarded_calls=True,
            inline_guarded_calls_no_matter_what=True,
            raise_analyzer=self.raise_analyzer)
        inliner.inline_once(block, len(block.operations) - 1)
        #block.exits[0].exitcase = block.exits[0].llexitcase = False

    def create_proxy_graph(self, op):
        """ creates a graph which calls the original function, checks for
        raised exceptions, fetches and then raises them again. If this graph is
        inlined, the correct exception matching blocks are produced."""
        # XXX slightly annoying: construct a graph by hand
        # but better than the alternative
        result = op.result.copy()
        opargs = []
        inputargs = []
        callargs = []
        ARGTYPES = []
        for var in op.args:
            if isinstance(var, Variable):
                v = Variable()
                v.concretetype = var.concretetype
                inputargs.append(v)
                opargs.append(v)
                callargs.append(var)
                ARGTYPES.append(var.concretetype)
            else:
                opargs.append(var)
        newop = SpaceOperation(op.opname, opargs, result)
        startblock = Block(inputargs)
        startblock.operations.append(newop)
        newgraph = FunctionGraph("dummy_exc1", startblock)
        startblock.closeblock(Link([result], newgraph.returnblock))
        newgraph.returnblock.inputargs[0].concretetype = op.result.concretetype
        self.gen_exc_check(startblock, newgraph.returnblock)
        excblock = Block([])

        llops = rtyper.LowLevelOpList(None)
        var_value = self.gen_getfield('exc_value', llops)
        var_type = self.gen_getfield('exc_type', llops)
        #
        c_check1 = self.c_assertion_error_ll_exc_type
        c_check2 = self.c_n_i_error_ll_exc_type
        llops.genop('debug_catch_exception', [var_type, c_check1, c_check2])
        #
        self.gen_setfield('exc_value', self.c_null_evalue, llops)
        self.gen_setfield('exc_type', self.c_null_etype, llops)
        excblock.operations[:] = llops
        newgraph.exceptblock.inputargs[
            0].concretetype = self.lltype_of_exception_type
        newgraph.exceptblock.inputargs[
            1].concretetype = self.lltype_of_exception_value
        excblock.closeblock(Link([var_type, var_value], newgraph.exceptblock))
        startblock.exits[True].target = excblock
        startblock.exits[True].args = []
        fptr = self.constant_func("dummy_exc1", ARGTYPES,
                                  op.result.concretetype, newgraph)
        return newgraph, SpaceOperation("direct_call", [fptr] + callargs,
                                        op.result)

    def gen_exc_check(self, block, returnblock, normalafterblock=None):
        llops = rtyper.LowLevelOpList(None)

        spaceop = block.operations[-1]
        alloc_shortcut = self.check_for_alloc_shortcut(spaceop)

        if alloc_shortcut:
            var_no_exc = self.gen_nonnull(spaceop.result, llops)
        else:
            v_exc_type = self.gen_getfield('exc_type', llops)
            var_no_exc = self.gen_isnull(v_exc_type, llops)
        #
        # We could add a "var_no_exc is likely true" hint, but it seems
        # not to help, so it was commented out again.
        #var_no_exc = llops.genop('likely', [var_no_exc], lltype.Bool)

        block.operations.extend(llops)

        block.exitswitch = var_no_exc
        #exception occurred case
        b = Block([])
        b.operations = [
            SpaceOperation('debug_record_traceback', [],
                           varoftype(lltype.Void))
        ]
        l = Link([error_constant(returnblock.inputargs[0].concretetype)],
                 returnblock)
        b.closeblock(l)
        l = Link([], b)
        l.exitcase = l.llexitcase = False

        #non-exception case
        l0 = block.exits[0]
        l0.exitcase = l0.llexitcase = True

        block.recloseblock(l0, l)
        insert_zeroing_op = False
        if spaceop.opname in ['malloc', 'malloc_varsize']:
            flavor = spaceop.args[1].value['flavor']
            if flavor == 'gc':
                insert_zeroing_op = True
            true_zero = spaceop.args[1].value.get('zero', False)
        # NB. when inserting more special-cases here, keep in mind that
        # you also need to list the opnames in transform_block()
        # (see "special cases")

        if insert_zeroing_op:
            if normalafterblock is None:
                normalafterblock = insert_empty_block(None, l0)
            v_result = spaceop.result
            if v_result in l0.args:
                result_i = l0.args.index(v_result)
                v_result_after = normalafterblock.inputargs[result_i]
            else:
                v_result_after = v_result.copy()
                l0.args.append(v_result)
                normalafterblock.inputargs.append(v_result_after)
            if true_zero:
                opname = "zero_everything_inside"
            else:
                opname = "zero_gc_pointers_inside"
            normalafterblock.operations.insert(
                0,
                SpaceOperation(opname, [v_result_after],
                               varoftype(lltype.Void)))

    def setup_excdata(self):
        EXCDATA = lltype.Struct('ExcData',
                                ('exc_type', self.lltype_of_exception_type),
                                ('exc_value', self.lltype_of_exception_value))
        self.EXCDATA = EXCDATA

        exc_data = lltype.malloc(EXCDATA, immortal=True)
        null_type = lltype.nullptr(self.lltype_of_exception_type.TO)
        null_value = lltype.nullptr(self.lltype_of_exception_value.TO)

        self.exc_data_ptr = exc_data
        self.cexcdata = Constant(exc_data, lltype.Ptr(self.EXCDATA))
        self.c_null_etype = Constant(null_type, self.lltype_of_exception_type)
        self.c_null_evalue = Constant(null_value,
                                      self.lltype_of_exception_value)

        return exc_data, null_type, null_value

    def constant_func(self, name, inputtypes, rettype, graph, **kwds):
        FUNC_TYPE = lltype.FuncType(inputtypes, rettype)
        fn_ptr = lltype.functionptr(FUNC_TYPE, name, graph=graph, **kwds)
        return Constant(fn_ptr, lltype.Ptr(FUNC_TYPE))

    def gen_getfield(self, name, llops):
        c_name = inputconst(lltype.Void, name)
        return llops.genop('getfield', [self.cexcdata, c_name],
                           resulttype=getattr(self.EXCDATA, name))

    def gen_setfield(self, name, v_value, llops):
        c_name = inputconst(lltype.Void, name)
        llops.genop('setfield', [self.cexcdata, c_name, v_value])

    def gen_isnull(self, v, llops):
        return llops.genop('ptr_iszero', [v], lltype.Bool)

    def gen_nonnull(self, v, llops):
        return llops.genop('ptr_nonzero', [v], lltype.Bool)

    def same_obj(self, ptr1, ptr2):
        return ptr1._same_obj(ptr2)

    def check_for_alloc_shortcut(self, spaceop):
        if spaceop.opname in ('malloc', 'malloc_varsize'):
            return True
        elif spaceop.opname == 'direct_call':
            fnobj = spaceop.args[0].value._obj
            if hasattr(fnobj, '_callable'):
                oopspec = getattr(fnobj._callable, 'oopspec', None)
                if oopspec and oopspec == 'newlist(length)':
                    return True
        return False

    def build_extra_funcs(self):
        EXCDATA = self.EXCDATA
        exc_data = self.exc_data_ptr

        def rpyexc_get_exception_addr():
            return (llmemory.cast_ptr_to_adr(exc_data) +
                    llmemory.offsetof(EXCDATA, 'exc_type'))

        def rpyexc_get_exc_value_addr():
            return (llmemory.cast_ptr_to_adr(exc_data) +
                    llmemory.offsetof(EXCDATA, 'exc_value'))

        self.rpyexc_get_exception_addr_ptr = self.build_func(
            "RPyGetExceptionAddr", rpyexc_get_exception_addr, [],
            llmemory.Address)

        self.rpyexc_get_exc_value_addr_ptr = self.build_func(
            "RPyGetExcValueAddr", rpyexc_get_exc_value_addr, [],
            llmemory.Address)
Esempio n. 38
0
def _annotate(rtyper, f, args_s, s_result):
    ann = MixLevelHelperAnnotator(rtyper)
    graph = ann.getgraph(f, args_s, s_result)
    ann.finish()
    return graph
Esempio n. 39
0
File: llmodel.py Progetto: Mu-L/pypy
    def _setup_frame_realloc(self, translate_support_code):
        FUNC_TP = lltype.Ptr(lltype.FuncType([llmemory.GCREF, lltype.Signed],
                                             llmemory.GCREF))
        base_ofs = self.get_baseofs_of_frame_field()

        def realloc_frame(frame, size):
            try:
                if not we_are_translated():
                    assert not self._exception_emulator[0]
                frame = lltype.cast_opaque_ptr(jitframe.JITFRAMEPTR, frame)
                if size > frame.jf_frame_info.jfi_frame_depth:
                    # update the frame_info size, which is for whatever reason
                    # not up to date
                    # frame info lives on assembler stack, so we need to enable
                    # writing
                    enter_assembler_writing()
                    frame.jf_frame_info.update_frame_depth(base_ofs, size)
                    leave_assembler_writing()
                new_frame = jitframe.JITFRAME.allocate(frame.jf_frame_info)
                frame.jf_forward = new_frame
                i = 0
                while i < len(frame.jf_frame):
                    new_frame.jf_frame[i] = frame.jf_frame[i]
                    frame.jf_frame[i] = 0
                    i += 1
                new_frame.jf_savedata = frame.jf_savedata
                new_frame.jf_guard_exc = frame.jf_guard_exc
                # all other fields are empty
                llop.gc_writebarrier(lltype.Void, new_frame)
                return lltype.cast_opaque_ptr(llmemory.GCREF, new_frame)
            except Exception as e:
                print "Unhandled exception", e, "in realloc_frame"
                return lltype.nullptr(llmemory.GCREF.TO)

        def realloc_frame_crash(frame, size):
            print "frame", frame, "size", size
            return lltype.nullptr(llmemory.GCREF.TO)

        if not translate_support_code:
            fptr = llhelper(FUNC_TP, realloc_frame)
        else:
            FUNC = FUNC_TP.TO
            args_s = [lltype_to_annotation(ARG) for ARG in FUNC.ARGS]
            s_result = lltype_to_annotation(FUNC.RESULT)
            mixlevelann = MixLevelHelperAnnotator(self.rtyper)
            graph = mixlevelann.getgraph(realloc_frame, args_s, s_result)
            fptr = mixlevelann.graph2delayed(graph, FUNC)
            mixlevelann.finish()
        self.realloc_frame = ptr2int(fptr)

        if not translate_support_code:
            fptr = llhelper(FUNC_TP, realloc_frame_crash)
        else:
            FUNC = FUNC_TP.TO
            args_s = [lltype_to_annotation(ARG) for ARG in FUNC.ARGS]
            s_result = lltype_to_annotation(FUNC.RESULT)
            mixlevelann = MixLevelHelperAnnotator(self.rtyper)
            graph = mixlevelann.getgraph(realloc_frame_crash, args_s, s_result)
            fptr = mixlevelann.graph2delayed(graph, FUNC)
            mixlevelann.finish()
        self.realloc_frame_crash = ptr2int(fptr)
Esempio n. 40
0
def _annotate(rtyper, f, args_s, s_result):
    ann = MixLevelHelperAnnotator(rtyper)
    graph = ann.getgraph(f, args_s, s_result)
    ann.finish()
    return graph
Esempio n. 41
0
class BaseGCTransformer(object):
    finished_helpers = False
    curr_block = None

    def __init__(self, translator, inline=False):
        self.translator = translator
        self.seen_graphs = set()
        self.prepared = False
        self.minimal_transform = set()
        if translator:
            self.mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
        else:
            self.mixlevelannotator = None
        self.inline = inline
        if translator and inline:
            self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping()
            self.raise_analyzer = RaiseAnalyzer(translator)
        self.graphs_to_inline = {}
        self.graph_dependencies = {}
        self.ll_finalizers_ptrs = []
        if self.MinimalGCTransformer:
            self.minimalgctransformer = self.MinimalGCTransformer(self)
        else:
            self.minimalgctransformer = None

    def get_lltype_of_exception_value(self):
        exceptiondata = self.translator.rtyper.exceptiondata
        return exceptiondata.lltype_of_exception_value

    def need_minimal_transform(self, graph):
        self.seen_graphs.add(graph)
        self.minimal_transform.add(graph)

    def inline_helpers_into(self, graph):
        from rpython.translator.backendopt.inline import iter_callsites
        to_enum = []
        for called, block, i in iter_callsites(graph, None):
            if called in self.graphs_to_inline:
                to_enum.append(called)
        any_inlining = False
        for inline_graph in to_enum:
            try:
                inline.inline_function(self.translator, inline_graph, graph,
                                       self.lltype_to_classdef,
                                       self.raise_analyzer,
                                       cleanup=False)
                any_inlining = True
            except inline.CannotInline as e:
                print 'CANNOT INLINE:', e
                print '\t%s into %s' % (inline_graph, graph)
                raise      # for now, make it a fatal error
        cleanup_graph(graph)
        if any_inlining:
            constant_fold_graph(graph)
        return any_inlining

    def inline_helpers_and_postprocess(self, graphs):
        for graph in graphs:
            any_inlining = self.inline and self.inline_helpers_into(graph)
            self.postprocess_graph(graph, any_inlining)

    def postprocess_graph(self, graph, any_inlining):
        pass

    def compute_borrowed_vars(self, graph):
        # the input args are borrowed, and stay borrowed for as long as they
        # are not merged with other values.
        var_families = DataFlowFamilyBuilder(graph).get_variable_families()
        borrowed_reps = {}
        for v in graph.getargs():
            borrowed_reps[var_families.find_rep(v)] = True
        # no support for returning borrowed values so far
        retvar = graph.getreturnvar()

        def is_borrowed(v1):
            return (var_families.find_rep(v1) in borrowed_reps
                    and v1 is not retvar)
        return is_borrowed

    def transform_block(self, block, is_borrowed):
        llops = LowLevelOpList()
        self.curr_block = block
        self.livevars = [var for var in block.inputargs
                    if var_needsgc(var) and not is_borrowed(var)]
        allvars = [var for var in block.getvariables() if var_needsgc(var)]
        self.var_last_needed_in = dict.fromkeys(allvars, 0)
        for i, op in enumerate(block.operations):
            for var in op.args:
                if not var_needsgc(var):
                    continue
                self.var_last_needed_in[var] = i
        for link in block.exits:
            for var in link.args:
                if not var_needsgc(var):
                    continue
                self.var_last_needed_in[var] = len(block.operations) + 1

        for i, op in enumerate(block.operations):
            hop = GcHighLevelOp(self, op, i, llops)
            hop.dispatch()

        if len(block.exits) != 0: # i.e not the return block
            assert not block.canraise

            deadinallexits = set(self.livevars)
            for link in block.exits:
                deadinallexits.difference_update(set(link.args))

            for var in deadinallexits:
                self.pop_alive(var, llops)

            for link in block.exits:
                livecounts = dict.fromkeys(set(self.livevars) - deadinallexits, 1)
                for v, v2 in zip(link.args, link.target.inputargs):
                    if is_borrowed(v2):
                        continue
                    if v in livecounts:
                        livecounts[v] -= 1
                    elif var_needsgc(v):
                        # 'v' is typically a Constant here, but it can be
                        # a borrowed variable going into a non-borrowed one
                        livecounts[v] = -1
                self.links_to_split[link] = livecounts

            block.operations[:] = llops
        self.livevars = None
        self.var_last_needed_in = None
        self.curr_block = None

    def start_transforming_graph(self, graph):
        pass    # for asmgcc.py

    def transform_graph(self, graph):
        if graph in self.minimal_transform:
            if self.minimalgctransformer:
                self.minimalgctransformer.transform_graph(graph)
            self.minimal_transform.remove(graph)
            return
        if graph in self.seen_graphs:
            return
        self.seen_graphs.add(graph)
        self.start_transforming_graph(graph)

        self.links_to_split = {} # link -> vars to pop_alive across the link

        # for sanity, we need an empty block at the start of the graph
        inserted_empty_startblock = False
        if not starts_with_empty_block(graph):
            insert_empty_startblock(graph)
            inserted_empty_startblock = True
        is_borrowed = self.compute_borrowed_vars(graph)

        for block in graph.iterblocks():
            self.transform_block(block, is_borrowed)

        for link, livecounts in self.links_to_split.iteritems():
            llops = LowLevelOpList()
            for var, livecount in livecounts.iteritems():
                for i in range(livecount):
                    self.pop_alive(var, llops)
                for i in range(-livecount):
                    self.push_alive(var, llops)
            if llops:
                if link.prevblock.exitswitch is None:
                    link.prevblock.operations.extend(llops)
                else:
                    insert_empty_block(link, llops)

        # remove the empty block at the start of the graph, which should
        # still be empty (but let's check)
        if starts_with_empty_block(graph) and inserted_empty_startblock:
            old_startblock = graph.startblock
            graph.startblock = graph.startblock.exits[0].target

        checkgraph(graph)

        self.links_to_split = None
        v = Variable('vanishing_exc_value')
        v.concretetype = self.get_lltype_of_exception_value()
        llops = LowLevelOpList()
        self.pop_alive(v, llops)
        graph.exc_cleanup = (v, list(llops))
        return is_borrowed    # xxx for tests only

    def annotate_helper(self, ll_helper, ll_args, ll_result, inline=False):
        assert not self.finished_helpers
        args_s = map(lltype_to_annotation, ll_args)
        s_result = lltype_to_annotation(ll_result)
        graph = self.mixlevelannotator.getgraph(ll_helper, args_s, s_result)
        # the produced graphs does not need to be fully transformed
        self.need_minimal_transform(graph)
        if inline:
            self.graphs_to_inline[graph] = True
        FUNCTYPE = lltype.FuncType(ll_args, ll_result)
        return self.mixlevelannotator.graph2delayed(graph, FUNCTYPE=FUNCTYPE)

    def inittime_helper(self, ll_helper, ll_args, ll_result, inline=True):
        ptr = self.annotate_helper(ll_helper, ll_args, ll_result, inline=inline)
        return Constant(ptr, lltype.typeOf(ptr))

    def annotate_finalizer(self, ll_finalizer, ll_args, ll_result):
        fptr = self.annotate_helper(ll_finalizer, ll_args, ll_result)
        self.ll_finalizers_ptrs.append(fptr)
        return fptr

    def finish_helpers(self, backendopt=True):
        if self.translator is not None:
            self.mixlevelannotator.finish_annotate()
        if self.translator is not None:
            self.mixlevelannotator.finish_rtype()
            if backendopt:
                self.mixlevelannotator.backend_optimize()
        self.finished_helpers = True
        # Make sure that the database also sees all finalizers now.
        # It is likely that the finalizers need special support there
        newgcdependencies = self.ll_finalizers_ptrs
        return newgcdependencies

    def get_finish_helpers(self):
        return self.finish_helpers

    def finish_tables(self):
        pass

    def get_finish_tables(self):
        return self.finish_tables

    def finish(self, backendopt=True):
        self.finish_helpers(backendopt=backendopt)
        self.finish_tables()

    def transform_generic_set(self, hop):
        opname = hop.spaceop.opname
        v_new = hop.spaceop.args[-1]
        v_old = hop.genop('g' + opname[1:],
                          hop.inputargs()[:-1],
                          resulttype=v_new.concretetype)
        self.push_alive(v_new, hop.llops)
        hop.rename('bare_' + opname)
        self.pop_alive(v_old, hop.llops)

    def push_alive(self, var, llops):
        pass

    def pop_alive(self, var, llops):
        pass

    def var_needs_set_transform(self, var):
        return False

    def default(self, hop):
        hop.llops.append(hop.spaceop)

    def gct_setfield(self, hop):
        if self.var_needs_set_transform(hop.spaceop.args[-1]):
            self.transform_generic_set(hop)
        else:
            hop.rename('bare_' + hop.spaceop.opname)
    gct_setarrayitem = gct_setfield
    gct_setinteriorfield = gct_setfield
    gct_raw_store = gct_setfield

    gct_getfield = default

    def gct_zero_gc_pointers_inside(self, hop):
        pass

    def gct_gc_writebarrier_before_copy(self, hop):
        # We take the conservative default and return False here, meaning
        # that rgc.ll_arraycopy() will do the copy by hand (i.e. with a
        # 'for' loop).  Subclasses that have their own logic, or that don't
        # need any kind of write barrier, may return True.
        op = hop.spaceop
        hop.genop("same_as",
                  [rmodel.inputconst(lltype.Bool, False)],
                  resultvar=op.result)

    def gct_gc_pin(self, hop):
        op = hop.spaceop
        hop.genop("same_as",
                    [rmodel.inputconst(lltype.Bool, False)],
                    resultvar=op.result)

    def gct_gc_unpin(self, hop):
        pass

    def gct_gc__is_pinned(self, hop):
        op = hop.spaceop
        hop.genop("same_as",
                  [rmodel.inputconst(lltype.Bool, False)],
                  resultvar=op.result)

    def gct_gc_identityhash(self, hop):
        # must be implemented in the various GCs
        raise NotImplementedError

    def gct_gc_id(self, hop):
        # this assumes a non-moving GC.  Moving GCs need to override this
        hop.rename('cast_ptr_to_int')

    def gct_gc_heap_stats(self, hop):
        from rpython.memory.gc.base import ARRAY_TYPEID_MAP

        return hop.cast_result(rmodel.inputconst(lltype.Ptr(ARRAY_TYPEID_MAP),
                                        lltype.nullptr(ARRAY_TYPEID_MAP)))
Esempio n. 42
0
class BaseGCTransformer(object):
    finished_helpers = False
    curr_block = None

    def __init__(self, translator, inline=False):
        self.translator = translator
        self.seen_graphs = set()
        self.prepared = False
        self.minimal_transform = set()
        if translator:
            self.mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
        else:
            self.mixlevelannotator = None
        self.inline = inline
        if translator and inline:
            self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping()
        self.graphs_to_inline = {}
        self.graph_dependencies = {}
        self.ll_finalizers_ptrs = []
        if self.MinimalGCTransformer:
            self.minimalgctransformer = self.MinimalGCTransformer(self)
        else:
            self.minimalgctransformer = None

    def get_lltype_of_exception_value(self):
        exceptiondata = self.translator.rtyper.exceptiondata
        return exceptiondata.lltype_of_exception_value

    def need_minimal_transform(self, graph):
        self.seen_graphs.add(graph)
        self.minimal_transform.add(graph)

    def inline_helpers(self, graphs):
        from rpython.translator.backendopt.inline import iter_callsites
        raise_analyzer = RaiseAnalyzer(self.translator)
        for graph in graphs:
            to_enum = []
            for called, block, i in iter_callsites(graph, None):
                if called in self.graphs_to_inline:
                    to_enum.append(called)
            must_constfold = False
            for inline_graph in to_enum:
                try:
                    inline.inline_function(self.translator, inline_graph, graph,
                                           self.lltype_to_classdef,
                                           raise_analyzer,
                                           cleanup=False)
                    must_constfold = True
                except inline.CannotInline as e:
                    print 'CANNOT INLINE:', e
                    print '\t%s into %s' % (inline_graph, graph)
            cleanup_graph(graph)
            if must_constfold:
                constant_fold_graph(graph)

    def compute_borrowed_vars(self, graph):
        # the input args are borrowed, and stay borrowed for as long as they
        # are not merged with other values.
        var_families = DataFlowFamilyBuilder(graph).get_variable_families()
        borrowed_reps = {}
        for v in graph.getargs():
            borrowed_reps[var_families.find_rep(v)] = True
        # no support for returning borrowed values so far
        retvar = graph.getreturnvar()

        def is_borrowed(v1):
            return (var_families.find_rep(v1) in borrowed_reps
                    and v1 is not retvar)
        return is_borrowed

    def transform_block(self, block, is_borrowed):
        llops = LowLevelOpList()
        self.curr_block = block
        self.livevars = [var for var in block.inputargs
                    if var_needsgc(var) and not is_borrowed(var)]
        allvars = [var for var in block.getvariables() if var_needsgc(var)]
        self.var_last_needed_in = dict.fromkeys(allvars, 0)
        for i, op in enumerate(block.operations):
            for var in op.args:
                if not var_needsgc(var):
                    continue
                self.var_last_needed_in[var] = i
        for link in block.exits:
            for var in link.args:
                if not var_needsgc(var):
                    continue
                self.var_last_needed_in[var] = len(block.operations) + 1

        for i, op in enumerate(block.operations):
            hop = GcHighLevelOp(self, op, i, llops)
            hop.dispatch()

        if len(block.exits) != 0: # i.e not the return block
            assert not block.canraise

            deadinallexits = set(self.livevars)
            for link in block.exits:
                deadinallexits.difference_update(set(link.args))

            for var in deadinallexits:
                self.pop_alive(var, llops)

            for link in block.exits:
                livecounts = dict.fromkeys(set(self.livevars) - deadinallexits, 1)
                for v, v2 in zip(link.args, link.target.inputargs):
                    if is_borrowed(v2):
                        continue
                    if v in livecounts:
                        livecounts[v] -= 1
                    elif var_needsgc(v):
                        # 'v' is typically a Constant here, but it can be
                        # a borrowed variable going into a non-borrowed one
                        livecounts[v] = -1
                self.links_to_split[link] = livecounts

            block.operations[:] = llops
        self.livevars = None
        self.var_last_needed_in = None
        self.curr_block = None

    def transform_graph(self, graph):
        if graph in self.minimal_transform:
            if self.minimalgctransformer:
                self.minimalgctransformer.transform_graph(graph)
            self.minimal_transform.remove(graph)
            return
        if graph in self.seen_graphs:
            return
        self.seen_graphs.add(graph)

        self.links_to_split = {} # link -> vars to pop_alive across the link

        # for sanity, we need an empty block at the start of the graph
        inserted_empty_startblock = False
        if not starts_with_empty_block(graph):
            insert_empty_startblock(graph)
            inserted_empty_startblock = True
        is_borrowed = self.compute_borrowed_vars(graph)

        for block in graph.iterblocks():
            self.transform_block(block, is_borrowed)

        for link, livecounts in self.links_to_split.iteritems():
            llops = LowLevelOpList()
            for var, livecount in livecounts.iteritems():
                for i in range(livecount):
                    self.pop_alive(var, llops)
                for i in range(-livecount):
                    self.push_alive(var, llops)
            if llops:
                if link.prevblock.exitswitch is None:
                    link.prevblock.operations.extend(llops)
                else:
                    insert_empty_block(link, llops)

        # remove the empty block at the start of the graph, which should
        # still be empty (but let's check)
        if starts_with_empty_block(graph) and inserted_empty_startblock:
            old_startblock = graph.startblock
            graph.startblock = graph.startblock.exits[0].target

        checkgraph(graph)

        self.links_to_split = None
        v = Variable('vanishing_exc_value')
        v.concretetype = self.get_lltype_of_exception_value()
        llops = LowLevelOpList()
        self.pop_alive(v, llops)
        graph.exc_cleanup = (v, list(llops))
        return is_borrowed    # xxx for tests only

    def annotate_helper(self, ll_helper, ll_args, ll_result, inline=False):
        assert not self.finished_helpers
        args_s = map(lltype_to_annotation, ll_args)
        s_result = lltype_to_annotation(ll_result)
        graph = self.mixlevelannotator.getgraph(ll_helper, args_s, s_result)
        # the produced graphs does not need to be fully transformed
        self.need_minimal_transform(graph)
        if inline:
            self.graphs_to_inline[graph] = True
        FUNCTYPE = lltype.FuncType(ll_args, ll_result)
        return self.mixlevelannotator.graph2delayed(graph, FUNCTYPE=FUNCTYPE)

    def inittime_helper(self, ll_helper, ll_args, ll_result, inline=True):
        ptr = self.annotate_helper(ll_helper, ll_args, ll_result, inline=inline)
        return Constant(ptr, lltype.typeOf(ptr))

    def annotate_finalizer(self, ll_finalizer, ll_args, ll_result):
        fptr = self.annotate_helper(ll_finalizer, ll_args, ll_result)
        self.ll_finalizers_ptrs.append(fptr)
        return fptr

    def finish_helpers(self, backendopt=True):
        if self.translator is not None:
            self.mixlevelannotator.finish_annotate()
        if self.translator is not None:
            self.mixlevelannotator.finish_rtype()
            if backendopt:
                self.mixlevelannotator.backend_optimize()
        self.finished_helpers = True
        # Make sure that the database also sees all finalizers now.
        # It is likely that the finalizers need special support there
        newgcdependencies = self.ll_finalizers_ptrs
        return newgcdependencies

    def get_finish_helpers(self):
        return self.finish_helpers

    def finish_tables(self):
        pass

    def get_finish_tables(self):
        return self.finish_tables

    def finish(self, backendopt=True):
        self.finish_helpers(backendopt=backendopt)
        self.finish_tables()

    def transform_generic_set(self, hop):
        opname = hop.spaceop.opname
        v_new = hop.spaceop.args[-1]
        v_old = hop.genop('g' + opname[1:],
                          hop.inputargs()[:-1],
                          resulttype=v_new.concretetype)
        self.push_alive(v_new, hop.llops)
        hop.rename('bare_' + opname)
        self.pop_alive(v_old, hop.llops)

    def push_alive(self, var, llops):
        pass

    def pop_alive(self, var, llops):
        pass

    def var_needs_set_transform(self, var):
        return False

    def default(self, hop):
        hop.llops.append(hop.spaceop)

    def gct_setfield(self, hop):
        if self.var_needs_set_transform(hop.spaceop.args[-1]):
            self.transform_generic_set(hop)
        else:
            hop.rename('bare_' + hop.spaceop.opname)
    gct_setarrayitem = gct_setfield
    gct_setinteriorfield = gct_setfield
    gct_raw_store = gct_setfield

    gct_getfield = default

    def gct_zero_gc_pointers_inside(self, hop):
        pass

    def gct_gc_writebarrier_before_copy(self, hop):
        # We take the conservative default and return False here, meaning
        # that rgc.ll_arraycopy() will do the copy by hand (i.e. with a
        # 'for' loop).  Subclasses that have their own logic, or that don't
        # need any kind of write barrier, may return True.
        op = hop.spaceop
        hop.genop("same_as",
                  [rmodel.inputconst(lltype.Bool, False)],
                  resultvar=op.result)

    def gct_gc_pin(self, hop):
        op = hop.spaceop
        hop.genop("same_as",
                    [rmodel.inputconst(lltype.Bool, False)],
                    resultvar=op.result)

    def gct_gc_unpin(self, hop):
        pass

    def gct_gc__is_pinned(self, hop):
        op = hop.spaceop
        hop.genop("same_as",
                  [rmodel.inputconst(lltype.Bool, False)],
                  resultvar=op.result)

    def gct_gc_identityhash(self, hop):
        # must be implemented in the various GCs
        raise NotImplementedError

    def gct_gc_id(self, hop):
        # this assumes a non-moving GC.  Moving GCs need to override this
        hop.rename('cast_ptr_to_int')

    def gct_gc_heap_stats(self, hop):
        from rpython.memory.gc.base import ARRAY_TYPEID_MAP

        return hop.cast_result(rmodel.inputconst(lltype.Ptr(ARRAY_TYPEID_MAP),
                                        lltype.nullptr(ARRAY_TYPEID_MAP)))

    def get_prebuilt_hash(self, obj):
        return None
Esempio n. 43
0
    def __init__(self, translator):
        self.translator = translator
        self.raise_analyzer = canraise.RaiseAnalyzer(translator)
        edata = translator.rtyper.exceptiondata
        self.lltype_of_exception_value = edata.lltype_of_exception_value
        self.lltype_of_exception_type = edata.lltype_of_exception_type
        self.mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
        exc_data, null_type, null_value = self.setup_excdata()

        (assertion_error_ll_exc_type,
         assertion_error_ll_exc) = self.get_builtin_exception(AssertionError)
        (n_i_error_ll_exc_type,
         n_i_error_ll_exc) = self.get_builtin_exception(NotImplementedError)

        self.c_assertion_error_ll_exc_type = constant_value(
            assertion_error_ll_exc_type)
        self.c_n_i_error_ll_exc_type = constant_value(n_i_error_ll_exc_type)

        def rpyexc_occured():
            exc_type = exc_data.exc_type
            return bool(exc_type)

        def rpyexc_fetch_type():
            return exc_data.exc_type

        def rpyexc_fetch_value():
            return exc_data.exc_value

        def rpyexc_clear():
            exc_data.exc_type = null_type
            exc_data.exc_value = null_value

        def rpyexc_raise(etype, evalue):
            # When compiling in debug mode, the following ll_asserts will
            # crash the program as soon as it raises AssertionError or
            # NotImplementedError.  Useful when you are in a debugger.
            # When compiling in release mode, AssertionErrors and
            # NotImplementedErrors are raised normally, and only later
            # caught by debug_catch_exception and printed, which allows
            # us to see at least part of the traceback for them.
            ll_assert(etype != assertion_error_ll_exc_type, "AssertionError")
            ll_assert(etype != n_i_error_ll_exc_type, "NotImplementedError")
            exc_data.exc_type = etype
            exc_data.exc_value = evalue
            lloperation.llop.debug_start_traceback(lltype.Void, etype)

        def rpyexc_reraise(etype, evalue):
            exc_data.exc_type = etype
            exc_data.exc_value = evalue
            lloperation.llop.debug_reraise_traceback(lltype.Void, etype)

        def rpyexc_fetch_exception():
            evalue = rpyexc_fetch_value()
            rpyexc_clear()
            return evalue

        def rpyexc_restore_exception(evalue):
            if evalue:
                exc_data.exc_type = ll_inst_type(evalue)
                exc_data.exc_value = evalue

        self.rpyexc_occured_ptr = self.build_func("RPyExceptionOccurred",
                                                  rpyexc_occured, [],
                                                  lltype.Bool)

        self.rpyexc_fetch_type_ptr = self.build_func(
            "RPyFetchExceptionType", rpyexc_fetch_type, [],
            self.lltype_of_exception_type)

        self.rpyexc_fetch_value_ptr = self.build_func(
            "RPyFetchExceptionValue", rpyexc_fetch_value, [],
            self.lltype_of_exception_value)

        self.rpyexc_clear_ptr = self.build_func("RPyClearException",
                                                rpyexc_clear, [], lltype.Void)

        self.rpyexc_raise_ptr = self.build_func(
            "RPyRaiseException",
            self.noinline(rpyexc_raise),
            [self.lltype_of_exception_type, self.lltype_of_exception_value],
            lltype.Void,
            jitcallkind='rpyexc_raise')  # for the JIT

        self.rpyexc_reraise_ptr = self.build_func(
            "RPyReRaiseException",
            rpyexc_reraise,
            [self.lltype_of_exception_type, self.lltype_of_exception_value],
            lltype.Void,
            jitcallkind='rpyexc_raise')  # for the JIT

        self.rpyexc_fetch_exception_ptr = self.build_func(
            "RPyFetchException", rpyexc_fetch_exception, [],
            self.lltype_of_exception_value)

        self.rpyexc_restore_exception_ptr = self.build_func(
            "RPyRestoreException", self.noinline(rpyexc_restore_exception),
            [self.lltype_of_exception_value], lltype.Void)

        self.build_extra_funcs()

        self.mixlevelannotator.finish()
        self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping(
        )
Esempio n. 44
0
class ExceptionTransformer(object):

    def __init__(self, translator):
        self.translator = translator
        self.raise_analyzer = canraise.RaiseAnalyzer(translator)
        edata = translator.rtyper.exceptiondata
        self.lltype_of_exception_value = edata.lltype_of_exception_value
        self.lltype_of_exception_type = edata.lltype_of_exception_type
        self.mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
        exc_data, null_type, null_value = self.setup_excdata()

        (assertion_error_ll_exc_type,
         assertion_error_ll_exc) = self.get_builtin_exception(AssertionError)
        (n_i_error_ll_exc_type,
         n_i_error_ll_exc) = self.get_builtin_exception(NotImplementedError)

        self.c_assertion_error_ll_exc_type = constant_value(
            assertion_error_ll_exc_type)
        self.c_n_i_error_ll_exc_type = constant_value(n_i_error_ll_exc_type)

        def rpyexc_occured():
            exc_type = exc_data.exc_type
            return bool(exc_type)

        def rpyexc_fetch_type():
            return exc_data.exc_type

        def rpyexc_fetch_value():
            return exc_data.exc_value

        def rpyexc_clear():
            exc_data.exc_type = null_type
            exc_data.exc_value = null_value

        def rpyexc_raise(etype, evalue):
            # When compiling in debug mode, the following ll_asserts will
            # crash the program as soon as it raises AssertionError or
            # NotImplementedError.  Useful when you are in a debugger.
            # When compiling in release mode, AssertionErrors and
            # NotImplementedErrors are raised normally, and only later
            # caught by debug_catch_exception and printed, which allows
            # us to see at least part of the traceback for them.
            ll_assert(etype != assertion_error_ll_exc_type, "AssertionError")
            ll_assert(etype != n_i_error_ll_exc_type, "NotImplementedError")
            exc_data.exc_type = etype
            exc_data.exc_value = evalue
            lloperation.llop.debug_start_traceback(lltype.Void, etype)

        def rpyexc_reraise(etype, evalue):
            exc_data.exc_type = etype
            exc_data.exc_value = evalue
            lloperation.llop.debug_reraise_traceback(lltype.Void, etype)

        def rpyexc_fetch_exception():
            evalue = rpyexc_fetch_value()
            rpyexc_clear()
            return evalue

        def rpyexc_restore_exception(evalue):
            if evalue:
                exc_data.exc_type = ll_inst_type(evalue)
                exc_data.exc_value = evalue

        self.rpyexc_occured_ptr = self.build_func(
            "RPyExceptionOccurred",
            rpyexc_occured,
            [], lltype.Bool)

        self.rpyexc_fetch_type_ptr = self.build_func(
            "RPyFetchExceptionType",
            rpyexc_fetch_type,
            [], self.lltype_of_exception_type)

        self.rpyexc_fetch_value_ptr = self.build_func(
            "RPyFetchExceptionValue",
            rpyexc_fetch_value,
            [], self.lltype_of_exception_value)

        self.rpyexc_clear_ptr = self.build_func(
            "RPyClearException",
            rpyexc_clear,
            [], lltype.Void)

        self.rpyexc_raise_ptr = self.build_func(
            "RPyRaiseException",
            self.noinline(rpyexc_raise),
            [self.lltype_of_exception_type, self.lltype_of_exception_value],
            lltype.Void,
            jitcallkind='rpyexc_raise') # for the JIT

        self.rpyexc_reraise_ptr = self.build_func(
            "RPyReRaiseException",
            rpyexc_reraise,
            [self.lltype_of_exception_type, self.lltype_of_exception_value],
            lltype.Void,
            jitcallkind='rpyexc_raise') # for the JIT

        self.rpyexc_fetch_exception_ptr = self.build_func(
            "RPyFetchException",
            rpyexc_fetch_exception,
            [], self.lltype_of_exception_value)

        self.rpyexc_restore_exception_ptr = self.build_func(
            "RPyRestoreException",
            self.noinline(rpyexc_restore_exception),
            [self.lltype_of_exception_value], lltype.Void)

        self.build_extra_funcs()

        self.mixlevelannotator.finish()
        self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping()

    def noinline(self, fn):
        fn = func_with_new_name(fn, fn.__name__)
        fn._dont_inline_ = True
        return fn

    def build_func(self, name, fn, inputtypes, rettype, **kwds):
        l2a = lltype_to_annotation
        graph = self.mixlevelannotator.getgraph(fn, map(l2a, inputtypes), l2a(rettype))
        return self.constant_func(name, inputtypes, rettype, graph,
                                  exception_policy="exc_helper", **kwds)

    def get_builtin_exception(self, Class):
        edata = self.translator.rtyper.exceptiondata
        bk = self.translator.annotator.bookkeeper
        error_def = bk.getuniqueclassdef(Class)
        error_ll_exc = edata.get_standard_ll_exc_instance(
            self.translator.rtyper, error_def)
        error_ll_exc_type = ll_inst_type(error_ll_exc)
        return error_ll_exc_type, error_ll_exc

    def transform_completely(self):
        for graph in self.translator.graphs:
            self.create_exception_handling(graph)

    def create_exception_handling(self, graph):
        """After an exception in a direct_call (or indirect_call), that is not caught
        by an explicit
        except statement, we need to reraise the exception. So after this
        direct_call we need to test if an exception had occurred. If so, we return
        from the current graph with a special value (False/-1/-1.0/null).
        Because of the added exitswitch we need an additional block.
        """
        if hasattr(graph, 'exceptiontransformed'):
            assert self.same_obj(self.exc_data_ptr, graph.exceptiontransformed)
            return
        else:
            self.raise_analyzer.analyze_direct_call(graph)
            graph.exceptiontransformed = self.exc_data_ptr

        join_blocks(graph)
        # collect the blocks before changing them
        n_need_exc_matching_blocks = 0
        n_gen_exc_checks           = 0
        #
        entrymap = mkentrymap(graph)
        if graph.exceptblock in entrymap:
            for link in entrymap[graph.exceptblock]:
                self.transform_jump_to_except_block(graph, entrymap, link)
        #
        for block in list(graph.iterblocks()):
            self.replace_fetch_restore_operations(block)
            need_exc_matching, gen_exc_checks = self.transform_block(graph, block)
            n_need_exc_matching_blocks += need_exc_matching
            n_gen_exc_checks           += gen_exc_checks
        cleanup_graph(graph)
        return n_need_exc_matching_blocks, n_gen_exc_checks

    def replace_fetch_restore_operations(self, block):
        # the gctransformer will create these operations.  It looks as if the
        # order of transformations is important - but the gctransformer will
        # put them in a new graph, so all transformations will run again.
        for i in range(len(block.operations)):
            opname = block.operations[i].opname
            if opname == 'gc_fetch_exception':
                block.operations[i].opname = "direct_call"
                block.operations[i].args = [self.rpyexc_fetch_exception_ptr]

            elif opname == 'gc_restore_exception':
                block.operations[i].opname = "direct_call"
                block.operations[i].args.insert(0, self.rpyexc_restore_exception_ptr)
            elif opname == 'get_exception_addr':    # only for lltype
                block.operations[i].opname = "direct_call"
                block.operations[i].args.insert(0, self.rpyexc_get_exception_addr_ptr)
            elif opname == 'get_exc_value_addr':    # only for lltype
                block.operations[i].opname = "direct_call"
                block.operations[i].args.insert(0, self.rpyexc_get_exc_value_addr_ptr)

    def transform_block(self, graph, block):
        need_exc_matching = False
        n_gen_exc_checks = 0
        if block is graph.exceptblock:
            return need_exc_matching, n_gen_exc_checks
        elif block is graph.returnblock:
            return need_exc_matching, n_gen_exc_checks
        last_operation = len(block.operations) - 1
        if block.exitswitch == c_last_exception:
            need_exc_matching = True
            last_operation -= 1
        elif (len(block.exits) == 1 and
              block.exits[0].target is graph.returnblock and
              len(block.operations) and
              (block.exits[0].args[0].concretetype is lltype.Void or
               block.exits[0].args[0] is block.operations[-1].result) and
              block.operations[-1].opname not in ('malloc',     # special cases
                                                  'malloc_nonmovable')):
            last_operation -= 1
        lastblock = block
        for i in range(last_operation, -1, -1):
            op = block.operations[i]
            if not self.raise_analyzer.can_raise(op):
                continue

            splitlink = split_block(None, block, i+1)
            afterblock = splitlink.target
            if lastblock is block:
                lastblock = afterblock

            self.gen_exc_check(block, graph.returnblock, afterblock)
            n_gen_exc_checks += 1
        if need_exc_matching:
            assert lastblock.exitswitch == c_last_exception
            if not self.raise_analyzer.can_raise(lastblock.operations[-1]):
                #print ("operation %s cannot raise, but has exception"
                #       " guarding in graph %s" % (lastblock.operations[-1],
                #                                  graph))
                lastblock.exitswitch = None
                lastblock.recloseblock(lastblock.exits[0])
                lastblock.exits[0].exitcase = None
            else:
                self.insert_matching(lastblock, graph)
        return need_exc_matching, n_gen_exc_checks

    def comes_from_last_exception(self, entrymap, link):
        seen = set()
        pending = [(link, link.args[1])]
        while pending:
            link, v = pending.pop()
            if (link, v) in seen:
                continue
            seen.add((link, v))
            if link.last_exc_value is not None and v is link.last_exc_value:
                return True
            block = link.prevblock
            if block is None:
                continue
            for op in block.operations[::-1]:
                if v is op.result:
                    if op.opname == 'cast_pointer':
                        v = op.args[0]
                    else:
                        break
            for link in entrymap.get(block, ()):
                for v1, v2 in zip(link.args, block.inputargs):
                    if v2 is v:
                        pending.append((link, v1))
        return False

    def transform_jump_to_except_block(self, graph, entrymap, link):
        reraise = self.comes_from_last_exception(entrymap, link)
        result = Variable()
        result.concretetype = lltype.Void
        block = Block([copyvar(None, v)
                       for v in graph.exceptblock.inputargs])
        if reraise:
            block.operations = [
                SpaceOperation("direct_call",
                               [self.rpyexc_reraise_ptr] + block.inputargs,
                               result),
                ]
        else:
            block.operations = [
                SpaceOperation("direct_call",
                               [self.rpyexc_raise_ptr] + block.inputargs,
                               result),
                SpaceOperation('debug_record_traceback', [],
                               varoftype(lltype.Void)),
                ]
        link.target = block
        RETTYPE = graph.returnblock.inputargs[0].concretetype
        l = Link([error_constant(RETTYPE)], graph.returnblock)
        block.recloseblock(l)

    def insert_matching(self, block, graph):
        proxygraph, op = self.create_proxy_graph(block.operations[-1])
        block.operations[-1] = op
        #non-exception case
        block.exits[0].exitcase = block.exits[0].llexitcase = None
        # use the dangerous second True flag :-)
        inliner = inline.OneShotInliner(
            self.translator, graph, self.lltype_to_classdef,
            inline_guarded_calls=True, inline_guarded_calls_no_matter_what=True,
            raise_analyzer=self.raise_analyzer)
        inliner.inline_once(block, len(block.operations)-1)
        #block.exits[0].exitcase = block.exits[0].llexitcase = False

    def create_proxy_graph(self, op):
        """ creates a graph which calls the original function, checks for
        raised exceptions, fetches and then raises them again. If this graph is
        inlined, the correct exception matching blocks are produced."""
        # XXX slightly annoying: construct a graph by hand
        # but better than the alternative
        result = copyvar(None, op.result)
        opargs = []
        inputargs = []
        callargs = []
        ARGTYPES = []
        for var in op.args:
            if isinstance(var, Variable):
                v = Variable()
                v.concretetype = var.concretetype
                inputargs.append(v)
                opargs.append(v)
                callargs.append(var)
                ARGTYPES.append(var.concretetype)
            else:
                opargs.append(var)
        newop = SpaceOperation(op.opname, opargs, result)
        startblock = Block(inputargs)
        startblock.operations.append(newop)
        newgraph = FunctionGraph("dummy_exc1", startblock)
        startblock.closeblock(Link([result], newgraph.returnblock))
        newgraph.returnblock.inputargs[0].concretetype = op.result.concretetype
        self.gen_exc_check(startblock, newgraph.returnblock)
        excblock = Block([])

        llops = rtyper.LowLevelOpList(None)
        var_value = self.gen_getfield('exc_value', llops)
        var_type  = self.gen_getfield('exc_type' , llops)
        #
        c_check1 = self.c_assertion_error_ll_exc_type
        c_check2 = self.c_n_i_error_ll_exc_type
        llops.genop('debug_catch_exception', [var_type, c_check1, c_check2])
        #
        self.gen_setfield('exc_value', self.c_null_evalue, llops)
        self.gen_setfield('exc_type',  self.c_null_etype,  llops)
        excblock.operations[:] = llops
        newgraph.exceptblock.inputargs[0].concretetype = self.lltype_of_exception_type
        newgraph.exceptblock.inputargs[1].concretetype = self.lltype_of_exception_value
        excblock.closeblock(Link([var_type, var_value], newgraph.exceptblock))
        startblock.exits[True].target = excblock
        startblock.exits[True].args = []
        fptr = self.constant_func("dummy_exc1", ARGTYPES, op.result.concretetype, newgraph)
        return newgraph, SpaceOperation("direct_call", [fptr] + callargs, op.result)

    def gen_exc_check(self, block, returnblock, normalafterblock=None):
        #var_exc_occured = Variable()
        #var_exc_occured.concretetype = lltype.Bool
        #block.operations.append(SpaceOperation("safe_call", [self.rpyexc_occured_ptr], var_exc_occured))

        llops = rtyper.LowLevelOpList(None)

        spaceop = block.operations[-1]
        alloc_shortcut = self.check_for_alloc_shortcut(spaceop)

        if alloc_shortcut:
            var_no_exc = self.gen_nonnull(spaceop.result, llops)
        else:
            v_exc_type = self.gen_getfield('exc_type', llops)
            var_no_exc = self.gen_isnull(v_exc_type, llops)

        block.operations.extend(llops)

        block.exitswitch = var_no_exc
        #exception occurred case
        b = Block([])
        b.operations = [SpaceOperation('debug_record_traceback', [],
                                       varoftype(lltype.Void))]
        l = Link([error_constant(returnblock.inputargs[0].concretetype)], returnblock)
        b.closeblock(l)
        l = Link([], b)
        l.exitcase = l.llexitcase = False

        #non-exception case
        l0 = block.exits[0]
        l0.exitcase = l0.llexitcase = True

        block.recloseblock(l0, l)

        insert_zeroing_op = False
        if spaceop.opname == 'malloc':
            flavor = spaceop.args[1].value['flavor']
            if flavor == 'gc':
                insert_zeroing_op = True
        elif spaceop.opname == 'malloc_nonmovable':
            # xxx we cannot insert zero_gc_pointers_inside after
            # malloc_nonmovable, because it can return null.  For now
            # we simply always force the zero=True flag on
            # malloc_nonmovable.
            c_flags = spaceop.args[1]
            c_flags.value = c_flags.value.copy()
            spaceop.args[1].value['zero'] = True
        # NB. when inserting more special-cases here, keep in mind that
        # you also need to list the opnames in transform_block()
        # (see "special cases")

        if insert_zeroing_op:
            if normalafterblock is None:
                normalafterblock = insert_empty_block(None, l0)
            v_result = spaceop.result
            if v_result in l0.args:
                result_i = l0.args.index(v_result)
                v_result_after = normalafterblock.inputargs[result_i]
            else:
                v_result_after = copyvar(None, v_result)
                l0.args.append(v_result)
                normalafterblock.inputargs.append(v_result_after)
            normalafterblock.operations.insert(
                0, SpaceOperation('zero_gc_pointers_inside',
                                  [v_result_after],
                                  varoftype(lltype.Void)))

    def setup_excdata(self):
        EXCDATA = lltype.Struct('ExcData',
            ('exc_type',  self.lltype_of_exception_type),
            ('exc_value', self.lltype_of_exception_value))
        self.EXCDATA = EXCDATA

        exc_data = lltype.malloc(EXCDATA, immortal=True)
        null_type = lltype.nullptr(self.lltype_of_exception_type.TO)
        null_value = lltype.nullptr(self.lltype_of_exception_value.TO)

        self.exc_data_ptr = exc_data
        self.cexcdata = Constant(exc_data, lltype.Ptr(self.EXCDATA))
        self.c_null_etype = Constant(null_type, self.lltype_of_exception_type)
        self.c_null_evalue = Constant(null_value, self.lltype_of_exception_value)

        return exc_data, null_type, null_value

    def constant_func(self, name, inputtypes, rettype, graph, **kwds):
        FUNC_TYPE = lltype.FuncType(inputtypes, rettype)
        fn_ptr = lltype.functionptr(FUNC_TYPE, name, graph=graph, **kwds)
        return Constant(fn_ptr, lltype.Ptr(FUNC_TYPE))

    def gen_getfield(self, name, llops):
        c_name = inputconst(lltype.Void, name)
        return llops.genop('getfield', [self.cexcdata, c_name],
                           resulttype = getattr(self.EXCDATA, name))

    def gen_setfield(self, name, v_value, llops):
        c_name = inputconst(lltype.Void, name)
        llops.genop('setfield', [self.cexcdata, c_name, v_value])

    def gen_isnull(self, v, llops):
        return llops.genop('ptr_iszero', [v], lltype.Bool)

    def gen_nonnull(self, v, llops):
        return llops.genop('ptr_nonzero', [v], lltype.Bool)

    def same_obj(self, ptr1, ptr2):
        return ptr1._same_obj(ptr2)

    def check_for_alloc_shortcut(self, spaceop):
        if spaceop.opname in ('malloc', 'malloc_varsize'):
            return True
        elif spaceop.opname == 'direct_call':
            fnobj = spaceop.args[0].value._obj
            if hasattr(fnobj, '_callable'):
                oopspec = getattr(fnobj._callable, 'oopspec', None)
                if oopspec and oopspec == 'newlist(length)':
                    return True
        return False

    def build_extra_funcs(self):
        EXCDATA = self.EXCDATA
        exc_data = self.exc_data_ptr

        def rpyexc_get_exception_addr():
            return (llmemory.cast_ptr_to_adr(exc_data) +
                    llmemory.offsetof(EXCDATA, 'exc_type'))

        def rpyexc_get_exc_value_addr():
            return (llmemory.cast_ptr_to_adr(exc_data) +
                    llmemory.offsetof(EXCDATA, 'exc_value'))

        self.rpyexc_get_exception_addr_ptr = self.build_func(
            "RPyGetExceptionAddr",
            rpyexc_get_exception_addr,
            [], llmemory.Address)

        self.rpyexc_get_exc_value_addr_ptr = self.build_func(
            "RPyGetExcValueAddr",
            rpyexc_get_exc_value_addr,
            [], llmemory.Address)
Esempio n. 45
0
    def __init__(self, translator):
        self.translator = translator
        self.raise_analyzer = canraise.RaiseAnalyzer(translator)
        edata = translator.rtyper.exceptiondata
        self.lltype_of_exception_value = edata.lltype_of_exception_value
        self.lltype_of_exception_type = edata.lltype_of_exception_type
        self.mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
        exc_data, null_type, null_value = self.setup_excdata()

        (assertion_error_ll_exc_type,
         assertion_error_ll_exc) = self.get_builtin_exception(AssertionError)
        (n_i_error_ll_exc_type,
         n_i_error_ll_exc) = self.get_builtin_exception(NotImplementedError)

        self.c_assertion_error_ll_exc_type = constant_value(
            assertion_error_ll_exc_type)
        self.c_n_i_error_ll_exc_type = constant_value(n_i_error_ll_exc_type)

        def rpyexc_occured():
            exc_type = exc_data.exc_type
            return bool(exc_type)

        def rpyexc_fetch_type():
            return exc_data.exc_type

        def rpyexc_fetch_value():
            return exc_data.exc_value

        def rpyexc_clear():
            exc_data.exc_type = null_type
            exc_data.exc_value = null_value

        def rpyexc_raise(etype, evalue):
            # When compiling in debug mode, the following ll_asserts will
            # crash the program as soon as it raises AssertionError or
            # NotImplementedError.  Useful when you are in a debugger.
            # When compiling in release mode, AssertionErrors and
            # NotImplementedErrors are raised normally, and only later
            # caught by debug_catch_exception and printed, which allows
            # us to see at least part of the traceback for them.
            ll_assert(etype != assertion_error_ll_exc_type, "AssertionError")
            ll_assert(etype != n_i_error_ll_exc_type, "NotImplementedError")
            exc_data.exc_type = etype
            exc_data.exc_value = evalue
            lloperation.llop.debug_start_traceback(lltype.Void, etype)

        def rpyexc_reraise(etype, evalue):
            exc_data.exc_type = etype
            exc_data.exc_value = evalue
            lloperation.llop.debug_reraise_traceback(lltype.Void, etype)

        def rpyexc_fetch_exception():
            evalue = rpyexc_fetch_value()
            rpyexc_clear()
            return evalue

        def rpyexc_restore_exception(evalue):
            if evalue:
                exc_data.exc_type = ll_inst_type(evalue)
                exc_data.exc_value = evalue

        self.rpyexc_occured_ptr = self.build_func(
            "RPyExceptionOccurred",
            rpyexc_occured,
            [], lltype.Bool)

        self.rpyexc_fetch_type_ptr = self.build_func(
            "RPyFetchExceptionType",
            rpyexc_fetch_type,
            [], self.lltype_of_exception_type)

        self.rpyexc_fetch_value_ptr = self.build_func(
            "RPyFetchExceptionValue",
            rpyexc_fetch_value,
            [], self.lltype_of_exception_value)

        self.rpyexc_clear_ptr = self.build_func(
            "RPyClearException",
            rpyexc_clear,
            [], lltype.Void)

        self.rpyexc_raise_ptr = self.build_func(
            "RPyRaiseException",
            self.noinline(rpyexc_raise),
            [self.lltype_of_exception_type, self.lltype_of_exception_value],
            lltype.Void,
            jitcallkind='rpyexc_raise') # for the JIT

        self.rpyexc_reraise_ptr = self.build_func(
            "RPyReRaiseException",
            rpyexc_reraise,
            [self.lltype_of_exception_type, self.lltype_of_exception_value],
            lltype.Void,
            jitcallkind='rpyexc_raise') # for the JIT

        self.rpyexc_fetch_exception_ptr = self.build_func(
            "RPyFetchException",
            rpyexc_fetch_exception,
            [], self.lltype_of_exception_value)

        self.rpyexc_restore_exception_ptr = self.build_func(
            "RPyRestoreException",
            self.noinline(rpyexc_restore_exception),
            [self.lltype_of_exception_value], lltype.Void)

        self.build_extra_funcs()

        self.mixlevelannotator.finish()
        self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping()
Esempio n. 46
0
class WarmRunnerDesc(object):

    def __init__(self, translator, policy=None, backendopt=True, CPUClass=None,
                 ProfilerClass=EmptyProfiler, **kwds):
        pyjitpl._warmrunnerdesc = self   # this is a global for debugging only!
        self.set_translator(translator)
        self.memory_manager = memmgr.MemoryManager()
        self.build_cpu(CPUClass, **kwds)
        self.inline_inlineable_portals()
        self.find_portals()
        self.codewriter = codewriter.CodeWriter(self.cpu, self.jitdrivers_sd)
        if policy is None:
            policy = JitPolicy()
        policy.set_supports_floats(self.cpu.supports_floats)
        policy.set_supports_longlong(self.cpu.supports_longlong)
        policy.set_supports_singlefloats(self.cpu.supports_singlefloats)
        graphs = self.codewriter.find_all_graphs(policy)
        policy.dump_unsafe_loops()
        self.check_access_directly_sanity(graphs)
        if backendopt:
            self.prejit_optimizations(policy, graphs)
        elif self.opt.listops:
            self.prejit_optimizations_minimal_inline(policy, graphs)

        self.build_meta_interp(ProfilerClass,
                             translator.config.translation.jit_opencoder_model)
        self.make_args_specifications()
        #
        from rpython.jit.metainterp.virtualref import VirtualRefInfo
        vrefinfo = VirtualRefInfo(self)
        self.codewriter.setup_vrefinfo(vrefinfo)
        #
        from rpython.jit.metainterp import counter
        if self.cpu.translate_support_code:
            self.jitcounter = counter.JitCounter(translator=translator)
        else:
            self.jitcounter = counter.DeterministicJitCounter()
        #
        self.hooks = policy.jithookiface
        self.make_virtualizable_infos()
        self.make_driverhook_graphs()
        self.make_enter_functions()
        self.rewrite_jit_merge_points(policy)

        verbose = False # not self.cpu.translate_support_code
        self.rewrite_access_helpers()
        self.create_jit_entry_points()
        jitcodes = self.codewriter.make_jitcodes(verbose=verbose)
        self.metainterp_sd.jitcodes = jitcodes
        self.rewrite_can_enter_jits()
        self.rewrite_set_param_and_get_stats()
        self.rewrite_force_virtual(vrefinfo)
        self.rewrite_jitcell_accesses()
        self.rewrite_force_quasi_immutable()
        self.add_finish()
        self.metainterp_sd.finish_setup(self.codewriter)

    def finish(self):
        vinfos = set([jd.virtualizable_info for jd in self.jitdrivers_sd])
        for vinfo in vinfos:
            if vinfo is not None:
                vinfo.finish()
        self.metainterp_sd.finish_setup_descrs()
        if self.cpu.translate_support_code:
            self.annhelper.finish()

    def _freeze_(self):
        return True

    def set_translator(self, translator):
        self.translator = translator
        self.rtyper = translator.rtyper
        self.gcdescr = gc.get_description(translator.config)

    def inline_inlineable_portals(self):
        """
        Find all the graphs which have been decorated with @jitdriver.inline
        and inline them in the callers, making them JIT portals. Then, create
        a fresh copy of the jitdriver for each of those new portals, because
        they cannot share the same one.  See
        test_ajit::test_inline_jit_merge_point
        """
        from rpython.translator.backendopt.inline import (
            inlinable_static_callers, auto_inlining)

        jmp_calls = {}
        def get_jmp_call(graph, _inline_jit_merge_point_):
            # there might be multiple calls to the @inlined function: the
            # first time we see it, we remove the call to the jit_merge_point
            # and we remember the corresponding op. Then, we create a new call
            # to it every time we need a new one (i.e., for each callsite
            # which becomes a new portal)
            try:
                op, jmp_graph = jmp_calls[graph]
            except KeyError:
                op, jmp_graph = fish_jmp_call(graph, _inline_jit_merge_point_)
                jmp_calls[graph] = op, jmp_graph
            #
            # clone the op
            newargs = op.args[:]
            newresult = Variable()
            newresult.concretetype = op.result.concretetype
            op = SpaceOperation(op.opname, newargs, newresult)
            return op, jmp_graph

        def fish_jmp_call(graph, _inline_jit_merge_point_):
            # graph is function which has been decorated with
            # @jitdriver.inline, so its very first op is a call to the
            # function which contains the actual jit_merge_point: fish it!
            jmp_block, op_jmp_call = next(callee.iterblockops())
            msg = ("The first operation of an _inline_jit_merge_point_ graph must be "
                   "a direct_call to the function passed to @jitdriver.inline()")
            assert op_jmp_call.opname == 'direct_call', msg
            jmp_funcobj = op_jmp_call.args[0].value._obj
            assert jmp_funcobj._callable is _inline_jit_merge_point_, msg
            jmp_block.operations.remove(op_jmp_call)
            return op_jmp_call, jmp_funcobj.graph

        # find all the graphs which call an @inline_in_portal function
        callgraph = inlinable_static_callers(self.translator.graphs, store_calls=True)
        new_callgraph = []
        new_portals = set()
        inlined_jit_merge_points = set()
        for caller, block, op_call, callee in callgraph:
            func = getattr(callee, 'func', None)
            _inline_jit_merge_point_ = getattr(func, '_inline_jit_merge_point_', None)
            if _inline_jit_merge_point_:
                _inline_jit_merge_point_._always_inline_ = True
                inlined_jit_merge_points.add(_inline_jit_merge_point_)
                op_jmp_call, jmp_graph = get_jmp_call(callee, _inline_jit_merge_point_)
                #
                # now we move the op_jmp_call from callee to caller, just
                # before op_call. We assume that the args passed to
                # op_jmp_call are the very same which are received by callee
                # (i.e., the one passed to op_call)
                assert len(op_call.args) == len(op_jmp_call.args)
                op_jmp_call.args[1:] = op_call.args[1:]
                idx = block.operations.index(op_call)
                block.operations.insert(idx, op_jmp_call)
                #
                # finally, we signal that we want to inline op_jmp_call into
                # caller, so that finally the actuall call to
                # driver.jit_merge_point will be seen there
                new_callgraph.append((caller, jmp_graph))
                new_portals.add(caller)

        # inline them!
        inline_threshold = 0.1 # we rely on the _always_inline_ set above
        auto_inlining(self.translator, inline_threshold, new_callgraph)
        # clean up _always_inline_ = True, it can explode later
        for item in inlined_jit_merge_points:
            del item._always_inline_

        # make a fresh copy of the JitDriver in all newly created
        # jit_merge_points
        self.clone_inlined_jit_merge_points(new_portals)

    def clone_inlined_jit_merge_points(self, graphs):
        """
        Find all the jit_merge_points in the given graphs, and replace the
        original JitDriver with a fresh clone.
        """
        if not graphs:
            return
        for graph, block, pos in find_jit_merge_points(graphs):
            op = block.operations[pos]
            v_driver = op.args[1]
            driver = v_driver.value
            if not driver.inline_jit_merge_point:
                continue
            new_driver = driver.clone()
            c_new_driver = Constant(new_driver, v_driver.concretetype)
            op.args[1] = c_new_driver

    def find_portals(self):
        self.jitdrivers_sd = []
        graphs = self.translator.graphs
        for graph, block, pos in find_jit_merge_points(graphs):
            support.autodetect_jit_markers_redvars(graph)
            self.split_graph_and_record_jitdriver(graph, block, pos)
        #
        assert (len(set([jd.jitdriver for jd in self.jitdrivers_sd])) ==
                len(self.jitdrivers_sd)), \
                "there are multiple jit_merge_points with the same jitdriver"

    def split_graph_and_record_jitdriver(self, graph, block, pos):
        op = block.operations[pos]
        jd = JitDriverStaticData()
        jd._jit_merge_point_in = graph
        args = op.args[2:]
        s_binding = self.translator.annotator.binding
        jd._portal_args_s = [s_binding(v) for v in args]
        graph = copygraph(graph)
        [jmpp] = find_jit_merge_points([graph])
        graph.startblock = support.split_before_jit_merge_point(*jmpp)
        # XXX this is incredibly obscure, but this is sometiems necessary
        #     so we don't explode in checkgraph. for reasons unknown this
        #     is not contanied within simplify_graph
        removenoops.remove_same_as(graph)
        # a crash in the following checkgraph() means that you forgot
        # to list some variable in greens=[] or reds=[] in JitDriver,
        # or that a jit_merge_point() takes a constant as an argument.
        checkgraph(graph)
        for v in graph.getargs():
            assert isinstance(v, Variable)
        assert len(dict.fromkeys(graph.getargs())) == len(graph.getargs())
        self.translator.graphs.append(graph)
        jd.portal_graph = graph
        # it's a bit unbelievable to have a portal without func
        assert hasattr(graph, "func")
        graph.func._dont_inline_ = True
        graph.func._jit_unroll_safe_ = True
        jd.jitdriver = block.operations[pos].args[1].value
        jd.vec = jd.jitdriver.vec
        jd.portal_runner_ptr = "<not set so far>"
        jd.result_type = history.getkind(jd.portal_graph.getreturnvar()
                                         .concretetype)[0]
        self.jitdrivers_sd.append(jd)

    def check_access_directly_sanity(self, graphs):
        from rpython.translator.backendopt.inline import collect_called_graphs
        jit_graphs = set(graphs)
        for graph in collect_called_graphs(self.translator.entry_point_graph,
                                           self.translator):
            if graph in jit_graphs:
                continue
            assert not getattr(graph, 'access_directly', False)

    def prejit_optimizations(self, policy, graphs):
        from rpython.translator.backendopt.all import backend_optimizations
        backend_optimizations(self.translator,
                              graphs=graphs,
                              merge_if_blocks=True,
                              constfold=True,
                              remove_asserts=True,
                              really_remove_asserts=True,
                              replace_we_are_jitted=False)

    def prejit_optimizations_minimal_inline(self, policy, graphs):
        from rpython.translator.backendopt.inline import auto_inline_graphs
        auto_inline_graphs(self.translator, graphs, 0.01)

    def build_cpu(self, CPUClass, translate_support_code=False,
                  no_stats=False, supports_floats=True,
                  supports_longlong=True, supports_singlefloats=True,
                  **kwds):
        assert CPUClass is not None
        self.opt = history.Options(**kwds)
        if no_stats:
            stats = history.NoStats()
        else:
            stats = history.Stats(None)
        self.stats = stats
        if translate_support_code:
            self.annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
        cpu = CPUClass(self.translator.rtyper, self.stats, self.opt,
                       translate_support_code, gcdescr=self.gcdescr)
        if not supports_floats:
            cpu.supports_floats = False
        if not supports_longlong:
            cpu.supports_longlong = False
        if not supports_singlefloats:
            cpu.supports_singlefloats = False
        self.cpu = cpu

    def build_meta_interp(self, ProfilerClass, opencoder_model):
        from rpython.jit.metainterp.opencoder import Model, BigModel
        self.metainterp_sd = MetaInterpStaticData(self.cpu,
                                                  self.opt,
                                                  ProfilerClass=ProfilerClass,
                                                  warmrunnerdesc=self)
        if opencoder_model == 'big':
            self.metainterp_sd.opencoder_model = BigModel
        else:
            self.metainterp_sd.opencoder_model = Model
        self.stats.metainterp_sd = self.metainterp_sd

    def make_virtualizable_infos(self):
        vinfos = {}
        for jd in self.jitdrivers_sd:
            #
            jd.greenfield_info = None
            for name in jd.jitdriver.greens:
                if '.' in name:
                    from rpython.jit.metainterp.greenfield import GreenFieldInfo
                    jd.greenfield_info = GreenFieldInfo(self.cpu, jd)
                    break
            #
            if not jd.jitdriver.virtualizables:
                jd.virtualizable_info = None
                jd.index_of_virtualizable = -1
                continue
            else:
                assert jd.greenfield_info is None, "XXX not supported yet"
            #
            jitdriver = jd.jitdriver
            assert len(jitdriver.virtualizables) == 1    # for now
            [vname] = jitdriver.virtualizables
            # XXX skip the Voids here too
            jd.index_of_virtualizable = jitdriver.reds.index(vname)
            #
            index = jd.num_green_args + jd.index_of_virtualizable
            VTYPEPTR = jd._JIT_ENTER_FUNCTYPE.ARGS[index]
            if VTYPEPTR not in vinfos:
                from rpython.jit.metainterp.virtualizable import VirtualizableInfo
                vinfos[VTYPEPTR] = VirtualizableInfo(self, VTYPEPTR)
            jd.virtualizable_info = vinfos[VTYPEPTR]

    def make_enter_functions(self):
        for jd in self.jitdrivers_sd:
            self.make_enter_function(jd)

    def make_enter_function(self, jd):
        from rpython.jit.metainterp.warmstate import WarmEnterState
        state = WarmEnterState(self, jd)
        maybe_compile_and_run, EnterJitAssembler = state.make_entry_point()
        jd.warmstate = state

        def crash_in_jit(e):
            tb = not we_are_translated() and sys.exc_info()[2]
            try:
                raise e
            except jitexc.JitException:
                raise     # go through
            except MemoryError:
                raise     # go through
            except StackOverflow:
                raise     # go through
            except Exception as e:
                if not we_are_translated():
                    print "~~~ Crash in JIT!"
                    print '~~~ %s: %s' % (e.__class__, e)
                    if sys.stdout == sys.__stdout__:
                        import pdb; pdb.post_mortem(tb)
                    raise e.__class__, e, tb
                fatalerror('~~~ Crash in JIT! %s' % (e,))
        crash_in_jit._dont_inline_ = True

        def maybe_enter_jit(*args):
            try:
                maybe_compile_and_run(state.increment_threshold, *args)
            except Exception as e:
                crash_in_jit(e)
        maybe_enter_jit._always_inline_ = True
        jd._maybe_enter_jit_fn = maybe_enter_jit
        jd._maybe_compile_and_run_fn = maybe_compile_and_run
        jd._EnterJitAssembler = EnterJitAssembler

    def make_driverhook_graphs(self):
        #
        annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
        for jd in self.jitdrivers_sd:
            jd._get_printable_location_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.get_printable_location,
                annmodel.SomeString())
            jd._get_unique_id_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.get_unique_id, annmodel.SomeInteger())
            jd._confirm_enter_jit_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.confirm_enter_jit, annmodel.s_Bool,
                onlygreens=False)
            jd._can_never_inline_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.can_never_inline, annmodel.s_Bool)
            jd._should_unroll_one_iteration_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.should_unroll_one_iteration,
                annmodel.s_Bool)
            #
            items = []
            types = ()
            pos = ()
            if jd.jitdriver.get_location:
                assert hasattr(jd.jitdriver.get_location, '_loc_types'), """
                You must decorate your get_location function:

                from rpython.rlib.rjitlog import rjitlog as jl
                @jl.returns(jl.MP_FILENAME, jl.MP_XXX, ...)
                def get_loc(your, green, keys):
                    name = "x.txt" # extract it from your green keys
                    return (name, ...)
                """
                types = jd.jitdriver.get_location._loc_types
                del jd.jitdriver.get_location._loc_types
                #
                for _,type in types:
                    if type == 's':
                        items.append(annmodel.SomeString())
                    elif type == 'i':
                        items.append(annmodel.SomeInteger())
                    else:
                        raise NotImplementedError
            s_Tuple = annmodel.SomeTuple(items)
            jd._get_location_ptr = self._make_hook_graph(jd,
                annhelper, jd.jitdriver.get_location, s_Tuple)
            jd._get_loc_types = types
        annhelper.finish()

    def _make_hook_graph(self, jitdriver_sd, annhelper, func,
                         s_result, s_first_arg=None, onlygreens=True):
        if func is None:
            return None
        #
        if not onlygreens:
            assert not jitdriver_sd.jitdriver.autoreds, (
                "reds='auto' is not compatible with JitDriver hooks such as "
                "confirm_enter_jit")
        extra_args_s = []
        if s_first_arg is not None:
            extra_args_s.append(s_first_arg)
        #
        args_s = jitdriver_sd._portal_args_s
        if onlygreens:
            args_s = args_s[:len(jitdriver_sd._green_args_spec)]
        graph = annhelper.getgraph(func, extra_args_s + args_s, s_result)
        funcptr = annhelper.graph2delayed(graph)
        return funcptr

    def make_args_specifications(self):
        for jd in self.jitdrivers_sd:
            self.make_args_specification(jd)

    def make_args_specification(self, jd):
        graph = jd._jit_merge_point_in
        _, _, op = locate_jit_merge_point(graph)
        greens_v, reds_v = support.decode_hp_hint_args(op)
        ALLARGS = [v.concretetype for v in (greens_v + reds_v)]
        jd._green_args_spec = [v.concretetype for v in greens_v]
        jd.red_args_types = [history.getkind(v.concretetype) for v in reds_v]
        jd.num_green_args = len(jd._green_args_spec)
        jd.num_red_args = len(jd.red_args_types)
        RESTYPE = graph.getreturnvar().concretetype
        (jd._JIT_ENTER_FUNCTYPE,
         jd._PTR_JIT_ENTER_FUNCTYPE) = self.cpu.ts.get_FuncType(ALLARGS, lltype.Void)
        (jd._PORTAL_FUNCTYPE,
         jd._PTR_PORTAL_FUNCTYPE) = self.cpu.ts.get_FuncType(ALLARGS, RESTYPE)
        #
        if jd.result_type == 'v':
            ASMRESTYPE = lltype.Void
        elif jd.result_type == history.INT:
            ASMRESTYPE = lltype.Signed
        elif jd.result_type == history.REF:
            ASMRESTYPE = llmemory.GCREF
        elif jd.result_type == history.FLOAT:
            ASMRESTYPE = lltype.Float
        else:
            assert False
        (_, jd._PTR_ASSEMBLER_HELPER_FUNCTYPE) = self.cpu.ts.get_FuncType(
            [llmemory.GCREF, llmemory.GCREF], ASMRESTYPE)

    def rewrite_jitcell_accesses(self):
        jitdrivers_by_name = {}
        for jd in self.jitdrivers_sd:
            name = jd.jitdriver.name
            if name != 'jitdriver':
                jitdrivers_by_name[name] = jd
        m = _find_jit_markers(self.translator.graphs,
                              ('get_jitcell_at_key', 'trace_next_iteration',
                               'dont_trace_here', 'trace_next_iteration_hash'))
        accessors = {}

        def get_accessor(name, jitdriver_name, function, ARGS, green_arg_spec):
            a = accessors.get((name, jitdriver_name))
            if a:
                return a
            d = {'function': function,
                 'cast_instance_to_gcref': cast_instance_to_gcref,
                 'lltype': lltype}
            arg_spec = ", ".join([("arg%d" % i) for i in range(len(ARGS))])
            arg_converters = []
            for i, spec in enumerate(green_arg_spec):
                if isinstance(spec, lltype.Ptr):
                    arg_converters.append("arg%d = lltype.cast_opaque_ptr(type%d, arg%d)" % (i, i, i))
                    d['type%d' % i] = spec
            convert = ";".join(arg_converters)
            if name == 'get_jitcell_at_key':
                exec py.code.Source("""
                def accessor(%s):
                    %s
                    return cast_instance_to_gcref(function(%s))
                """ % (arg_spec, convert, arg_spec)).compile() in d
                FUNC = lltype.Ptr(lltype.FuncType(ARGS, llmemory.GCREF))
            elif name == "trace_next_iteration_hash":
                exec py.code.Source("""
                def accessor(arg0):
                    function(arg0)
                """).compile() in d
                FUNC = lltype.Ptr(lltype.FuncType([lltype.Unsigned],
                                                  lltype.Void))
            else:
                exec py.code.Source("""
                def accessor(%s):
                    %s
                    function(%s)
                """ % (arg_spec, convert, arg_spec)).compile() in d
                FUNC = lltype.Ptr(lltype.FuncType(ARGS, lltype.Void))
            func = d['accessor']
            ll_ptr = self.helper_func(FUNC, func)
            accessors[(name, jitdriver_name)] = ll_ptr
            return ll_ptr

        for graph, block, index in m:
            op = block.operations[index]
            jitdriver_name = op.args[1].value
            JitCell = jitdrivers_by_name[jitdriver_name].warmstate.JitCell
            ARGS = [x.concretetype for x in op.args[2:]]
            if op.args[0].value == 'get_jitcell_at_key':
                func = JitCell.get_jitcell
            elif op.args[0].value == 'dont_trace_here':
                func = JitCell.dont_trace_here
            elif op.args[0].value == 'trace_next_iteration_hash':
                func = JitCell.trace_next_iteration_hash
            else:
                func = JitCell._trace_next_iteration
            argspec = jitdrivers_by_name[jitdriver_name]._green_args_spec
            accessor = get_accessor(op.args[0].value,
                                    jitdriver_name, func,
                                    ARGS, argspec)
            v_result = op.result
            c_accessor = Constant(accessor, concretetype=lltype.Void)
            newop = SpaceOperation('direct_call', [c_accessor] + op.args[2:],
                                   v_result)
            block.operations[index] = newop

    def rewrite_can_enter_jits(self):
        sublists = {}
        for jd in self.jitdrivers_sd:
            sublists[jd.jitdriver] = jd, []
            jd.no_loop_header = True
        #
        loop_headers = find_loop_headers(self.translator.graphs)
        for graph, block, index in loop_headers:
            op = block.operations[index]
            jitdriver = op.args[1].value
            assert jitdriver in sublists, \
                   "loop_header with no matching jit_merge_point"
            jd, sublist = sublists[jitdriver]
            jd.no_loop_header = False
        #
        can_enter_jits = find_can_enter_jit(self.translator.graphs)
        for graph, block, index in can_enter_jits:
            op = block.operations[index]
            jitdriver = op.args[1].value
            assert jitdriver in sublists, \
                   "can_enter_jit with no matching jit_merge_point"
            assert not jitdriver.autoreds, (
                   "can_enter_jit not supported with a jitdriver that "
                   "has reds='auto'")
            jd, sublist = sublists[jitdriver]
            origportalgraph = jd._jit_merge_point_in
            if graph is not origportalgraph:
                sublist.append((graph, block, index))
                jd.no_loop_header = False
            else:
                pass   # a 'can_enter_jit' before the 'jit-merge_point', but
                       # originally in the same function: we ignore it here
                       # see e.g. test_jitdriver.test_simple
        for jd in self.jitdrivers_sd:
            _, sublist = sublists[jd.jitdriver]
            self.rewrite_can_enter_jit(jd, sublist)

    def rewrite_can_enter_jit(self, jd, can_enter_jits):
        FUNCPTR = jd._PTR_JIT_ENTER_FUNCTYPE
        jit_enter_fnptr = self.helper_func(FUNCPTR, jd._maybe_enter_jit_fn)

        if len(can_enter_jits) == 0:
            # see test_warmspot.test_no_loop_at_all
            operations = jd.portal_graph.startblock.operations
            op1 = operations[0]
            assert (op1.opname == 'jit_marker' and
                    op1.args[0].value == 'jit_merge_point')
            op0 = SpaceOperation(
                'jit_marker',
                [Constant('can_enter_jit', lltype.Void)] + op1.args[1:],
                None)
            operations.insert(0, op0)
            can_enter_jits = [(jd.portal_graph, jd.portal_graph.startblock, 0)]

        for graph, block, index in can_enter_jits:
            if graph is jd._jit_merge_point_in:
                continue

            op = block.operations[index]
            greens_v, reds_v = support.decode_hp_hint_args(op)
            args_v = greens_v + reds_v

            vlist = [Constant(jit_enter_fnptr, FUNCPTR)] + args_v

            v_result = Variable()
            v_result.concretetype = lltype.Void
            newop = SpaceOperation('direct_call', vlist, v_result)
            block.operations[index] = newop

    def helper_func(self, FUNCPTR, func):
        if not self.cpu.translate_support_code:
            return llhelper(FUNCPTR, func)
        FUNC = FUNCPTR.TO
        args_s = [lltype_to_annotation(ARG) for ARG in FUNC.ARGS]
        s_result = lltype_to_annotation(FUNC.RESULT)
        graph = self.annhelper.getgraph(func, args_s, s_result)
        return self.annhelper.graph2delayed(graph, FUNC)

    def rewrite_access_helpers(self):
        ah = find_access_helpers(self.translator.graphs)
        for graph, block, index in ah:
            op = block.operations[index]
            self.rewrite_access_helper(op)

    def create_jit_entry_points(self):
        for func, args, result in all_jit_entrypoints:
            self.helper_func(lltype.Ptr(lltype.FuncType(args, result)), func)
            annotated_jit_entrypoints.append((func, None))

    def rewrite_access_helper(self, op):
        # make sure we make a copy of function so it no longer belongs
        # to extregistry
        func = op.args[1].value
        if func.func_name.startswith('stats_'):
            # get special treatment since we rewrite it to a call that accepts
            # jit driver
            assert len(op.args) >= 3, ("%r must have a first argument "
                                       "(which is None)" % (func,))
            func = func_with_new_name(func, func.func_name + '_compiled')

            def new_func(ignored, *args):
                return func(self, *args)
            ARGS = [lltype.Void] + [arg.concretetype for arg in op.args[3:]]
        else:
            ARGS = [arg.concretetype for arg in op.args[2:]]
            new_func = func_with_new_name(func, func.func_name + '_compiled')
        RESULT = op.result.concretetype
        FUNCPTR = lltype.Ptr(lltype.FuncType(ARGS, RESULT))
        ptr = self.helper_func(FUNCPTR, new_func)
        op.opname = 'direct_call'
        op.args = [Constant(ptr, FUNCPTR)] + op.args[2:]

    def rewrite_jit_merge_points(self, policy):
        for jd in self.jitdrivers_sd:
            self.rewrite_jit_merge_point(jd, policy)

    def rewrite_jit_merge_point(self, jd, policy):
        #
        # Mutate the original portal graph from this:
        #
        #       def original_portal(..):
        #           stuff
        #           while 1:
        #               jit_merge_point(*args)
        #               more stuff
        #
        # to that:
        #
        #       def original_portal(..):
        #           stuff
        #           return portal_runner(*args)
        #
        #       def portal_runner(*args):
        #           while 1:
        #               try:
        #                   return portal(*args)
        #               except JitException, e:
        #                   return handle_jitexception(e)
        #
        #       def portal(*args):
        #           while 1:
        #               more stuff
        #
        origportalgraph = jd._jit_merge_point_in
        portalgraph = jd.portal_graph
        PORTALFUNC = jd._PORTAL_FUNCTYPE

        # ____________________________________________________________
        # Prepare the portal_runner() helper
        #
        from rpython.jit.metainterp.warmstate import specialize_value
        from rpython.jit.metainterp.warmstate import unspecialize_value
        portal_ptr = self.cpu.ts.functionptr(PORTALFUNC, 'portal',
                                         graph=portalgraph)
        jd._portal_ptr = portal_ptr
        #
        portalfunc_ARGS = []
        nums = {}
        for i, ARG in enumerate(PORTALFUNC.ARGS):
            kind = history.getkind(ARG)
            assert kind != 'void'
            if i < len(jd.jitdriver.greens):
                color = 'green'
            else:
                color = 'red'
            attrname = '%s_%s' % (color, kind)
            count = nums.get(attrname, 0)
            nums[attrname] = count + 1
            portalfunc_ARGS.append((ARG, attrname, count))
        portalfunc_ARGS = unrolling_iterable(portalfunc_ARGS)
        #
        rtyper = self.translator.rtyper
        RESULT = PORTALFUNC.RESULT
        result_kind = history.getkind(RESULT)
        assert result_kind.startswith(jd.result_type)
        ts = self.cpu.ts
        state = jd.warmstate
        maybe_compile_and_run = jd._maybe_compile_and_run_fn
        EnterJitAssembler = jd._EnterJitAssembler

        def ll_portal_runner(*args):
            try:
                # maybe enter from the function's start.
                maybe_compile_and_run(
                    state.increment_function_threshold, *args)
                #
                # then run the normal portal function, i.e. the
                # interpreter's main loop.  It might enter the jit
                # via maybe_enter_jit(), which typically ends with
                # handle_fail() being called, which raises on the
                # following exceptions --- catched here, because we
                # want to interrupt the whole interpreter loop.
                return support.maybe_on_top_of_llinterp(rtyper,
                                                  portal_ptr)(*args)
            except jitexc.JitException as e:
                result = handle_jitexception(e)
                if result_kind != 'void':
                    result = specialize_value(RESULT, result)
                return result

        def handle_jitexception(e):
            # XXX there are too many exceptions all around...
            while True:
                if isinstance(e, EnterJitAssembler):
                    try:
                        return e.execute()
                    except jitexc.JitException as e:
                        continue
                #
                if isinstance(e, jitexc.ContinueRunningNormally):
                    args = ()
                    for ARGTYPE, attrname, count in portalfunc_ARGS:
                        x = getattr(e, attrname)[count]
                        x = specialize_value(ARGTYPE, x)
                        args = args + (x,)
                    try:
                        result = support.maybe_on_top_of_llinterp(rtyper,
                                                            portal_ptr)(*args)
                    except jitexc.JitException as e:
                        continue
                    if result_kind != 'void':
                        result = unspecialize_value(result)
                    return result
                #
                if result_kind == 'void':
                    if isinstance(e, jitexc.DoneWithThisFrameVoid):
                        return None
                if result_kind == 'int':
                    if isinstance(e, jitexc.DoneWithThisFrameInt):
                        return e.result
                if result_kind == 'ref':
                    if isinstance(e, jitexc.DoneWithThisFrameRef):
                        return e.result
                if result_kind == 'float':
                    if isinstance(e, jitexc.DoneWithThisFrameFloat):
                        return e.result
                #
                if isinstance(e, jitexc.ExitFrameWithExceptionRef):
                    value = ts.cast_to_baseclass(e.value)
                    if not we_are_translated():
                        raise LLException(ts.get_typeptr(value), value)
                    else:
                        value = cast_base_ptr_to_instance(Exception, value)
                        assert value is not None
                        raise value
                #
                raise AssertionError("all cases should have been handled")

        jd._ll_portal_runner = ll_portal_runner # for debugging
        jd.portal_runner_ptr = self.helper_func(jd._PTR_PORTAL_FUNCTYPE,
                                                ll_portal_runner)
        jd.portal_runner_adr = llmemory.cast_ptr_to_adr(jd.portal_runner_ptr)
        jd.portal_calldescr = self.cpu.calldescrof(
            jd._PTR_PORTAL_FUNCTYPE.TO,
            jd._PTR_PORTAL_FUNCTYPE.TO.ARGS,
            jd._PTR_PORTAL_FUNCTYPE.TO.RESULT,
            EffectInfo.MOST_GENERAL)

        vinfo = jd.virtualizable_info

        def assembler_call_helper(deadframe, virtualizableref):
            fail_descr = self.cpu.get_latest_descr(deadframe)
            try:
                fail_descr.handle_fail(deadframe, self.metainterp_sd, jd)
            except jitexc.JitException as e:
                return handle_jitexception(e)
            else:
                assert 0, "should have raised"

        jd._assembler_call_helper = assembler_call_helper # for debugging
        jd._assembler_helper_ptr = self.helper_func(
            jd._PTR_ASSEMBLER_HELPER_FUNCTYPE,
            assembler_call_helper)
        jd.assembler_helper_adr = llmemory.cast_ptr_to_adr(
            jd._assembler_helper_ptr)
        if vinfo is not None:
            jd.vable_token_descr = vinfo.vable_token_descr

        def handle_jitexception_from_blackhole(bhcaller, e):
            result = handle_jitexception(e)
            if result_kind == 'void':
                pass
            elif result_kind == 'int':
                bhcaller._setup_return_value_i(result)
            elif result_kind == 'ref':
                bhcaller._setup_return_value_r(result)
            elif result_kind == 'float':
                bhcaller._setup_return_value_f(result)
            else:
                assert False
        jd.handle_jitexc_from_bh = handle_jitexception_from_blackhole

        # ____________________________________________________________
        # Now mutate origportalgraph to end with a call to portal_runner_ptr
        #
        origblock, origindex, op = locate_jit_merge_point(origportalgraph)
        assert op.opname == 'jit_marker'
        assert op.args[0].value == 'jit_merge_point'
        greens_v, reds_v = support.decode_hp_hint_args(op)
        vlist = [Constant(jd.portal_runner_ptr, jd._PTR_PORTAL_FUNCTYPE)]
        vlist += greens_v
        vlist += reds_v
        v_result = Variable()
        v_result.concretetype = PORTALFUNC.RESULT
        newop = SpaceOperation('direct_call', vlist, v_result)
        del origblock.operations[origindex:]
        origblock.operations.append(newop)
        origblock.exitswitch = None
        origblock.recloseblock(Link([v_result], origportalgraph.returnblock))
        # the origportal now can raise (even if it did not raise before),
        # which means that we cannot inline it anywhere any more, but that's
        # fine since any forced inlining has been done before
        #
        checkgraph(origportalgraph)

    def add_finish(self):
        def finish():
            if self.metainterp_sd.profiler.initialized:
                self.metainterp_sd.profiler.finish()
            self.metainterp_sd.cpu.finish_once()

        if self.cpu.translate_support_code:
            call_final_function(self.translator, finish,
                                annhelper=self.annhelper)

    def rewrite_set_param_and_get_stats(self):
        from rpython.rtyper.lltypesystem.rstr import STR

        closures = {}
        graphs = self.translator.graphs
        _, PTR_SET_PARAM_FUNCTYPE = self.cpu.ts.get_FuncType([lltype.Signed],
                                                             lltype.Void)
        _, PTR_SET_PARAM_STR_FUNCTYPE = self.cpu.ts.get_FuncType(
            [lltype.Ptr(STR)], lltype.Void)
        def make_closure(jd, fullfuncname, is_string):
            if jd is None:
                def closure(i):
                    if is_string:
                        i = hlstr(i)
                    for jd in self.jitdrivers_sd:
                        getattr(jd.warmstate, fullfuncname)(i)
            else:
                state = jd.warmstate
                def closure(i):
                    if is_string:
                        i = hlstr(i)
                    getattr(state, fullfuncname)(i)
            if is_string:
                TP = PTR_SET_PARAM_STR_FUNCTYPE
            else:
                TP = PTR_SET_PARAM_FUNCTYPE
            funcptr = self.helper_func(TP, closure)
            return Constant(funcptr, TP)
        #
        for graph, block, i in find_set_param(graphs):

            op = block.operations[i]
            if op.args[1].value is not None:
                for jd in self.jitdrivers_sd:
                    if jd.jitdriver is op.args[1].value:
                        break
                else:
                    assert 0, "jitdriver of set_param() not found"
            else:
                jd = None
            funcname = op.args[2].value
            key = jd, funcname
            if key not in closures:
                closures[key] = make_closure(jd, 'set_param_' + funcname,
                                             funcname == 'enable_opts')
            op.opname = 'direct_call'
            op.args[:3] = [closures[key]]

    def rewrite_force_virtual(self, vrefinfo):
        all_graphs = self.translator.graphs
        vrefinfo.replace_force_virtual_with_call(all_graphs)

    def replace_force_quasiimmut_with_direct_call(self, op):
        ARG = op.args[0].concretetype
        mutatefieldname = op.args[1].value
        key = (ARG, mutatefieldname)
        if key in self._cache_force_quasiimmed_funcs:
            cptr = self._cache_force_quasiimmed_funcs[key]
        else:
            from rpython.jit.metainterp import quasiimmut
            func = quasiimmut.make_invalidation_function(ARG, mutatefieldname)
            FUNC = lltype.Ptr(lltype.FuncType([ARG], lltype.Void))
            llptr = self.helper_func(FUNC, func)
            cptr = Constant(llptr, FUNC)
            self._cache_force_quasiimmed_funcs[key] = cptr
        op.opname = 'direct_call'
        op.args = [cptr, op.args[0]]

    def rewrite_force_quasi_immutable(self):
        self._cache_force_quasiimmed_funcs = {}
        graphs = self.translator.graphs
        for graph, block, i in find_force_quasi_immutable(graphs):
            self.replace_force_quasiimmut_with_direct_call(block.operations[i])