def rtype_bool(self, hop): if not self.s_pbc.can_be_None: return inputconst(Bool, True) else: v1, = hop.inputargs(self) return hop.genop('char_ne', [v1, inputconst(Char, '\000')], resulttype=Bool)
def gendirectcall(self, ll_function, *args_v): rtyper = self.rtyper args_s = [] newargs_v = [] for v in args_v: if v.concretetype is Void: s_value = rtyper.binding(v, default=annmodel.s_None) if not s_value.is_constant(): raise TyperError("non-constant variable of type Void") if not isinstance(s_value, annmodel.SomePBC): raise TyperError("non-PBC Void argument: %r", (s_value,)) args_s.append(s_value) else: args_s.append(annmodel.lltype_to_annotation(v.concretetype)) newargs_v.append(v) self.rtyper.call_all_setups() # compute ForwardReferences now # hack for bound methods if hasattr(ll_function, 'im_func'): bk = rtyper.annotator.bookkeeper args_s.insert(0, bk.immutablevalue(ll_function.im_self)) newargs_v.insert(0, inputconst(Void, ll_function.im_self)) ll_function = ll_function.im_func graph = annotate_lowlevel_helper(rtyper.annotator, ll_function, args_s, rtyper.lowlevel_ann_policy) self.record_extra_call(graph) # build the 'direct_call' operation f = self.rtyper.getcallable(graph) c = inputconst(typeOf(f), f) fobj = self.rtyper.type_system_deref(f) return self.genop('direct_call', [c]+newargs_v, resulttype = typeOf(fobj).RESULT)
def rtype_bltn_list(self, hop): from rpython.rtyper.ootypesystem import rlist v_tup = hop.inputarg(self, 0) RESULT = hop.r_result.lowleveltype c_resulttype = inputconst(ootype.Void, RESULT) c_length = inputconst(ootype.Signed, len(self.items_r)) hop.exception_is_here() if isinstance(RESULT, ootype.Array): v_list = hop.genop('oonewarray', [c_resulttype, c_length], resulttype=RESULT) else: assert isinstance(RESULT, ootype.List) v_list = hop.genop('new', [c_resulttype], resulttype=RESULT) c_resize = inputconst(ootype.Void, '_ll_resize') hop.genop('oosend', [c_resize, v_list, c_length], resulttype=ootype.Void) c_setitem = inputconst(ootype.Void, 'll_setitem_fast') for index in range(len(self.items_r)): name = self.fieldnames[index] r_item = self.items_r[index] c_name = hop.inputconst(ootype.Void, name) v_item = hop.genop("oogetfield", [v_tup, c_name], resulttype=r_item) v_item = hop.llops.convertvar(v_item, r_item, hop.r_result.item_repr) c_index = inputconst(ootype.Signed, index) hop.genop('oosend', [c_setitem, v_list, c_index, v_item], resulttype=ootype.Void) return v_list
def new_instance(self, llops, classcallhop=None): """Build a new instance, without calling __init__.""" flavor = self.gcflavor flags = {'flavor': flavor} ctype = inputconst(Void, self.object_type) cflags = inputconst(Void, flags) vlist = [ctype, cflags] vptr = llops.genop('malloc', vlist, resulttype=Ptr(self.object_type)) ctypeptr = inputconst(CLASSTYPE, self.rclass.getvtable()) self.setfield(vptr, '__class__', ctypeptr, llops) # initialize instance attributes from their defaults from the class if self.classdef is not None: flds = self.allinstancefields.keys() flds.sort() for fldname in flds: if fldname == '__class__': continue mangled_name, r = self.allinstancefields[fldname] if r.lowleveltype is Void: continue value = self.classdef.classdesc.read_attribute(fldname, None) if value is not None: ll_value = r.convert_desc_or_const(value) # don't write NULL GC pointers: we know that the malloc # done above initialized at least the GC Ptr fields to # NULL already, and that's true for all our GCs if (isinstance(r.lowleveltype, Ptr) and r.lowleveltype.TO._gckind == 'gc' and not ll_value): continue cvalue = inputconst(r.lowleveltype, ll_value) self.setfield(vptr, fldname, cvalue, llops, flags={'access_directly': True}) return vptr
def gettype_from_unboxed(self, llops, vinst, can_be_none=False): unboxedclass_repr = getclassrepr(self.rtyper, self.unboxedclassdef) cunboxedcls = inputconst(CLASSTYPE, unboxedclass_repr.getvtable()) if self.is_parent: # If the lltype of vinst shows that it cannot be a tagged value, # we can directly read the typeptr. Otherwise, call a helper that # checks if the tag bit is set in the pointer. unboxedinstance_repr = getinstancerepr(self.rtyper, self.unboxedclassdef) try: lltype.castable(unboxedinstance_repr.lowleveltype, vinst.concretetype) except lltype.InvalidCast: can_be_tagged = False else: can_be_tagged = True vinst = llops.genop('cast_pointer', [vinst], resulttype=self.common_repr()) if can_be_tagged: if can_be_none: func = ll_unboxed_getclass_canbenone else: func = ll_unboxed_getclass return llops.gendirectcall(func, vinst, cunboxedcls) elif can_be_none: return llops.gendirectcall(ll_inst_type, vinst) else: ctypeptr = inputconst(lltype.Void, 'typeptr') return llops.genop('getfield', [vinst, ctypeptr], resulttype = CLASSTYPE) else: return cunboxedcls
def new_instance(self, llops, classcallhop=None): """Build a new instance, without calling __init__.""" flavor = self.gcflavor flags = {'flavor': flavor } ctype = inputconst(Void, self.object_type) cflags = inputconst(Void, flags) vlist = [ctype, cflags] cnonmovable = self.classdef.classdesc.read_attribute( '_alloc_nonmovable_', Constant(False)) if cnonmovable.value: opname = 'malloc_nonmovable' else: opname = 'malloc' vptr = llops.genop(opname, vlist, resulttype = Ptr(self.object_type)) ctypeptr = inputconst(CLASSTYPE, self.rclass.getvtable()) self.setfield(vptr, '__class__', ctypeptr, llops) # initialize instance attributes from their defaults from the class if self.classdef is not None: flds = self.allinstancefields.keys() flds.sort() for fldname in flds: if fldname == '__class__': continue mangled_name, r = self.allinstancefields[fldname] if r.lowleveltype is Void: continue value = self.classdef.classdesc.read_attribute(fldname, None) if value is not None: cvalue = inputconst(r.lowleveltype, r.convert_desc_or_const(value)) self.setfield(vptr, fldname, cvalue, llops, flags={'access_directly': True}) return vptr
def _generate_newlist(self, llops, items_v, v_sizehint): c_list = inputconst(ootype.Void, self.lowleveltype) v_result = llops.genop("new", [c_list], resulttype=self.lowleveltype) c_resize = inputconst(ootype.Void, "_ll_resize") c_length = inputconst(ootype.Signed, len(items_v)) llops.genop("oosend", [c_resize, v_result, c_length], resulttype=ootype.Void) return v_result
def dispatcher(self, shape, index, argtypes, resulttype): key = shape, index, tuple(argtypes), resulttype if key in self._dispatch_cache: return self._dispatch_cache[key] from rpython.translator.unsimplify import varoftype from rpython.flowspace.model import FunctionGraph, Link, Block, SpaceOperation inputargs = [varoftype(t) for t in [Char] + argtypes] startblock = Block(inputargs) startblock.exitswitch = inputargs[0] graph = FunctionGraph("dispatcher", startblock, varoftype(resulttype)) row_of_graphs = self.callfamily.calltables[shape][index] links = [] descs = list(self.s_pbc.descriptions) if self.s_pbc.can_be_None: descs.insert(0, None) for desc in descs: if desc is None: continue args_v = [varoftype(t) for t in argtypes] b = Block(args_v) llfn = self.rtyper.getcallable(row_of_graphs[desc]) v_fn = inputconst(typeOf(llfn), llfn) v_result = varoftype(resulttype) b.operations.append( SpaceOperation("direct_call", [v_fn] + args_v, v_result)) b.closeblock(Link([v_result], graph.returnblock)) i = self.descriptions.index(desc) links.append(Link(inputargs[1:], b, chr(i))) links[-1].llexitcase = chr(i) startblock.closeblock(*links) self.rtyper.annotator.translator.graphs.append(graph) ll_ret = getfunctionptr(graph) #FTYPE = FuncType c_ret = self._dispatch_cache[key] = inputconst(typeOf(ll_ret), ll_ret) return c_ret
def newlist(llops, r_list, items_v, v_sizehint=None): v_result = r_list._generate_newlist(llops, items_v, v_sizehint) c_setitem = inputconst(ootype.Void, "ll_setitem_fast") for i, v_item in enumerate(items_v): ci = inputconst(Signed, i) llops.genop("oosend", [c_setitem, v_result, ci, v_item], resulttype=ootype.Void) return v_result
def newtuple(cls, llops, r_tuple, items_v): # items_v should have the lowleveltype of the internal reprs if len(r_tuple.items_r) == 0: return inputconst(r_tuple, ()) # always the same empty tuple c1 = inputconst(ootype.Void, r_tuple.lowleveltype) v_result = llops.genop("new", [c1], resulttype=r_tuple.lowleveltype) for i in range(len(r_tuple.items_r)): cname = inputconst(ootype.Void, r_tuple.fieldnames[i]) llops.genop("oosetfield", [v_result, cname, items_v[i]]) return v_result
def push_roots(self, hop, keep_current_args=False): livevars = self.get_livevars_for_roots(hop, keep_current_args) self.num_pushs += len(livevars) if not livevars: return [] c_len = rmodel.inputconst(lltype.Signed, len(livevars) ) base_addr = hop.genop("direct_call", [self.incr_stack_ptr, c_len ], resulttype=llmemory.Address) for k,var in enumerate(livevars): c_k = rmodel.inputconst(lltype.Signed, k * sizeofaddr) v_adr = gen_cast(hop.llops, llmemory.Address, var) hop.genop("raw_store", [base_addr, c_k, v_adr]) return livevars
def pop_roots(self, hop, livevars): if not livevars: return c_len = rmodel.inputconst(lltype.Signed, len(livevars) ) base_addr = hop.genop("direct_call", [self.decr_stack_ptr, c_len ], resulttype=llmemory.Address) if self.gcdata.gc.moving_gc: # for moving collectors, reload the roots into the local variables for k,var in enumerate(livevars): c_k = rmodel.inputconst(lltype.Signed, k * sizeofaddr) v_newaddr = hop.genop("raw_load", [base_addr, c_k], resulttype=llmemory.Address) hop.genop("gc_reload_possibly_moved", [v_newaddr, var])
def gct_gc_deallocate(self, hop): TYPE = hop.spaceop.args[0].value v_addr = hop.spaceop.args[1] dealloc_fptr = self.dynamic_deallocation_funcptr_for_type(TYPE) cdealloc_fptr = rmodel.inputconst( lltype.typeOf(dealloc_fptr), dealloc_fptr) hop.genop("direct_call", [cdealloc_fptr, v_addr])
def test_invalid_stack_access(): py.test.skip("stack-flavored mallocs no longer supported") class A(object): pass globala = A() globala.next = None globala.i = 1 def g(a): globala.next = a def f(): a = A() a.i = 2 g(a) def h(): f() return globala.next.i interp, graph = get_interpreter(h, []) fgraph = graph.startblock.operations[0].args[0].value._obj.graph assert fgraph.startblock.operations[0].opname == "malloc" fgraph.startblock.operations[0].args[1] = inputconst(Void, {"flavor": "stack"}) py.test.raises(RuntimeError, "interp.eval_graph(graph, [])")
def decompose_slice_args(self): # Select which kind of slicing is needed. We support: # * [start:] # * [start:stop] # * [:-1] s_start = self.args_s[1] s_stop = self.args_s[2] if (s_start.is_constant() and s_start.const in (None, 0) and s_stop.is_constant() and s_stop.const == -1): return "minusone", [] if isinstance(s_start, annmodel.SomeInteger): if not s_start.nonneg: raise TyperError("slice start must be proved non-negative") if isinstance(s_stop, annmodel.SomeInteger): if not s_stop.nonneg: raise TyperError("slice stop must be proved non-negative") if s_start.is_constant() and s_start.const is None: v_start = inputconst(Signed, 0) else: v_start = self.inputarg(Signed, arg=1) if s_stop.is_constant() and s_stop.const is None: return "startonly", [v_start] else: v_stop = self.inputarg(Signed, arg=2) return "startstop", [v_start, v_stop]
def make_dispatcher(self, shape, index, argtypes, resulttype): inputargs = [varoftype(t) for t in [Char] + argtypes] startblock = Block(inputargs) startblock.exitswitch = inputargs[0] graph = FunctionGraph("dispatcher", startblock, varoftype(resulttype)) row_of_graphs = self.callfamily.calltables[shape][index] links = [] descs = list(self.s_pbc.descriptions) if self.s_pbc.can_be_None: descs.insert(0, None) for desc in descs: if desc is None: continue args_v = [varoftype(t) for t in argtypes] b = Block(args_v) llfn = self.rtyper.getcallable(row_of_graphs[desc]) v_fn = inputconst(typeOf(llfn), llfn) v_result = varoftype(resulttype) b.operations.append( SpaceOperation("direct_call", [v_fn] + args_v, v_result)) b.closeblock(Link([v_result], graph.returnblock)) i = self.descriptions.index(desc) links.append(Link(inputargs[1:], b, chr(i))) links[-1].llexitcase = chr(i) startblock.closeblock(*links) return graph
def get_unique_llfn(self): # try to build a unique low-level function. Avoid to use # whenever possible! Doesn't work with specialization, multiple # different call sites, etc. if self.lowleveltype is not Void: raise TyperError("cannot pass multiple functions here") assert len(self.s_pbc.descriptions) == 1 # lowleveltype wouldn't be Void otherwise funcdesc, = self.s_pbc.descriptions tables = [] # find the simple call in the calltable for shape, table in self.callfamily.calltables.items(): if not shape[1] and not shape[2]: tables.append(table) if len(tables) != 1: raise TyperError("cannot pass a function with various call shapes") table, = tables graphs = [] for row in table: if funcdesc in row: graphs.append(row[funcdesc]) if not graphs: raise TyperError("cannot pass here a function that is not called") graph = graphs[0] if graphs != [graph]*len(graphs): raise TyperError("cannot pass a specialized function here") llfn = self.rtyper.getcallable(graph) return inputconst(typeOf(llfn), llfn)
def getpbcfield(self, vcls, access_set, attr, llops): if (access_set, attr) not in self.pbcfields: raise TyperError("internal error: missing PBC field") mangled_name, r = self.pbcfields[access_set, attr] v_meta = self.fromclasstype(vcls, llops) cname = inputconst(ootype.Void, mangled_name) return llops.genop('oogetfield', [v_meta, cname], resulttype=r)
def newtuple(cls, llops, r_tuple, items_v): # items_v should have the lowleveltype of the internal reprs assert len(r_tuple.items_r) == len(items_v) for r_item, v_item in zip(r_tuple.items_r, items_v): assert r_item.lowleveltype == v_item.concretetype # if len(r_tuple.items_r) == 0: return inputconst(Void, ()) # a Void empty tuple c1 = inputconst(Void, r_tuple.lowleveltype.TO) cflags = inputconst(Void, {'flavor': 'gc'}) v_result = llops.genop('malloc', [c1, cflags], resulttype = r_tuple.lowleveltype) for i in range(len(r_tuple.items_r)): cname = inputconst(Void, r_tuple.fieldnames[i]) llops.genop('setfield', [v_result, cname, items_v[i]]) return v_result
def newlist(llops, r_list, items_v, v_sizehint=None): LIST = r_list.LIST if len(items_v) == 0: if v_sizehint is None: v_result = llops.gendirectcall(LIST.ll_newemptylist) else: v_result = llops.gendirectcall(LIST.ll_newlist_hint, v_sizehint) else: assert v_sizehint is None cno = inputconst(Signed, len(items_v)) v_result = llops.gendirectcall(LIST.ll_newlist, cno) v_func = inputconst(Void, dum_nocheck) for i, v_item in enumerate(items_v): ci = inputconst(Signed, i) llops.gendirectcall(ll_setitem_nonneg, v_func, v_result, ci, v_item) return v_result
def getpbcfield(self, vcls, access_set, attr, llops): if (access_set, attr) not in self.pbcfields: raise TyperError("internal error: missing PBC field") mangled_name, r = self.pbcfields[access_set, attr] v_vtable = self.fromtypeptr(vcls, llops) cname = inputconst(Void, mangled_name) return llops.genop('getfield', [v_vtable, cname], resulttype=r)
def rtype_method_join(self, hop): hop.exception_cannot_occur() rstr = hop.args_r[0] if hop.s_result.is_constant(): return inputconst(rstr.repr, hop.s_result.const) r_lst = hop.args_r[1] if not isinstance(r_lst, hop.rtyper.type_system.rlist.BaseListRepr): raise TyperError("string.join of non-list: %r" % r_lst) v_str, v_lst = hop.inputargs(rstr.repr, r_lst) v_length, v_items = self._list_length_items(hop, v_lst, r_lst.lowleveltype) if hop.args_s[0].is_constant() and hop.args_s[0].const == '': if r_lst.item_repr == rstr.repr: llfn = self.ll.ll_join_strs elif (r_lst.item_repr == hop.rtyper.type_system.rstr.char_repr or r_lst.item_repr == hop.rtyper.type_system.rstr.unichar_repr): v_tp = hop.inputconst(Void, self.lowleveltype) return hop.gendirectcall(self.ll.ll_join_chars, v_length, v_items, v_tp) else: raise TyperError("''.join() of non-string list: %r" % r_lst) return hop.gendirectcall(llfn, v_length, v_items) else: if r_lst.item_repr == rstr.repr: llfn = self.ll.ll_join else: raise TyperError("sep.join() of non-string list: %r" % r_lst) return hop.gendirectcall(llfn, v_str, v_length, v_items)
def genexternalcall(self, fnname, args_v, resulttype=None, **flags): if isinstance(resulttype, Repr): resulttype = resulttype.lowleveltype argtypes = [v.concretetype for v in args_v] FUNCTYPE = FuncType(argtypes, resulttype or Void) f = functionptr(FUNCTYPE, fnname, **flags) cf = inputconst(typeOf(f), f) return self.genop('direct_call', [cf]+list(args_v), resulttype)
def getfield(self, v_inst, attr, llops, flags={}): mangled = mangle(attr, self.rtyper.getconfig()) v_attr = inputconst(ootype.Void, mangled) r_value = self.allfields[mangled] self.lowleveltype._check_field(mangled) self.hook_access_field(v_inst, v_attr, llops, flags) return llops.genop('oogetfield', [v_inst, v_attr], resulttype = r_value)
def get_concrete_llfn(self, s_pbc, args_s, op): bk = self.rtyper.annotator.bookkeeper funcdesc, = s_pbc.descriptions args = simple_args(args_s) with bk.at_position(None): graph = funcdesc.get_graph(args, op) llfn = self.rtyper.getcallable(graph) return inputconst(typeOf(llfn), llfn)
def rtype_bltn_list(self, hop): from rpython.rtyper.lltypesystem import rlist nitems = len(self.items_r) vtup = hop.inputarg(self, 0) LIST = hop.r_result.lowleveltype.TO cno = inputconst(Signed, nitems) hop.exception_is_here() vlist = hop.gendirectcall(LIST.ll_newlist, cno) v_func = hop.inputconst(Void, rlist.dum_nocheck) for index in range(nitems): name = self.fieldnames[index] ritem = self.items_r[index] cname = hop.inputconst(Void, name) vitem = hop.genop('getfield', [vtup, cname], resulttype = ritem) vitem = hop.llops.convertvar(vitem, ritem, hop.r_result.item_repr) cindex = inputconst(Signed, index) hop.gendirectcall(rlist.ll_setitem_nonneg, v_func, vlist, cindex, vitem) return vlist
def gct_gc_writebarrier_before_copy(self, hop): # We take the conservative default and return False here, meaning # that rgc.ll_arraycopy() will do the copy by hand (i.e. with a # 'for' loop). Subclasses that have their own logic, or that don't # need any kind of write barrier, may return True. op = hop.spaceop hop.genop("same_as", [rmodel.inputconst(lltype.Bool, False)], resultvar=op.result)
def dispatcher(self, shape, index, argtypes, resulttype): key = shape, index, tuple(argtypes), resulttype if key in self._dispatch_cache: return self._dispatch_cache[key] graph = self.make_dispatcher(shape, index, argtypes, resulttype) self.rtyper.annotator.translator.graphs.append(graph) ll_ret = getfunctionptr(graph) c_ret = self._dispatch_cache[key] = inputconst(typeOf(ll_ret), ll_ret) return c_ret
def inputarg(self, converted_to, arg): """Returns the arg'th input argument of the current operation, as a Variable or Constant converted to the requested type. 'converted_to' should be a Repr instance or a Primitive low-level type. """ if not isinstance(converted_to, Repr): converted_to = self.rtyper.getprimitiverepr(converted_to) v = self.args_v[arg] if isinstance(v, Constant): return inputconst(converted_to, v.value) assert hasattr(v, 'concretetype') s_binding = self.args_s[arg] if s_binding.is_constant(): return inputconst(converted_to, s_binding.const) r_binding = self.args_r[arg] return self.llops.convertvar(v, r_binding, converted_to)
def gct_malloc(self, hop, add_flags=None): TYPE = hop.spaceop.result.concretetype.TO assert not TYPE._is_varsize() flags = hop.spaceop.args[1].value flavor = flags['flavor'] meth = getattr(self, 'gct_fv_%s_malloc' % flavor, None) assert meth, "%s has no support for malloc with flavor %r" % (self, flavor) c_size = rmodel.inputconst(lltype.Signed, llmemory.sizeof(TYPE)) v_raw = meth(hop, flags, TYPE, c_size) hop.cast_result(v_raw)
def get_concrete_llfn(self, s_pbc, args_s, op): bk = self.rtyper.annotator.bookkeeper descs = list(s_pbc.descriptions) vfcs = description.FunctionDesc.variant_for_call_site args = simple_args(args_s) shape, index = vfcs(bk, self.callfamily, descs, args, op) funcdesc, = descs row_of_one_graph = self.callfamily.calltables[shape][index] graph = row_of_one_graph[funcdesc] llfn = self.rtyper.getcallable(graph) return inputconst(lltype.typeOf(llfn), llfn)
def setclsfield(self, vcls, attr, vvalue, llops): """Write the given attribute of 'vcls'.""" if attr in self.clsfields: mangled_name, r = self.clsfields[attr] v_vtable = self.fromtypeptr(vcls, llops) cname = inputconst(Void, mangled_name) llops.genop('setfield', [v_vtable, cname, vvalue]) else: if self.classdef is None: raise MissingRTypeAttribute(attr) self.rbase.setclsfield(vcls, attr, vvalue, llops)
def get_is_virtual_fnptr(self): # def is_virtual(inst): if not inst: return False return inst.typeptr == self.jit_virtual_ref_vtable # FUNC = lltype.FuncType([rclass.OBJECTPTR], lltype.Bool) funcptr = self.warmrunnerdesc.helper_func(lltype.Ptr(FUNC), is_virtual) return inputconst(lltype.typeOf(funcptr), funcptr)
def getclsfield(self, vcls, attr, llops): """Read the given attribute of 'vcls'.""" if attr in self.clsfields: mangled_name, r = self.clsfields[attr] v_vtable = self.fromtypeptr(vcls, llops) cname = inputconst(Void, mangled_name) return llops.genop('getfield', [v_vtable, cname], resulttype=r) else: if self.classdef is None: raise MissingRTypeAttribute(attr) return self.rbase.getclsfield(vcls, attr, llops)
def convert_to_concrete_llfn(self, v, shape, index, llop): """Convert the variable 'v' to a variable referring to a concrete low-level function. In case the call table contains multiple rows, 'index' and 'shape' tells which of its items we are interested in. """ assert v.concretetype == Void funcdesc, = self.s_pbc.descriptions row_of_one_graph = self.callfamily.calltables[shape][index] graph = row_of_one_graph[funcdesc] llfn = self.rtyper.getcallable(graph) return inputconst(typeOf(llfn), llfn)
def convert_to_concrete_llfn(self, v, shape, index, llop): """Convert the variable 'v' to a variable referring to a concrete low-level function. In case the call table contains multiple rows, 'index' and 'shape' tells which of its items we are interested in. """ assert v.concretetype == self.lowleveltype if self.lowleveltype is lltype.Void: assert len(self.s_pbc.descriptions) == 1 # lowleveltype wouldn't be Void otherwise funcdesc, = self.s_pbc.descriptions row_of_one_graph = self.callfamily.calltables[shape][index] graph = row_of_one_graph[funcdesc] llfn = self.rtyper.getcallable(graph) return inputconst(lltype.typeOf(llfn), llfn) elif len(self.uniquerows) == 1: return v else: # 'v' is a Struct pointer, read the corresponding field row = self.concretetable[shape, index] cname = inputconst(lltype.Void, row.attrname) return self.get_specfunc_row(llop, v, cname, row.fntype)
def get_force_virtual_fnptr(self): # def force_virtual_if_necessary(inst): if not inst or inst.typeptr != self.jit_virtual_ref_vtable: return inst # common, fast case return self.force_virtual(inst) # FUNC = lltype.FuncType([rclass.OBJECTPTR], rclass.OBJECTPTR) funcptr = self.warmrunnerdesc.helper_func(lltype.Ptr(FUNC), force_virtual_if_necessary) return inputconst(lltype.typeOf(funcptr), funcptr)
def pop_alive(self, var, llops): PTRTYPE = var.concretetype v_adr = gen_cast(llops, llmemory.Address, var) dealloc_fptr = self.dynamic_deallocation_funcptr_for_type(PTRTYPE.TO) if dealloc_fptr is self.no_pointer_dealloc_ptr.value: # simple case llops.genop("direct_call", [self.decref_simple_ptr, v_adr]) else: cdealloc_fptr = rmodel.inputconst(lltype.typeOf(dealloc_fptr), dealloc_fptr) llops.genop("direct_call", [self.decref_ptr, v_adr, cdealloc_fptr])
def gendirectcall(self, ll_function, *args_v): rtyper = self.rtyper args_s = [] newargs_v = [] with rtyper.annotator.using_policy(rtyper.lowlevel_ann_policy): for v in args_v: if v.concretetype is Void: s_value = rtyper.annotation(v) if s_value is None: s_value = annmodel.s_None if not s_value.is_constant(): raise TyperError("non-constant variable of type Void") if not isinstance(s_value, (annmodel.SomePBC, annmodel.SomeNone)): raise TyperError("non-PBC Void argument: %r", (s_value, )) args_s.append(s_value) else: args_s.append(lltype_to_annotation(v.concretetype)) newargs_v.append(v) self.rtyper.call_all_setups() # compute ForwardReferences now # hack for bound methods if hasattr(ll_function, 'im_func'): bk = rtyper.annotator.bookkeeper args_s.insert(0, bk.immutablevalue(ll_function.im_self)) newargs_v.insert(0, inputconst(Void, ll_function.im_self)) ll_function = ll_function.im_func graph = annotate_lowlevel_helper(rtyper.annotator, ll_function, args_s, rtyper.lowlevel_ann_policy) self.record_extra_call(graph) # build the 'direct_call' operation f = self.rtyper.getcallable(graph) c = inputconst(typeOf(f), f) fobj = f._obj return self.genop('direct_call', [c] + newargs_v, resulttype=typeOf(fobj).RESULT)
def rtype_int(self, hop): hop.has_implicit_exception(ValueError) # record that we know about it string_repr = hop.args_r[0].repr if hop.nb_args == 1: v_str, = hop.inputargs(string_repr) c_base = inputconst(Signed, 10) hop.exception_is_here() return hop.gendirectcall(self.ll.ll_int, v_str, c_base) if not hop.args_r[1] == rint.signed_repr: raise TyperError('base needs to be an int') v_str, v_base = hop.inputargs(string_repr, rint.signed_repr) hop.exception_is_here() return hop.gendirectcall(self.ll.ll_int, v_str, v_base)
def convert_to_concrete_llfn(self, v, shape, index, llop): """Convert the variable 'v' to a variable referring to a concrete low-level function. In case the call table contains multiple rows, 'index' and 'shape' tells which of its items we are interested in. """ assert v.concretetype == self.lowleveltype if len(self.uniquerows) == 1: return v else: # 'v' is a Struct pointer, read the corresponding field row = self.concretetable[shape, index] cname = inputconst(Void, row.attrname) return self.get_specfunc_row(llop, v, cname, row.fntype)
def new_instance(self, llops, classcallhop=None, nonmovable=False): """Build a new instance, without calling __init__.""" flavor = self.gcflavor flags = {'flavor': flavor} if nonmovable: flags['nonmovable'] = True ctype = inputconst(Void, self.object_type) cflags = inputconst(Void, flags) vlist = [ctype, cflags] vptr = llops.genop('malloc', vlist, resulttype=Ptr(self.object_type)) ctypeptr = inputconst(CLASSTYPE, self.rclass.getvtable()) self.setfield(vptr, '__class__', ctypeptr, llops) # initialize instance attributes from their defaults from the class if self.classdef is not None: flds = self.allinstancefields.keys() flds.sort() for fldname in flds: if fldname == '__class__': continue mangled_name, r = self.allinstancefields[fldname] if r.lowleveltype is Void: continue value = self.classdef.classdesc.read_attribute(fldname, None) if value is not None: ll_value = r.convert_desc_or_const(value) # don't write NULL GC pointers: we know that the malloc # done above initialized at least the GC Ptr fields to # NULL already, and that's true for all our GCs if (isinstance(r.lowleveltype, Ptr) and r.lowleveltype.TO._gckind == 'gc' and not ll_value): continue cvalue = inputconst(r.lowleveltype, ll_value) self.setfield(vptr, fldname, cvalue, llops, flags={'access_directly': True}) return vptr
class __extend__(pairtype(FunctionReprBase, FunctionReprBase)): def rtype_is_((robj1, robj2), hop): if hop.s_result.is_constant(): return inputconst(Bool, hop.s_result.const) s_pbc = annmodel.unionof(robj1.s_pbc, robj2.s_pbc) r_pbc = hop.rtyper.getrepr(s_pbc) v1, v2 = hop.inputargs(r_pbc, r_pbc) assert v1.concretetype == v2.concretetype if v1.concretetype == Char: return hop.genop('char_eq', [v1, v2], resulttype=Bool) elif isinstance(v1.concretetype, Ptr): return hop.genop('ptr_eq', [v1, v2], resulttype=Bool) else: raise TyperError("unknown type %r" % (v1.concretetype, ))
def getfield(self, vinst, attr, llops, force_cast=False, flags={}): """Read the given attribute (or __class__ for the type) of 'vinst'.""" if attr in self.fields: mangled_name, r = self.fields[attr] cname = inputconst(Void, mangled_name) if force_cast: vinst = llops.genop('cast_pointer', [vinst], resulttype=self) self.hook_access_field(vinst, cname, llops, flags) return llops.genop('getfield', [vinst, cname], resulttype=r) else: if self.classdef is None: raise MissingRTypeAttribute(attr) return self.rbase.getfield(vinst, attr, llops, force_cast=True, flags=flags)
def new_instance(self, llops, classcallhop=None): """Build a new instance, without calling __init__.""" flavor = self.gcflavor flags = {'flavor': flavor} ctype = inputconst(Void, self.object_type) cflags = inputconst(Void, flags) vlist = [ctype, cflags] cnonmovable = self.classdef.classdesc.read_attribute( '_alloc_nonmovable_', Constant(False)) if cnonmovable.value: opname = 'malloc_nonmovable' else: opname = 'malloc' vptr = llops.genop(opname, vlist, resulttype=Ptr(self.object_type)) ctypeptr = inputconst(CLASSTYPE, self.rclass.getvtable()) self.setfield(vptr, '__class__', ctypeptr, llops) # initialize instance attributes from their defaults from the class if self.classdef is not None: flds = self.allinstancefields.keys() flds.sort() for fldname in flds: if fldname == '__class__': continue mangled_name, r = self.allinstancefields[fldname] if r.lowleveltype is Void: continue value = self.classdef.classdesc.read_attribute(fldname, None) if value is not None: cvalue = inputconst(r.lowleveltype, r.convert_desc_or_const(value)) self.setfield(vptr, fldname, cvalue, llops, flags={'access_directly': True}) return vptr
def _convert_link(self, block, link): if link.exitcase is not None and link.exitcase != 'default': if isinstance(block.exitswitch, Variable): r_case = self.bindingrepr(block.exitswitch) else: assert block.exitswitch == c_last_exception r_case = rclass.get_type_repr(self) link.llexitcase = r_case.convert_const(link.exitcase) else: link.llexitcase = None a = link.last_exception if isinstance(a, Variable): a.concretetype = self.exceptiondata.lltype_of_exception_type elif isinstance(a, Constant): link.last_exception = inputconst( self.exceptiondata.r_exception_type, a.value) a = link.last_exc_value if isinstance(a, Variable): a.concretetype = self.exceptiondata.lltype_of_exception_value elif isinstance(a, Constant): link.last_exc_value = inputconst( self.exceptiondata.r_exception_value, a.value)
def gct_malloc(self, hop, add_flags=None): TYPE = hop.spaceop.result.concretetype.TO if TYPE._hints.get('never_allocate'): raise GCTransformError( "struct %s was marked as @never_allocate but a call to malloc() " "was found. This probably means that the corresponding class is " "supposed to be constant-folded away, but for some reason it was not." % TYPE._name) assert not TYPE._is_varsize() flags = hop.spaceop.args[1].value flavor = flags['flavor'] meth = getattr(self, 'gct_fv_%s_malloc' % flavor, None) assert meth, "%s has no support for malloc with flavor %r" % (self, flavor) c_size = rmodel.inputconst(lltype.Signed, llmemory.sizeof(TYPE)) v_raw = meth(hop, flags, TYPE, c_size) hop.cast_result(v_raw)
def test_stack_malloc(): py.test.skip("stack-flavored mallocs no longer supported") class A(object): pass def f(): a = A() a.i = 1 return a.i interp, graph = get_interpreter(f, []) graph.startblock.operations[0].args[1] = inputconst( Void, {'flavor': "stack"}) result = interp.eval_graph(graph, []) assert result == 1
def test_ll_get_dict_item(): """ Tests the low-level implementation of get_dict_item. """ from rpython.annotator.annrpython import RPythonAnnotator from rpython.annotator.model import SomeTuple, SomeInteger, SomeString from rpython.rtyper.rtyper import RPythonTyper from rpython.rtyper.rmodel import inputconst from rpython.rtyper.annlowlevel import llstr, hlstr from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rtyper.lltypesystem import rordereddict, rstr dummykeyobj = None dummyvalueobj = None def _get_str_dict(): # STR -> lltype.Signed DICT = rordereddict.get_ll_dict( lltype.Ptr(rstr.STR), lltype.Signed, ll_fasthash_function=rstr.LLHelpers.ll_strhash, ll_hash_function=rstr.LLHelpers.ll_strhash, ll_eq_function=rstr.LLHelpers.ll_streq, dummykeyobj=dummykeyobj, dummyvalueobj=dummyvalueobj) return DICT s_tuple = SomeTuple([SomeString(), SomeInteger()]) DICT = _get_str_dict() ll_d = rordereddict.ll_newdict(DICT) a = RPythonAnnotator() rtyper = RPythonTyper(a) a.translator.rtyper = rtyper r_tuple = rtyper.getrepr(s_tuple) cTUPLE = inputconst(lltype.Void, r_tuple.lowleveltype) s_tuple = rtyper.annotation(cTUPLE) rtyper.call_all_setups() for i in range(20): rordereddict.ll_dict_setitem(ll_d, llstr(str(i)), i) for i in range(20): element = ll_get_dict_item(s_tuple.const, ll_d, i) assert (str(i), i) == (hlstr(element.item0), element.item1)
def insert_link_conversions(self, block, skip=0): # insert the needed conversions on the links can_insert_here = block.exitswitch is None and len(block.exits) == 1 for link in block.exits[skip:]: self._convert_link(block, link) inputargs_reprs = self.setup_block_entry(link.target) newops = self.make_new_lloplist(block) newlinkargs = {} for i in range(len(link.args)): a1 = link.args[i] r_a2 = inputargs_reprs[i] if isinstance(a1, Constant): link.args[i] = inputconst(r_a2, a1.value) continue # the Constant was typed, done if a1 is link.last_exception: r_a1 = self.exceptiondata.r_exception_type elif a1 is link.last_exc_value: r_a1 = self.exceptiondata.r_exception_value else: r_a1 = self.bindingrepr(a1) if r_a1 == r_a2: continue # no conversion needed try: new_a1 = newops.convertvar(a1, r_a1, r_a2) except TyperError, e: self.gottypererror(e, block, link, newops) continue # try other args if new_a1 != a1: newlinkargs[i] = new_a1 if newops: if can_insert_here: block.operations.extend(newops) else: # cannot insert conversion operations around a single # link, unless it is the only exit of this block. # create a new block along the link... newblock = insert_empty_block( self.annotator, link, # ...and store the conversions there. newops=newops) link = newblock.exits[0] for i, new_a1 in newlinkargs.items(): link.args[i] = new_a1
def rtype_getattr(self, hop): if hop.s_result.is_constant(): return hop.inputconst(hop.r_result, hop.s_result.const) else: attr = hop.args_s[1].const if attr == '__name__': from rpython.rtyper.lltypesystem import rstr class_repr = self.rtyper.rootclass_repr vcls, vattr = hop.inputargs(class_repr, Void) cname = inputconst(Void, 'name') return hop.genop('getfield', [vcls, cname], resulttype=Ptr(rstr.STR)) access_set, class_repr = self.get_access_set(attr) vcls, vattr = hop.inputargs(class_repr, Void) v_res = class_repr.getpbcfield(vcls, access_set, attr, hop.llops) s_res = access_set.s_value r_res = self.rtyper.getrepr(s_res) return hop.llops.convertvar(v_res, r_res, hop.r_result)
def conversion_table(r_from, r_to): if r_to in r_from._conversion_tables: return r_from._conversion_tables[r_to] else: t = malloc(Array(Char, hints={'nolength': True}), len(r_from.descriptions), immortal=True) l = [] for i, d in enumerate(r_from.descriptions): if d in r_to.descriptions: j = r_to.descriptions.index(d) l.append(j) t[i] = chr(j) else: l.append(None) if l == range(len(r_from.descriptions)): r = None else: r = inputconst(Ptr(Array(Char, hints={'nolength': True})), t) r_from._conversion_tables[r_to] = r return r
def replace_force_virtualizable_with_call(graphs, VTYPEPTR, funcptr): # funcptr should be a function pointer with a VTYPEPTR argument c_funcptr = inputconst(lltype.typeOf(funcptr), funcptr) count = 0 for graph in graphs: for block in graph.iterblocks(): if not block.operations: continue newoplist = [] for i, op in enumerate(block.operations): if (op.opname == 'jit_force_virtualizable' and op.args[0].concretetype == VTYPEPTR): if op.args[-1].value.get('access_directly', False): continue op.opname = 'direct_call' op.args = [c_funcptr, op.args[0]] count += 1 newoplist.append(op) block.operations = newoplist log("replaced %d 'jit_force_virtualizable' with %r" % (count, funcptr))
def _setup_repr(self): if self.s_pbc.subset_of: assert self.s_pbc.can_be_None == self.s_pbc.subset_of.can_be_None r = self.rtyper.getrepr(self.s_pbc.subset_of) if r is not self: r.setup() self.descriptions = r.descriptions self.c_pointer_table = r.c_pointer_table return self.descriptions = list(self.s_pbc.descriptions) if self.s_pbc.can_be_None: self.descriptions.insert(0, None) POINTER_TABLE = Array(self.pointer_repr.lowleveltype, hints={'nolength': True}) pointer_table = malloc(POINTER_TABLE, len(self.descriptions), immortal=True) for i, desc in enumerate(self.descriptions): if desc is not None: pointer_table[i] = self.pointer_repr.convert_desc(desc) else: pointer_table[i] = self.pointer_repr.convert_const(None) self.c_pointer_table = inputconst(Ptr(POINTER_TABLE), pointer_table)
def get_unique_llfn(self): # try to build a unique low-level function. Avoid to use # whenever possible! Doesn't work with specialization, multiple # different call sites, etc. funcdesc, = self.s_pbc.descriptions tables = [] # find the simple call in the calltable for shape, table in self.callfamily.calltables.items(): if not shape[1] and not shape[2]: tables.append(table) if len(tables) != 1: raise TyperError("cannot pass a function with various call shapes") table, = tables graphs = [] for row in table: if funcdesc in row: graphs.append(row[funcdesc]) if not graphs: raise TyperError("cannot pass here a function that is not called") graph = graphs[0] if graphs != [graph] * len(graphs): raise TyperError("cannot pass a specialized function here") llfn = self.rtyper.getcallable(graph) return inputconst(typeOf(llfn), llfn)
def getvalue_from_unboxed(self, llops, vinst): assert not self.is_parent v2 = llops.genop('cast_ptr_to_int', [vinst], resulttype=lltype.Signed) c_one = inputconst(lltype.Signed, 1) return llops.genop('int_rshift', [v2, c_one], resulttype=lltype.Signed)
def hook_access_field(self, vinst, cname, llops, flags): # if not flags.get('access_directly'): if self.my_redirected_fields.get(cname.value): cflags = inputconst(lltype.Void, flags) llops.genop('jit_force_virtualizable', [vinst, cname, cflags])
def setfield(self, vinst, attr, vvalue, llops, flags={}): mangled_name = mangle(attr, self.rtyper.getconfig()) cname = inputconst(ootype.Void, mangled_name) self.hook_access_field(vinst, cname, llops, flags) self.hook_setfield(vinst, attr, llops) llops.genop('oosetfield', [vinst, cname, vvalue])
def gen_setfield(self, name, v_value, llops): c_name = inputconst(lltype.Void, name) llops.genop('setfield', [self.cexcdata, c_name, v_value])