/rpython/jit/codewriter/support.py
Python | 787 lines | 607 code | 130 blank | 50 comment | 63 complexity | 45a5a82fe9e264261e17fd7ddeb8fae2 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
- import sys
- from rpython.annotator import model as annmodel
- from rpython.rtyper.llannotation import lltype_to_annotation
- from rpython.annotator.policy import AnnotatorPolicy
- from rpython.flowspace.model import Variable, Constant
- from rpython.jit.metainterp.typesystem import deref
- from rpython.rlib import rgc
- from rpython.rlib.jit import elidable, oopspec
- from rpython.rlib.rarithmetic import r_longlong, r_ulonglong, r_uint, intmask
- from rpython.rlib.rarithmetic import LONG_BIT
- from rpython.rtyper import rlist
- from rpython.rtyper.lltypesystem import rlist as rlist_ll
- from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
- from rpython.rtyper.extregistry import ExtRegistryEntry
- from rpython.rtyper.llinterp import LLInterpreter
- from rpython.rtyper.lltypesystem import lltype, rffi, llmemory, rstr as ll_rstr, rdict as ll_rdict
- from rpython.rtyper import rclass
- from rpython.rtyper.lltypesystem import rordereddict
- from rpython.rtyper.lltypesystem.lloperation import llop
- from rpython.rtyper.lltypesystem.module import ll_math
- from rpython.translator.translator import TranslationContext
- from rpython.translator.unsimplify import split_block
- def getargtypes(annotator, values):
- if values is None: # for backend tests producing stand-alone exe's
- from rpython.annotator.listdef import s_list_of_strings
- return [s_list_of_strings]
- return [_annotation(annotator, x) for x in values]
- def _annotation(a, x):
- T = lltype.typeOf(x)
- if T == lltype.Ptr(ll_rstr.STR):
- t = str
- else:
- t = lltype_to_annotation(T)
- return a.typeannotation(t)
- def annotate(func, values, inline=None, backendoptimize=True,
- translationoptions={}):
- # build the normal ll graphs for ll_function
- t = TranslationContext()
- for key, value in translationoptions.items():
- setattr(t.config.translation, key, value)
- annpolicy = AnnotatorPolicy()
- a = t.buildannotator(policy=annpolicy)
- argtypes = getargtypes(a, values)
- a.build_types(func, argtypes, main_entry_point=True)
- rtyper = t.buildrtyper()
- rtyper.specialize()
- #if inline:
- # auto_inlining(t, threshold=inline)
- if backendoptimize:
- from rpython.translator.backendopt.all import backend_optimizations
- backend_optimizations(t, inline_threshold=inline or 0,
- remove_asserts=True, really_remove_asserts=True)
- return rtyper
- def getgraph(func, values):
- rtyper = annotate(func, values)
- return rtyper.annotator.translator.graphs[0]
- def autodetect_jit_markers_redvars(graph):
- # the idea is to find all the jit_merge_point and
- # add all the variables across the links to the reds.
- for block, op in graph.iterblockops():
- if op.opname == 'jit_marker':
- jitdriver = op.args[1].value
- if not jitdriver.autoreds:
- continue
- # if we want to support also can_enter_jit, we should find a
- # way to detect a consistent set of red vars to pass *both* to
- # jit_merge_point and can_enter_jit. The current simple
- # solution doesn't work because can_enter_jit might be in
- # another block, so the set of alive_v will be different.
- methname = op.args[0].value
- assert methname == 'jit_merge_point', (
- "reds='auto' is supported only for jit drivers which "
- "calls only jit_merge_point. Found a call to %s" % methname)
- #
- # compute the set of live variables across the jit_marker
- alive_v = set()
- for link in block.exits:
- alive_v.update(link.args)
- alive_v.difference_update(link.getextravars())
- for op1 in block.operations[::-1]:
- if op1 is op:
- break # stop when the meet the jit_marker
- alive_v.discard(op1.result)
- alive_v.update(op1.args)
- greens_v = op.args[2:]
- reds_v = alive_v - set(greens_v)
- reds_v = [v for v in reds_v if isinstance(v, Variable) and
- v.concretetype is not lltype.Void]
- reds_v = sort_vars(reds_v)
- op.args.extend(reds_v)
- if jitdriver.numreds is None:
- jitdriver.numreds = len(reds_v)
- elif jitdriver.numreds != len(reds_v):
- raise AssertionError("there are multiple jit_merge_points "
- "with the same jitdriver")
- def split_before_jit_merge_point(graph, portalblock, portalopindex):
- """Split the block just before the 'jit_merge_point',
- making sure the input args are in the canonical order.
- """
- # split the block just before the jit_merge_point()
- if portalopindex > 0:
- link = split_block(portalblock, portalopindex)
- portalblock = link.target
- portalop = portalblock.operations[0]
- # split again, this time enforcing the order of the live vars
- # specified by decode_hp_hint_args().
- assert portalop.opname == 'jit_marker'
- assert portalop.args[0].value == 'jit_merge_point'
- greens_v, reds_v = decode_hp_hint_args(portalop)
- link = split_block(portalblock, 0, greens_v + reds_v)
- return link.target
- def sort_vars(args_v):
- from rpython.jit.metainterp.history import getkind
- _kind2count = {'int': 1, 'ref': 2, 'float': 3}
- return sorted(args_v, key=lambda v: _kind2count[getkind(v.concretetype)])
- def decode_hp_hint_args(op):
- # Returns (list-of-green-vars, list-of-red-vars) without Voids.
- # Both lists must be sorted: first INT, then REF, then FLOAT.
- assert op.opname == 'jit_marker'
- jitdriver = op.args[1].value
- numgreens = len(jitdriver.greens)
- assert jitdriver.numreds is not None
- numreds = jitdriver.numreds
- greens_v = op.args[2:2+numgreens]
- reds_v = op.args[2+numgreens:]
- assert len(reds_v) == numreds
- #
- def _sort(args_v, is_green):
- lst = [v for v in args_v if v.concretetype is not lltype.Void]
- if is_green:
- assert len(lst) == len(args_v), (
- "not supported so far: 'greens' variables contain Void")
- # a crash here means that you have to reorder the variable named in
- # the JitDriver. Indeed, greens and reds must both be sorted: first
- # all INTs, followed by all REFs, followed by all FLOATs.
- lst2 = sort_vars(lst)
- assert lst == lst2
- return lst
- #
- return (_sort(greens_v, True), _sort(reds_v, False))
- def maybe_on_top_of_llinterp(rtyper, fnptr):
- # Run a generated graph on top of the llinterp for testing.
- # When translated, this just returns the fnptr.
- funcobj = fnptr._obj
- if hasattr(funcobj, 'graph'):
- llinterp = LLInterpreter(rtyper) #, exc_data_ptr=exc_data_ptr)
- def on_top_of_llinterp(*args):
- return llinterp.eval_graph(funcobj.graph, list(args))
- else:
- assert hasattr(funcobj, '_callable')
- def on_top_of_llinterp(*args):
- return funcobj._callable(*args)
- return on_top_of_llinterp
- class Entry(ExtRegistryEntry):
- _about_ = maybe_on_top_of_llinterp
- def compute_result_annotation(self, s_rtyper, s_fnptr):
- return s_fnptr
- def specialize_call(self, hop):
- hop.exception_cannot_occur()
- return hop.inputarg(hop.args_r[1], arg=1)
- # ____________________________________________________________
- #
- # Manually map oopspec'ed operations back to their ll implementation
- # coming from modules like rpython.rtyper.rlist. The following
- # functions are fished from the globals() by setup_extra_builtin().
- def _ll_0_newlist(LIST):
- return LIST.ll_newlist(0)
- def _ll_1_newlist(LIST, count):
- return LIST.ll_newlist(count)
- _ll_0_newlist.need_result_type = True
- _ll_1_newlist.need_result_type = True
- _ll_1_newlist_clear = rlist._ll_alloc_and_clear
- _ll_1_newlist_clear.need_result_type = True
- def _ll_1_newlist_hint(LIST, hint):
- return LIST.ll_newlist_hint(hint)
- _ll_1_newlist_hint.need_result_type = True
- def _ll_1_list_len(l):
- return l.ll_length()
- def _ll_2_list_getitem(l, index):
- return rlist.ll_getitem(rlist.dum_checkidx, l, index)
- def _ll_3_list_setitem(l, index, newitem):
- rlist.ll_setitem(rlist.dum_checkidx, l, index, newitem)
- def _ll_2_list_delitem(l, index):
- rlist.ll_delitem(rlist.dum_checkidx, l, index)
- def _ll_1_list_pop(l):
- return rlist.ll_pop_default(rlist.dum_checkidx, l)
- def _ll_2_list_pop(l, index):
- return rlist.ll_pop(rlist.dum_checkidx, l, index)
- _ll_2_list_append = rlist.ll_append
- _ll_2_list_extend = rlist.ll_extend
- _ll_3_list_insert = rlist.ll_insert_nonneg
- _ll_2_list_delslice_startonly = rlist.ll_listdelslice_startonly
- _ll_3_list_delslice_startstop = rlist.ll_listdelslice_startstop
- _ll_2_list_inplace_mul = rlist.ll_inplace_mul
- _ll_2_list_getitem_foldable = _ll_2_list_getitem
- _ll_1_list_len_foldable = _ll_1_list_len
- _ll_5_list_ll_arraycopy = rgc.ll_arraycopy
- _ll_3_list_resize_hint_really = rlist_ll._ll_list_resize_hint_really
- @elidable
- def _ll_1_gc_identityhash(x):
- return lltype.identityhash(x)
- # the following function should not be "@elidable": I can think of
- # a corner case in which id(const) is constant-folded, and then 'const'
- # disappears and is collected too early (possibly causing another object
- # with the same id() to appear).
- def _ll_1_gc_id(ptr):
- return llop.gc_id(lltype.Signed, ptr)
- def _ll_1_gc_pin(ptr):
- return llop.gc_pin(lltype.Bool, ptr)
- def _ll_1_gc_unpin(ptr):
- llop.gc_unpin(lltype.Void, ptr)
- @oopspec("jit.force_virtual(inst)")
- def _ll_1_jit_force_virtual(inst):
- return llop.jit_force_virtual(lltype.typeOf(inst), inst)
- def _ll_1_int_abs(x):
- # this version doesn't branch
- mask = x >> (LONG_BIT - 1)
- return (x ^ mask) - mask
- def _ll_2_int_floordiv(x, y):
- # this is used only if the RPython program uses llop.int_floordiv()
- # explicitly. For 'a // b', see _handle_int_special() in jtransform.py.
- # This is the reverse of rpython.rtyper.rint.ll_int_py_div(), i.e.
- # the same logic as rpython.rtyper.lltypesystem.opimpl.op_int_floordiv
- # but written in a no-branch style.
- r = x // y
- p = r * y
- # the JIT knows that if x and y are both positive, this is just 'r'
- return r + (((x ^ y) >> (LONG_BIT - 1)) & (p != x))
- def _ll_2_int_mod(x, y):
- # same comments as _ll_2_int_floordiv()
- r = x % y
- # the JIT knows that if x and y are both positive, this doesn't change 'r'
- r -= y & (((x ^ y) & (r | -r)) >> (LONG_BIT - 1))
- return r
- def _ll_1_cast_uint_to_float(x):
- # XXX on 32-bit platforms, this should be done using cast_longlong_to_float
- # (which is a residual call right now in the x86 backend)
- return llop.cast_uint_to_float(lltype.Float, x)
- def _ll_1_cast_float_to_uint(x):
- # XXX on 32-bit platforms, this should be done using cast_float_to_longlong
- # (which is a residual call right now in the x86 backend)
- return llop.cast_float_to_uint(lltype.Unsigned, x)
- def _ll_0_ll_read_timestamp():
- from rpython.rlib import rtimer
- return rtimer.read_timestamp()
- # math support
- # ------------
- _ll_1_ll_math_ll_math_sqrt = ll_math.ll_math_sqrt
- # long long support
- # -----------------
- def u_to_longlong(x):
- return rffi.cast(lltype.SignedLongLong, x)
- def _ll_1_llong_invert(xll):
- y = ~r_ulonglong(xll)
- return u_to_longlong(y)
- def _ll_1_ullong_invert(xull):
- return ~xull
- def _ll_2_llong_lt(xll, yll):
- return xll < yll
- def _ll_2_llong_le(xll, yll):
- return xll <= yll
- def _ll_2_llong_eq(xll, yll):
- return xll == yll
- def _ll_2_llong_ne(xll, yll):
- return xll != yll
- def _ll_2_llong_gt(xll, yll):
- return xll > yll
- def _ll_2_llong_ge(xll, yll):
- return xll >= yll
- def _ll_2_ullong_eq(xull, yull):
- return xull == yull
- def _ll_2_ullong_ne(xull, yull):
- return xull != yull
- def _ll_2_ullong_ult(xull, yull):
- return xull < yull
- def _ll_2_ullong_ule(xull, yull):
- return xull <= yull
- def _ll_2_ullong_ugt(xull, yull):
- return xull > yull
- def _ll_2_ullong_uge(xull, yull):
- return xull >= yull
- def _ll_2_llong_add(xll, yll):
- z = r_ulonglong(xll) + r_ulonglong(yll)
- return u_to_longlong(z)
- def _ll_2_llong_sub(xll, yll):
- z = r_ulonglong(xll) - r_ulonglong(yll)
- return u_to_longlong(z)
- def _ll_2_llong_mul(xll, yll):
- z = r_ulonglong(xll) * r_ulonglong(yll)
- return u_to_longlong(z)
- def _ll_2_llong_and(xll, yll):
- z = r_ulonglong(xll) & r_ulonglong(yll)
- return u_to_longlong(z)
- def _ll_2_llong_or(xll, yll):
- z = r_ulonglong(xll) | r_ulonglong(yll)
- return u_to_longlong(z)
- def _ll_2_llong_xor(xll, yll):
- z = r_ulonglong(xll) ^ r_ulonglong(yll)
- return u_to_longlong(z)
- def _ll_2_ullong_add(xull, yull):
- z = (xull) + (yull)
- return (z)
- def _ll_2_ullong_sub(xull, yull):
- z = (xull) - (yull)
- return (z)
- def _ll_2_ullong_mul(xull, yull):
- z = (xull) * (yull)
- return (z)
- def _ll_2_ullong_and(xull, yull):
- z = (xull) & (yull)
- return (z)
- def _ll_2_ullong_or(xull, yull):
- z = (xull) | (yull)
- return (z)
- def _ll_2_ullong_xor(xull, yull):
- z = (xull) ^ (yull)
- return (z)
- def _ll_2_llong_lshift(xll, y):
- z = r_ulonglong(xll) << y
- return u_to_longlong(z)
- def _ll_2_ullong_lshift(xull, y):
- return xull << y
- def _ll_2_llong_rshift(xll, y):
- return xll >> y
- def _ll_2_ullong_urshift(xull, y):
- return xull >> y
- def _ll_1_llong_from_int(x):
- return r_longlong(intmask(x))
- def _ll_1_ullong_from_int(x):
- return r_ulonglong(intmask(x))
- def _ll_1_llong_from_uint(x):
- return r_longlong(r_uint(x))
- def _ll_1_ullong_from_uint(x):
- return r_ulonglong(r_uint(x))
- def _ll_1_llong_to_int(xll):
- return intmask(xll)
- def _ll_1_llong_from_float(xf):
- return r_longlong(xf)
- def _ll_1_ullong_from_float(xf):
- return r_ulonglong(xf)
- def _ll_1_llong_to_float(xll):
- return float(rffi.cast(lltype.SignedLongLong, xll))
- def _ll_1_ullong_u_to_float(xull):
- return float(rffi.cast(lltype.UnsignedLongLong, xull))
- def _ll_1_llong_abs(xll):
- if xll < 0:
- return -xll
- else:
- return xll
- # in the following calls to builtins, the JIT is allowed to look inside:
- inline_calls_to = [
- ('int_abs', [lltype.Signed], lltype.Signed),
- ('int_floordiv', [lltype.Signed, lltype.Signed], lltype.Signed),
- ('int_mod', [lltype.Signed, lltype.Signed], lltype.Signed),
- ('ll_math.ll_math_sqrt', [lltype.Float], lltype.Float),
- ]
- class LLtypeHelpers:
- # ---------- dict ----------
- _ll_1_dict_copy = ll_rdict.ll_copy
- _ll_1_dict_clear = ll_rdict.ll_clear
- _ll_2_dict_update = ll_rdict.ll_update
- # ---------- dict keys(), values(), items(), iter ----------
- _ll_1_dict_keys = ll_rdict.ll_dict_keys
- _ll_1_dict_values = ll_rdict.ll_dict_values
- _ll_1_dict_items = ll_rdict.ll_dict_items
- _ll_1_dict_keys .need_result_type = True
- _ll_1_dict_values.need_result_type = True
- _ll_1_dict_items .need_result_type = True
- _ll_1_dictiter_next = ll_rdict._ll_dictnext
- _ll_1_dict_resize = ll_rdict.ll_dict_resize
- # ---------- ordered dict ----------
- _ll_1_odict_copy = rordereddict.ll_dict_copy
- _ll_1_odict_clear = rordereddict.ll_dict_clear
- _ll_2_odict_update = rordereddict.ll_dict_update
- _ll_1_odict_keys = rordereddict.ll_dict_keys
- _ll_1_odict_values = rordereddict.ll_dict_values
- _ll_1_odict_items = rordereddict.ll_dict_items
- _ll_1_odict_keys .need_result_type = True
- _ll_1_odict_values.need_result_type = True
- _ll_1_odict_items .need_result_type = True
- _ll_1_odictiter_next = rordereddict._ll_dictnext
- _ll_1_odict_resize = rordereddict.ll_dict_resize
- # ---------- strings and unicode ----------
- _ll_1_str_str2unicode = ll_rstr.LLHelpers.ll_str2unicode
- def _ll_4_str_eq_slice_checknull(s1, start, length, s2):
- """str1[start : start + length] == str2."""
- if not s2:
- return 0
- chars2 = s2.chars
- if len(chars2) != length:
- return 0
- j = 0
- chars1 = s1.chars
- while j < length:
- if chars1[start + j] != chars2[j]:
- return 0
- j += 1
- return 1
- def _ll_4_str_eq_slice_nonnull(s1, start, length, s2):
- """str1[start : start + length] == str2, assuming str2 != NULL."""
- chars2 = s2.chars
- if len(chars2) != length:
- return 0
- j = 0
- chars1 = s1.chars
- while j < length:
- if chars1[start + j] != chars2[j]:
- return 0
- j += 1
- return 1
- def _ll_4_str_eq_slice_char(s1, start, length, c2):
- """str1[start : start + length] == c2."""
- if length != 1:
- return 0
- if s1.chars[start] != c2:
- return 0
- return 1
- def _ll_2_str_eq_nonnull(s1, s2):
- len1 = len(s1.chars)
- len2 = len(s2.chars)
- if len1 != len2:
- return 0
- j = 0
- chars1 = s1.chars
- chars2 = s2.chars
- while j < len1:
- if chars1[j] != chars2[j]:
- return 0
- j += 1
- return 1
- def _ll_2_str_eq_nonnull_char(s1, c2):
- chars = s1.chars
- if len(chars) != 1:
- return 0
- if chars[0] != c2:
- return 0
- return 1
- def _ll_2_str_eq_checknull_char(s1, c2):
- if not s1:
- return 0
- chars = s1.chars
- if len(chars) != 1:
- return 0
- if chars[0] != c2:
- return 0
- return 1
- def _ll_2_str_eq_lengthok(s1, s2):
- j = 0
- chars1 = s1.chars
- chars2 = s2.chars
- len1 = len(chars1)
- while j < len1:
- if chars1[j] != chars2[j]:
- return 0
- j += 1
- return 1
- # ---------- malloc with del ----------
- def _ll_2_raw_malloc(TP, size):
- return lltype.malloc(TP, size, flavor='raw')
- def build_ll_0_alloc_with_del(RESULT, vtable):
- def _ll_0_alloc_with_del():
- p = lltype.malloc(RESULT.TO)
- lltype.cast_pointer(rclass.OBJECTPTR, p).typeptr = vtable
- return p
- return _ll_0_alloc_with_del
- def build_raw_malloc_varsize_builder(zero=False,
- add_memory_pressure=False,
- track_allocation=True):
- def build_ll_1_raw_malloc_varsize(ARRAY):
- def _ll_1_raw_malloc_varsize(n):
- return lltype.malloc(ARRAY, n, flavor='raw', zero=zero,
- add_memory_pressure=add_memory_pressure,
- track_allocation=track_allocation)
- return _ll_1_raw_malloc_varsize
- return build_ll_1_raw_malloc_varsize
- build_ll_1_raw_malloc_varsize = (
- build_raw_malloc_varsize_builder())
- build_ll_1_raw_malloc_varsize_zero = (
- build_raw_malloc_varsize_builder(zero=True))
- build_ll_1_raw_malloc_varsize_zero_add_memory_pressure = (
- build_raw_malloc_varsize_builder(zero=True, add_memory_pressure=True))
- build_ll_1_raw_malloc_varsize_add_memory_pressure = (
- build_raw_malloc_varsize_builder(add_memory_pressure=True))
- build_ll_1_raw_malloc_varsize_no_track_allocation = (
- build_raw_malloc_varsize_builder(track_allocation=False))
- build_ll_1_raw_malloc_varsize_zero_no_track_allocation = (
- build_raw_malloc_varsize_builder(zero=True, track_allocation=False))
- build_ll_1_raw_malloc_varsize_zero_add_memory_pressure_no_track_allocation = (
- build_raw_malloc_varsize_builder(zero=True, add_memory_pressure=True, track_allocation=False))
- build_ll_1_raw_malloc_varsize_add_memory_pressure_no_track_allocation = (
- build_raw_malloc_varsize_builder(add_memory_pressure=True, track_allocation=False))
- def build_raw_malloc_fixedsize_builder(zero=False,
- add_memory_pressure=False,
- track_allocation=True):
- def build_ll_0_raw_malloc_fixedsize(STRUCT):
- def _ll_0_raw_malloc_fixedsize():
- return lltype.malloc(STRUCT, flavor='raw', zero=zero,
- add_memory_pressure=add_memory_pressure,
- track_allocation=track_allocation)
- return _ll_0_raw_malloc_fixedsize
- return build_ll_0_raw_malloc_fixedsize
- build_ll_0_raw_malloc_fixedsize = (
- build_raw_malloc_fixedsize_builder())
- build_ll_0_raw_malloc_fixedsize_zero = (
- build_raw_malloc_fixedsize_builder(zero=True))
- build_ll_0_raw_malloc_fixedsize_zero_add_memory_pressure = (
- build_raw_malloc_fixedsize_builder(zero=True, add_memory_pressure=True))
- build_ll_0_raw_malloc_fixedsize_add_memory_pressure = (
- build_raw_malloc_fixedsize_builder(add_memory_pressure=True))
- build_ll_0_raw_malloc_fixedsize_no_track_allocation = (
- build_raw_malloc_fixedsize_builder(track_allocation=False))
- build_ll_0_raw_malloc_fixedsize_zero_no_track_allocation = (
- build_raw_malloc_fixedsize_builder(zero=True, track_allocation=False))
- build_ll_0_raw_malloc_fixedsize_zero_add_memory_pressure_no_track_allocation = (
- build_raw_malloc_fixedsize_builder(zero=True, add_memory_pressure=True, track_allocation=False))
- build_ll_0_raw_malloc_fixedsize_add_memory_pressure_no_track_allocation = (
- build_raw_malloc_fixedsize_builder(add_memory_pressure=True, track_allocation=False))
- def build_raw_free_builder(track_allocation=True):
- def build_ll_1_raw_free(ARRAY):
- def _ll_1_raw_free(p):
- lltype.free(p, flavor='raw',
- track_allocation=track_allocation)
- return _ll_1_raw_free
- return build_ll_1_raw_free
- build_ll_1_raw_free = (
- build_raw_free_builder())
- build_ll_1_raw_free_no_track_allocation = (
- build_raw_free_builder(track_allocation=False))
- def _ll_1_threadlocalref_get(TP, offset):
- return llop.threadlocalref_get(TP, offset)
- _ll_1_threadlocalref_get.need_result_type = 'exact' # don't deref
- def _ll_1_weakref_create(obj):
- return llop.weakref_create(llmemory.WeakRefPtr, obj)
- def _ll_1_weakref_deref(TP, obj):
- return llop.weakref_deref(lltype.Ptr(TP), obj)
- _ll_1_weakref_deref.need_result_type = True
- def _ll_1_gc_add_memory_pressure(num):
- llop.gc_add_memory_pressure(lltype.Void, num)
- def setup_extra_builtin(rtyper, oopspec_name, nb_args, extra=None):
- name = '_ll_%d_%s' % (nb_args, oopspec_name.replace('.', '_'))
- if extra is not None:
- name = 'build' + name
- try:
- wrapper = globals()[name]
- except KeyError:
- wrapper = getattr(LLtypeHelpers, name).im_func
- if extra is not None:
- wrapper = wrapper(*extra)
- return wrapper
- # # ____________________________________________________________
- class Index:
- def __init__(self, n):
- self.n = n
- def parse_oopspec(fnobj):
- FUNCTYPE = lltype.typeOf(fnobj)
- ll_func = fnobj._callable
- nb_args = len(FUNCTYPE.ARGS)
- argnames = ll_func.func_code.co_varnames[:nb_args]
- # parse the oopspec and fill in the arguments
- operation_name, args = ll_func.oopspec.split('(', 1)
- assert args.endswith(')')
- args = args[:-1] + ',' # trailing comma to force tuple syntax
- if args.strip() == ',':
- args = '()'
- nb_args = len(argnames)
- argname2index = dict(zip(argnames, [Index(n) for n in range(nb_args)]))
- argtuple = eval(args, argname2index)
- return operation_name, argtuple
- def normalize_opargs(argtuple, opargs):
- result = []
- for obj in argtuple:
- if isinstance(obj, Index):
- result.append(opargs[obj.n])
- else:
- result.append(Constant(obj, lltype.typeOf(obj)))
- return result
- def get_call_oopspec_opargs(fnobj, opargs):
- oopspec, argtuple = parse_oopspec(fnobj)
- normalized_opargs = normalize_opargs(argtuple, opargs)
- return oopspec, normalized_opargs
- def get_identityhash_oopspec(op):
- return 'gc_identityhash', op.args
- def get_gcid_oopspec(op):
- return 'gc_id', op.args
- RENAMED_ADT_NAME = {
- 'list': {
- 'll_getitem_fast': 'getitem',
- 'll_setitem_fast': 'setitem',
- 'll_length': 'len',
- },
- }
- def get_send_oopspec(SELFTYPE, name):
- oopspec_name = SELFTYPE.oopspec_name
- assert oopspec_name is not None
- renamed = RENAMED_ADT_NAME.get(oopspec_name, {})
- pubname = renamed.get(name, name)
- oopspec = '%s.%s' % (oopspec_name, pubname)
- return oopspec
- def decode_builtin_call(op):
- if op.opname == 'direct_call':
- fnobj = op.args[0].value._obj
- opargs = op.args[1:]
- return get_call_oopspec_opargs(fnobj, opargs)
- elif op.opname == 'gc_identityhash':
- return get_identityhash_oopspec(op)
- elif op.opname == 'gc_id':
- return get_gcid_oopspec(op)
- else:
- raise ValueError(op.opname)
- 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 = deref(ll_restype)
- 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