def call_final_function(translator, final_func, annhelper=None): """When the program finishes normally, call 'final_func()'.""" from pypy.annotation import model as annmodel from pypy.rpython.lltypesystem import lltype from pypy.rpython.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.graphs[0] 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)
def call_final_function(translator, final_func, annhelper=None): """When the program finishes normally, call 'final_func()'.""" from pypy.annotation import model as annmodel from pypy.rpython.lltypesystem import lltype from pypy.rpython.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)
def builtin_func_for_spec(rtyper, oopspec_name, ll_args, ll_res): key = (oopspec_name, tuple(ll_args), ll_res) try: return rtyper._builtin_func_for_spec_cache[key] except (KeyError, AttributeError): pass args_s = [annmodel.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 = annmodel.lltype_to_annotation(ll_res) impl = setup_extra_builtin(rtyper, oopspec_name, len(args_s)) if getattr(impl, 'need_result_type', False): bk = rtyper.annotator.bookkeeper args_s.insert(0, annmodel.SomePBC([bk.getdesc(deref(ll_res))])) # mixlevelann = MixLevelHelperAnnotator(rtyper) c_func = mixlevelann.constfunc(impl, args_s, s_result) mixlevelann.finish() # 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
def make_driverhook_graphs(self): from pypy.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()
def builtin_func_for_spec(rtyper, oopspec_name, ll_args, ll_res): key = (oopspec_name, tuple(ll_args), ll_res) try: return rtyper._builtin_func_for_spec_cache[key] except (KeyError, AttributeError): pass args_s = [annmodel.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 = annmodel.lltype_to_annotation(ll_res) impl = setup_extra_builtin(rtyper, oopspec_name, len(args_s)) if getattr(impl, "need_result_type", False): bk = rtyper.annotator.bookkeeper args_s.insert(0, annmodel.SomePBC([bk.getdesc(deref(ll_res))])) # mixlevelann = MixLevelHelperAnnotator(rtyper) c_func = mixlevelann.constfunc(impl, args_s, s_result) mixlevelann.finish() # 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
def make_driverhook_graphs(self): from pypy.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()
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 = [annmodel.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 = annmodel.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
def make_leave_jit_graph(self): self.leave_graph = None if self.jitdriver.leave: args_s = self.portal_args_s from pypy.annotation import model as annmodel annhelper = MixLevelHelperAnnotator(self.translator.rtyper) s_result = annmodel.s_None self.leave_graph = annhelper.getgraph(self.jitdriver.leave, args_s, s_result) annhelper.finish()
def test_enforced_args(): from pypy.annotation.model import s_None from pypy.rpython.annlowlevel import MixLevelHelperAnnotator from pypy.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()
def build_meta_interp(self, CPUClass, translate_support_code=False, view="auto", no_stats=False, ProfilerClass=EmptyProfiler, **kwds): assert CPUClass is not None opt = history.Options(**kwds) if no_stats: stats = history.NoStats() else: stats = history.Stats() self.stats = stats if translate_support_code: self.annhelper = MixLevelHelperAnnotator(self.translator.rtyper) annhelper = self.annhelper else: annhelper = None cpu = CPUClass(self.translator.rtyper, self.stats, translate_support_code, gcdescr=self.gcdescr) self.cpu = cpu self.metainterp_sd = MetaInterpStaticData( self.portal_graph, # xxx cpu, self.stats, opt, ProfilerClass=ProfilerClass, warmrunnerdesc=self)
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() 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 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
def make_driverhook_graphs(self): from pypy.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) self.set_jitcell_at_ptr = self._make_hook_graph( annhelper, self.jitdriver.set_jitcell_at, annmodel.s_None, s_BaseJitCell_not_None) self.get_jitcell_at_ptr = self._make_hook_graph( annhelper, self.jitdriver.get_jitcell_at, s_BaseJitCell_or_None) self.can_inline_ptr = self._make_hook_graph( annhelper, self.jitdriver.can_inline, annmodel.s_Bool) self.get_printable_location_ptr = self._make_hook_graph( annhelper, self.jitdriver.get_printable_location, s_Str) annhelper.finish()
def make_driverhook_graphs(self): from pypy.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) self.set_jitcell_at_ptr = self._make_hook_graph( annhelper, self.jitdriver.set_jitcell_at, annmodel.s_None, s_BaseJitCell_not_None) self.get_jitcell_at_ptr = self._make_hook_graph( annhelper, self.jitdriver.get_jitcell_at, s_BaseJitCell_or_None) self.can_inline_ptr = self._make_hook_graph(annhelper, self.jitdriver.can_inline, annmodel.s_Bool) self.get_printable_location_ptr = self._make_hook_graph( annhelper, self.jitdriver.get_printable_location, s_Str) annhelper.finish()
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
def call_initial_function(translator, initial_func, annhelper=None): """Before the program starts, call 'initial_func()'.""" from pypy.annotation import model as annmodel from pypy.rpython.lltypesystem import lltype from pypy.rpython.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)
def replace_force_virtualizable(self, rtyper, graphs): from pypy.annotation import model as annmodel from pypy.rpython.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) if self.type_system == 'lltype': s_vinst = annmodel.SomePtr(v_inst_ll_type) else: s_vinst = annmodel.SomeOOInstance(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
def call_initial_function(translator, initial_func, annhelper=None): """Before the program starts, call 'initial_func()'.""" from pypy.annotation import model as annmodel from pypy.rpython.lltypesystem import lltype from pypy.rpython.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)
def replace_promote_virtualizable(self, rtyper, graphs): from pypy.annotation import model as annmodel from pypy.rpython.annlowlevel import MixLevelHelperAnnotator graph = graphs[0] for block, op in graph.iterblockops(): if op.opname == 'promote_virtualizable': v_inst_ll_type = op.args[0].concretetype break def mycall(vinst_ll): pass annhelper = MixLevelHelperAnnotator(rtyper) if self.type_system == 'lltype': s_vinst = annmodel.SomePtr(v_inst_ll_type) else: s_vinst = annmodel.SomeOOInstance(v_inst_ll_type) funcptr = annhelper.delayedfunction(mycall, [s_vinst], annmodel.s_None) annhelper.finish() replace_promote_virtualizable_with_call(graphs, v_inst_ll_type, funcptr) return funcptr
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(type_system='lltype').specialize() exctransformer = t.getexceptiontransformer() exctransformer.create_exception_handling(graphof(t, common)) from pypy.annotation import model as annmodel from pypy.rpython.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 conftest.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 pypy.rpython.llinterp import LLInterpreter llinterp = LLInterpreter(t.rtyper) res = llinterp.eval_graph(later_graph, [10]) assert res == 1
def __init__(self, translator, inline=False): self.translator = translator self.seen_graphs = {} self.prepared = False self.minimal_transform = {} 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 test_enforced_args(): from pypy.annotation.model import s_None from pypy.rpython.annlowlevel import MixLevelHelperAnnotator from pypy.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()
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(type_system="lltype").specialize() exctransformer = t.getexceptiontransformer() exctransformer.create_exception_handling(graphof(t, common)) from pypy.annotation import model as annmodel from pypy.rpython.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 conftest.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 pypy.rpython.llinterp import LLInterpreter llinterp = LLInterpreter(t.rtyper) res = llinterp.eval_graph(later_graph, [10]) assert res == 1
class BaseExceptionTransformer(object): def __init__(self, translator): self.translator = translator self.raise_analyzer = canraise.RaiseAnalyzer(translator) edata = translator.rtyper.getexceptiondata() 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() rclass = translator.rtyper.type_system.rclass (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 = rclass.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 = annmodel.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.getexceptiondata() rclass = self.translator.rtyper.type_system.rclass 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 = rclass.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 = {} pending = [(link, link.args[1])] while pending: link, v = pending.pop() if (link, v) in seen: continue seen[link, v] = True 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) # XXX: does alloc_shortcut make sense also for ootype? if alloc_shortcut: T = spaceop.result.concretetype 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 getannmixlevel(self): if self.annmixlevel is not None: return self.annmixlevel from pypy.rpython.annlowlevel import MixLevelHelperAnnotator self.annmixlevel = MixLevelHelperAnnotator(self) return self.annmixlevel
class BaseExceptionTransformer(object): def __init__(self, translator): self.translator = translator self.raise_analyzer = canraise.RaiseAnalyzer(translator) edata = translator.rtyper.getexceptiondata() 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() rclass = translator.rtyper.type_system.rclass (runtime_error_ll_exc_type, runtime_error_ll_exc) = self.get_builtin_exception(RuntimeError) (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 = rclass.ll_inst_type(evalue) exc_data.exc_value = evalue def rpyexc_raise_runtime_error(): rpyexc_raise(runtime_error_ll_exc_type, runtime_error_ll_exc) 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_raise_runtime_error_ptr = self.build_func( "RPyRaiseRuntimeError", self.noinline(rpyexc_raise_runtime_error), [], lltype.Void) 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 = annmodel.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.getexceptiondata() rclass = self.translator.rtyper.type_system.rclass 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 = rclass.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, always_exc_clear=False): """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 self.always_exc_clear = always_exc_clear 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_stack_unwind(block) 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) removenoops.remove_superfluous_keep_alive(graph) return n_need_exc_matching_blocks, n_gen_exc_checks def replace_stack_unwind(self, block): for i in range(len(block.operations)): if block.operations[i].opname == 'stack_unwind': # if there are stack_unwind ops left, # the graph was not stackless-transformed # so we need to raise a RuntimeError in any # case block.operations[i].opname = "direct_call" block.operations[i].args = [self.rpyexc_raise_runtime_error_ptr] 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)): 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 = {} pending = [(link, link.args[1])] while pending: link, v = pending.pop() if (link, v) in seen: continue seen[link, v] = True 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) # XXX: does alloc_shortcut make sense also for ootype? if alloc_shortcut: T = spaceop.result.concretetype 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 # XXX this is not right. it also inserts zero_gc_pointers_inside # XXX on a path that malloc_nonmovable returns null, but does not raise # XXX which might end up with a segfault. But we don't have such gc now if spaceop.opname == 'malloc' or spaceop.opname == 'malloc_nonmovable': flavor = spaceop.args[1].value['flavor'] if flavor == 'gc': insert_zeroing_op = True 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))) if self.always_exc_clear: # insert code that clears the exception even in the non-exceptional # case... this is a hint for the JIT, but pointless otherwise if normalafterblock is None: normalafterblock = insert_empty_block(None, l0) llops = rtyper.LowLevelOpList(None) self.gen_setfield('exc_value', self.c_null_evalue, llops) self.gen_setfield('exc_type', self.c_null_etype, llops) normalafterblock.operations[:0] = llops
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
def __init__(self, translator): self.translator = translator self.raise_analyzer = canraise.RaiseAnalyzer(translator) edata = translator.rtyper.getexceptiondata() 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() rclass = translator.rtyper.type_system.rclass (runtime_error_ll_exc_type, runtime_error_ll_exc) = self.get_builtin_exception(RuntimeError) (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 = rclass.ll_inst_type(evalue) exc_data.exc_value = evalue def rpyexc_raise_runtime_error(): rpyexc_raise(runtime_error_ll_exc_type, runtime_error_ll_exc) 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_raise_runtime_error_ptr = self.build_func( "RPyRaiseRuntimeError", self.noinline(rpyexc_raise_runtime_error), [], lltype.Void) 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()
class BaseExceptionTransformer(object): def __init__(self, translator): self.translator = translator self.raise_analyzer = canraise.RaiseAnalyzer(translator) edata = translator.rtyper.getexceptiondata() 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() rclass = translator.rtyper.type_system.rclass runtime_error_def = translator.annotator.bookkeeper.getuniqueclassdef(RuntimeError) runtime_error_ll_exc = edata.get_standard_ll_exc_instance(translator.rtyper, runtime_error_def) runtime_error_ll_exc_type = rclass.ll_inst_type(runtime_error_ll_exc) 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): # assert(!RPyExceptionOccurred()); exc_data.exc_type = etype exc_data.exc_value = evalue def rpyexc_fetch_exception(): evalue = rpyexc_fetch_value() rpyexc_clear() return evalue def rpyexc_restore_exception(evalue): if evalue: rpyexc_raise(rclass.ll_inst_type(evalue), evalue) def rpyexc_raise_runtime_error(): rpyexc_raise(runtime_error_ll_exc_type, runtime_error_ll_exc) 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", rpyexc_raise, [self.lltype_of_exception_type, self.lltype_of_exception_value], lltype.Void, jitcallkind='rpyexc_raise') # for the JIT self.rpyexc_raise_runtime_error_ptr = self.build_func( "RPyRaiseRuntimeError", rpyexc_raise_runtime_error, [], lltype.Void) 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", rpyexc_restore_exception, [self.lltype_of_exception_value], lltype.Void) self.mixlevelannotator.finish() self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping() def build_func(self, name, fn, inputtypes, rettype, **kwds): l2a = annmodel.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 transform_completely(self): for graph in self.translator.graphs: self.create_exception_handling(graph) def create_exception_handling(self, graph, always_exc_clear=False): """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 self.always_exc_clear = always_exc_clear join_blocks(graph) # collect the blocks before changing them n_need_exc_matching_blocks = 0 n_gen_exc_checks = 0 for block in list(graph.iterblocks()): self.replace_stack_unwind(block) 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 self.transform_except_block(graph, graph.exceptblock) cleanup_graph(graph) removenoops.remove_superfluous_keep_alive(graph) return n_need_exc_matching_blocks, n_gen_exc_checks def replace_stack_unwind(self, block): for i in range(len(block.operations)): if block.operations[i].opname == 'stack_unwind': # if there are stack_unwind ops left, # the graph was not stackless-transformed # so we need to raise a RuntimeError in any # case block.operations[i].opname = "direct_call" block.operations[i].args = [self.rpyexc_raise_runtime_error_ptr] 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)): if block.operations[i].opname == 'gc_fetch_exception': block.operations[i].opname = "direct_call" block.operations[i].args = [self.rpyexc_fetch_exception_ptr] if block.operations[i].opname == 'gc_restore_exception': block.operations[i].opname = "direct_call" block.operations[i].args.insert(0, self.rpyexc_restore_exception_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)): 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 transform_except_block(self, graph, block): # attach an except block -- let's hope that nobody uses it graph.exceptblock = Block([Variable('etype'), # exception class Variable('evalue')]) # exception value graph.exceptblock.operations = () graph.exceptblock.closeblock() result = Variable() result.concretetype = lltype.Void block.operations = [SpaceOperation( "direct_call", [self.rpyexc_raise_ptr] + block.inputargs, result)] l = Link([error_constant(graph.returnblock.inputargs[0].concretetype)], 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) 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) # XXX: does alloc_shortcut make sense also for ootype? if alloc_shortcut: T = spaceop.result.concretetype 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 l = Link([error_constant(returnblock.inputargs[0].concretetype)], returnblock) 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 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))) if self.always_exc_clear: # insert code that clears the exception even in the non-exceptional # case... this is a hint for the JIT, but pointless otherwise if normalafterblock is None: normalafterblock = insert_empty_block(None, l0) llops = rtyper.LowLevelOpList(None) self.gen_setfield('exc_value', self.c_null_evalue, llops) self.gen_setfield('exc_type', self.c_null_etype, llops) normalafterblock.operations[:0] = llops
def __init__(self, translator): self.translator = translator self.raise_analyzer = canraise.RaiseAnalyzer(translator) edata = translator.rtyper.getexceptiondata() 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() rclass = translator.rtyper.type_system.rclass runtime_error_def = translator.annotator.bookkeeper.getuniqueclassdef(RuntimeError) runtime_error_ll_exc = edata.get_standard_ll_exc_instance(translator.rtyper, runtime_error_def) runtime_error_ll_exc_type = rclass.ll_inst_type(runtime_error_ll_exc) 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): # assert(!RPyExceptionOccurred()); exc_data.exc_type = etype exc_data.exc_value = evalue def rpyexc_fetch_exception(): evalue = rpyexc_fetch_value() rpyexc_clear() return evalue def rpyexc_restore_exception(evalue): if evalue: rpyexc_raise(rclass.ll_inst_type(evalue), evalue) def rpyexc_raise_runtime_error(): rpyexc_raise(runtime_error_ll_exc_type, runtime_error_ll_exc) 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", rpyexc_raise, [self.lltype_of_exception_type, self.lltype_of_exception_value], lltype.Void, jitcallkind='rpyexc_raise') # for the JIT self.rpyexc_raise_runtime_error_ptr = self.build_func( "RPyRaiseRuntimeError", rpyexc_raise_runtime_error, [], lltype.Void) 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", rpyexc_restore_exception, [self.lltype_of_exception_value], lltype.Void) self.mixlevelannotator.finish() self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping()
class BaseExceptionTransformer(object): def __init__(self, translator): self.translator = translator self.raise_analyzer = canraise.RaiseAnalyzer(translator) edata = translator.rtyper.getexceptiondata() 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() rclass = translator.rtyper.type_system.rclass (runtime_error_ll_exc_type, runtime_error_ll_exc) = self.get_builtin_exception(RuntimeError) (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) 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): # assert(!RPyExceptionOccurred()); 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 def rpyexc_fetch_exception(): evalue = rpyexc_fetch_value() rpyexc_clear() return evalue def rpyexc_restore_exception(evalue): if evalue: rpyexc_raise(rclass.ll_inst_type(evalue), evalue) def rpyexc_raise_runtime_error(): rpyexc_raise(runtime_error_ll_exc_type, runtime_error_ll_exc) 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", rpyexc_raise, [self.lltype_of_exception_type, self.lltype_of_exception_value], lltype.Void, jitcallkind='rpyexc_raise') # for the JIT self.rpyexc_raise_runtime_error_ptr = self.build_func( "RPyRaiseRuntimeError", rpyexc_raise_runtime_error, [], lltype.Void) 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", 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 build_func(self, name, fn, inputtypes, rettype, **kwds): l2a = annmodel.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.getexceptiondata() rclass = self.translator.rtyper.type_system.rclass 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 = rclass.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, always_exc_clear=False): """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 self.always_exc_clear = always_exc_clear join_blocks(graph) # collect the blocks before changing them n_need_exc_matching_blocks = 0 n_gen_exc_checks = 0 for block in list(graph.iterblocks()): self.replace_stack_unwind(block) 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 self.transform_except_block(graph, graph.exceptblock) cleanup_graph(graph) removenoops.remove_superfluous_keep_alive(graph) return n_need_exc_matching_blocks, n_gen_exc_checks def replace_stack_unwind(self, block): for i in range(len(block.operations)): if block.operations[i].opname == 'stack_unwind': # if there are stack_unwind ops left, # the graph was not stackless-transformed # so we need to raise a RuntimeError in any # case block.operations[i].opname = "direct_call" block.operations[i].args = [ self.rpyexc_raise_runtime_error_ptr ] 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)): 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 transform_except_block(self, graph, block): # attach an except block -- let's hope that nobody uses it graph.exceptblock = Block([ Variable('etype'), # exception class Variable('evalue') ]) # exception value graph.exceptblock.operations = () graph.exceptblock.closeblock() result = Variable() result.concretetype = lltype.Void block.operations = [ SpaceOperation("direct_call", [self.rpyexc_raise_ptr] + block.inputargs, result) ] l = Link([error_constant(graph.returnblock.inputargs[0].concretetype)], 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) 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) # XXX: does alloc_shortcut make sense also for ootype? if alloc_shortcut: T = spaceop.result.concretetype 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 l = Link([error_constant(returnblock.inputargs[0].concretetype)], returnblock) 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 # XXX this is not right. it also inserts zero_gc_pointers_inside # XXX on a path that malloc_nonmovable returns null, but does not raise # XXX which might end up with a segfault. But we don't have such gc now if spaceop.opname == 'malloc' or spaceop.opname == 'malloc_nonmovable': flavor = spaceop.args[1].value['flavor'] if flavor == 'gc': insert_zeroing_op = True 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))) if self.always_exc_clear: # insert code that clears the exception even in the non-exceptional # case... this is a hint for the JIT, but pointless otherwise if normalafterblock is None: normalafterblock = insert_empty_block(None, l0) llops = rtyper.LowLevelOpList(None) self.gen_setfield('exc_value', self.c_null_evalue, llops) self.gen_setfield('exc_type', self.c_null_etype, llops) normalafterblock.operations[:0] = llops
def __init__(self, translator): self.translator = translator self.raise_analyzer = canraise.RaiseAnalyzer(translator) edata = translator.rtyper.getexceptiondata() 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() rclass = translator.rtyper.type_system.rclass (runtime_error_ll_exc_type, runtime_error_ll_exc) = self.get_builtin_exception(RuntimeError) (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) 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): # assert(!RPyExceptionOccurred()); 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 def rpyexc_fetch_exception(): evalue = rpyexc_fetch_value() rpyexc_clear() return evalue def rpyexc_restore_exception(evalue): if evalue: rpyexc_raise(rclass.ll_inst_type(evalue), evalue) def rpyexc_raise_runtime_error(): rpyexc_raise(runtime_error_ll_exc_type, runtime_error_ll_exc) 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", rpyexc_raise, [self.lltype_of_exception_type, self.lltype_of_exception_value], lltype.Void, jitcallkind='rpyexc_raise') # for the JIT self.rpyexc_raise_runtime_error_ptr = self.build_func( "RPyRaiseRuntimeError", rpyexc_raise_runtime_error, [], lltype.Void) 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", 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 __init__(self, translator): self.translator = translator self.raise_analyzer = canraise.RaiseAnalyzer(translator) edata = translator.rtyper.getexceptiondata() self.lltype_of_exception_value = edata.lltype_of_exception_value self.lltype_of_exception_type = edata.lltype_of_exception_type mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper) l2a = annmodel.lltype_to_annotation 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) def rpyexc_occured(): exc_type = exc_data.exc_type return bool(exc_type) # XXX tmp HACK for genllvm # llvm is strongly typed between bools and ints, which means we have no way of # calling rpyexc_occured() from c code with lltype.Bool 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): # assert(!RPyExceptionOccurred()); exc_data.exc_type = etype exc_data.exc_value = evalue RPYEXC_OCCURED_TYPE = lltype.FuncType([], lltype.Bool) rpyexc_occured_graph = mixlevelannotator.getgraph( rpyexc_occured, [], l2a(lltype.Bool)) self.rpyexc_occured_ptr = Constant(lltype.functionptr( RPYEXC_OCCURED_TYPE, "RPyExceptionOccurred", graph=rpyexc_occured_graph, exception_policy="exc_helper"), lltype.Ptr(RPYEXC_OCCURED_TYPE)) # XXX tmp HACK for genllvm _RPYEXC_OCCURED_TYPE = lltype.FuncType([], lltype.Signed) _rpyexc_occured_graph = mixlevelannotator.getgraph( _rpyexc_occured, [], l2a(lltype.Signed)) self._rpyexc_occured_ptr = Constant(lltype.functionptr( _RPYEXC_OCCURED_TYPE, "_RPyExceptionOccurred", graph=_rpyexc_occured_graph, exception_policy="exc_helper"), lltype.Ptr(_RPYEXC_OCCURED_TYPE)) RPYEXC_FETCH_TYPE_TYPE = lltype.FuncType([], self.lltype_of_exception_type) rpyexc_fetch_type_graph = mixlevelannotator.getgraph( rpyexc_fetch_type, [], l2a(self.lltype_of_exception_type)) self.rpyexc_fetch_type_ptr = Constant(lltype.functionptr( RPYEXC_FETCH_TYPE_TYPE, "RPyFetchExceptionType", graph=rpyexc_fetch_type_graph, exception_policy="exc_helper"), lltype.Ptr(RPYEXC_FETCH_TYPE_TYPE)) RPYEXC_FETCH_VALUE_TYPE = lltype.FuncType([], self.lltype_of_exception_value) rpyexc_fetch_value_graph = mixlevelannotator.getgraph( rpyexc_fetch_value, [], l2a(self.lltype_of_exception_value)) self.rpyexc_fetch_value_ptr = Constant(lltype.functionptr( RPYEXC_FETCH_VALUE_TYPE, "RPyFetchExceptionValue", graph=rpyexc_fetch_value_graph, exception_policy="exc_helper"), lltype.Ptr(RPYEXC_FETCH_VALUE_TYPE)) RPYEXC_CLEAR = lltype.FuncType([], lltype.Void) rpyexc_clear_graph = mixlevelannotator.getgraph( rpyexc_clear, [], l2a(lltype.Void)) self.rpyexc_clear_ptr = Constant(lltype.functionptr( RPYEXC_CLEAR, "RPyClearException", graph=rpyexc_clear_graph, exception_policy="exc_helper"), lltype.Ptr(RPYEXC_CLEAR)) RPYEXC_RAISE = lltype.FuncType([self.lltype_of_exception_type, self.lltype_of_exception_value], lltype.Void) rpyexc_raise_graph = mixlevelannotator.getgraph( rpyexc_raise, [l2a(self.lltype_of_exception_type), l2a(self.lltype_of_exception_value)], l2a(lltype.Void)) self.rpyexc_raise_ptr = Constant(lltype.functionptr( RPYEXC_RAISE, "RPyRaiseException", graph=rpyexc_raise_graph, exception_policy="exc_helper", jitcallkind='rpyexc_raise', # for the JIT ), lltype.Ptr(RPYEXC_RAISE)) mixlevelannotator.finish() self.exc_data_ptr = exc_data self.cexcdata = Constant(exc_data, lltype.Ptr(EXCDATA)) self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping() p = lltype.nullptr(self.lltype_of_exception_type.TO) self.c_null_etype = Constant(p, self.lltype_of_exception_type) p = lltype.nullptr(self.lltype_of_exception_value.TO) self.c_null_evalue = Constant(p, self.lltype_of_exception_value)
argc = get_argc() argv = get_argv() args = [rffi.charp2str(argv[i]) for i in range(argc)] result = 1 try: result = entrypoint(args) except Exception, exc: os.write( 2, 'DEBUG: An uncaught exception was raised in entrypoint: ' + str(exc) + '\n') return result entrypoint._annenforceargs_ = [s_list_of_strings] mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper) res = annmodel.lltype_to_annotation(lltype.Signed) graph = mixlevelannotator.getgraph(new_entrypoint, [], res) mixlevelannotator.finish() mixlevelannotator.backend_optimize() return new_entrypoint class GenLLVM(object): # see create_codewriter() below function_count = {} def __init__(self, translator, standalone): # reset counters
def __init__(self, translator): self.translator = translator self.raise_analyzer = canraise.RaiseAnalyzer(translator) edata = translator.rtyper.getexceptiondata() 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() rclass = translator.rtyper.type_system.rclass (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 = rclass.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( )