def decorator(func): def wrapped(*args): if objectmodel.we_are_translated(): val = func(*args) else: val = default return val wrapped.__name__ = func.__name__ return jit.dont_look_inside(wrapped)
def test_access_directly_stop_at_dont_look_inside(self): from rpython.rlib.jit import dont_look_inside class A: _virtualizable_ = ["x"] def h(a): g(a) h = dont_look_inside(h) def g(a): a.x = 2 h(a) def f(): a = A() a = hint(a, access_directly=True) a.x = 1 g(a) t, typer, graph = self.gengraph(f, []) deref = typer.type_system.deref desc = typer.annotator.bookkeeper.getdesc(g) g_graphs = desc._cache.items() assert len(g_graphs) == 2 g_graphs.sort() assert g_graphs[0][0] is None # default g_graph = g_graphs[0][1] g_graph_directly = g_graphs[1][1] f_graph = t._graphof(f) h_graph = t._graphof(h) # 1 graph! def get_direct_call_graph(graph): for block, op in graph.iterblockops(): if op.opname == "direct_call": return deref(op.args[0].value).graph return None assert get_direct_call_graph(f_graph) is g_graph_directly assert get_direct_call_graph(g_graph) is h_graph assert get_direct_call_graph(g_graph_directly) is h_graph assert get_direct_call_graph(h_graph) is g_graph
def test_access_directly_stop_at_dont_look_inside(self): from rpython.rlib.jit import dont_look_inside class A: _virtualizable2_ = ['x'] def h(a): g(a) h = dont_look_inside(h) def g(a): a.x = 2 h(a) def f(): a = A() a = hint(a, access_directly=True) a.x = 1 g(a) t, typer, graph = self.gengraph(f, []) deref = typer.type_system_deref desc = typer.annotator.bookkeeper.getdesc(g) g_graphs = desc._cache.items() assert len(g_graphs) == 2 g_graphs.sort() assert g_graphs[0][0] is None # default g_graph = g_graphs[0][1] g_graph_directly = g_graphs[1][1] f_graph = t._graphof(f) h_graph = t._graphof(h) # 1 graph! def get_direct_call_graph(graph): for block, op in graph.iterblockops(): if op.opname == 'direct_call': return deref(op.args[0].value).graph return None assert get_direct_call_graph(f_graph) is g_graph_directly assert get_direct_call_graph(g_graph) is h_graph assert get_direct_call_graph(g_graph_directly) is h_graph assert get_direct_call_graph(h_graph) is g_graph
def llexternal(name, args, result, _callable=None, compilation_info=ExternalCompilationInfo(), sandboxsafe=False, releasegil='auto', _nowrapper=False, calling_conv='c', elidable_function=False, macro=None, random_effects_on_gcobjs='auto'): """Build an external function that will invoke the C function 'name' with the given 'args' types and 'result' type. You get by default a wrapper that casts between number types as needed to match the arguments. You can also pass an RPython string when a CCHARP argument is expected, and the C function receives a 'const char*' pointing to a read-only null-terminated character of arrays, as usual for C. The C function can have callbacks, but they must be specified explicitly as constant RPython functions. We don't support yet C functions that invoke callbacks passed otherwise (e.g. set by a previous C call). releasegil: whether it's ok to release the GIL around the call. Default is yes, unless sandboxsafe is set, in which case we consider that the function is really short-running and don't bother releasing the GIL. An explicit True or False overrides this logic. """ if _callable is not None: assert callable(_callable) ext_type = lltype.FuncType(args, result) if _callable is None: if macro is not None: if macro is True: macro = name _callable = generate_macro_wrapper( name, macro, ext_type, compilation_info) else: _callable = ll2ctypes.LL2CtypesCallable(ext_type, calling_conv) if elidable_function: _callable._elidable_function_ = True kwds = {} has_callback = False for ARG in args: if _isfunctype(ARG): has_callback = True if has_callback: kwds['_callbacks'] = callbackholder = CallbackHolder() else: callbackholder = None if releasegil in (False, True): # invoke the around-handlers, which release the GIL, if and only if # the C function is thread-safe. invoke_around_handlers = releasegil else: # default case: # invoke the around-handlers only for "not too small" external calls; # sandboxsafe is a hint for "too-small-ness" (e.g. math functions). # Also, _nowrapper functions cannot release the GIL, by default. invoke_around_handlers = not sandboxsafe and not _nowrapper if random_effects_on_gcobjs not in (False, True): random_effects_on_gcobjs = ( invoke_around_handlers or # because it can release the GIL has_callback) # because the callback can do it assert not (elidable_function and random_effects_on_gcobjs) funcptr = lltype.functionptr(ext_type, name, external='C', compilation_info=compilation_info, _callable=_callable, _safe_not_sandboxed=sandboxsafe, _debugexc=True, # on top of llinterp canraise=False, random_effects_on_gcobjs= random_effects_on_gcobjs, calling_conv=calling_conv, **kwds) if isinstance(_callable, ll2ctypes.LL2CtypesCallable): _callable.funcptr = funcptr if _nowrapper: return funcptr if invoke_around_handlers: # The around-handlers are releasing the GIL in a threaded pypy. # We need tons of care to ensure that no GC operation and no # exception checking occurs while the GIL is released. # The actual call is done by this small piece of non-inlinable # generated code in order to avoid seeing any GC pointer: # neither '*args' nor the GC objects originally passed in as # argument to wrapper(), if any (e.g. RPython strings). argnames = ', '.join(['a%d' % i for i in range(len(args))]) source = py.code.Source(""" def call_external_function(%(argnames)s): before = aroundstate.before if before: before() # NB. it is essential that no exception checking occurs here! res = funcptr(%(argnames)s) after = aroundstate.after if after: after() return res """ % locals()) miniglobals = {'aroundstate': aroundstate, 'funcptr': funcptr, '__name__': __name__, # for module name propagation } exec source.compile() in miniglobals call_external_function = miniglobals['call_external_function'] call_external_function._dont_inline_ = True call_external_function._annspecialcase_ = 'specialize:ll' call_external_function._gctransformer_hint_close_stack_ = True call_external_function._call_aroundstate_target_ = funcptr call_external_function = func_with_new_name(call_external_function, 'ccall_' + name) # don't inline, as a hack to guarantee that no GC pointer is alive # anywhere in call_external_function else: # if we don't have to invoke the aroundstate, we can just call # the low-level function pointer carelessly call_external_function = funcptr unrolling_arg_tps = unrolling_iterable(enumerate(args)) def wrapper(*args): real_args = () to_free = () for i, TARGET in unrolling_arg_tps: arg = args[i] freeme = None if TARGET == CCHARP: if arg is None: arg = lltype.nullptr(CCHARP.TO) # None => (char*)NULL freeme = arg elif isinstance(arg, str): arg = str2charp(arg) # XXX leaks if a str2charp() fails with MemoryError # and was not the first in this function freeme = arg elif TARGET == CWCHARP: if arg is None: arg = lltype.nullptr(CWCHARP.TO) # None => (wchar_t*)NULL freeme = arg elif isinstance(arg, unicode): arg = unicode2wcharp(arg) # XXX leaks if a unicode2wcharp() fails with MemoryError # and was not the first in this function freeme = arg elif TARGET is VOIDP: if arg is None: arg = lltype.nullptr(VOIDP.TO) elif isinstance(arg, str): arg = str2charp(arg) freeme = arg elif isinstance(arg, unicode): arg = unicode2wcharp(arg) freeme = arg elif _isfunctype(TARGET) and not _isllptr(arg): # XXX pass additional arguments if invoke_around_handlers: arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg, callbackholder, aroundstate)) else: arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg, callbackholder)) else: SOURCE = lltype.typeOf(arg) if SOURCE != TARGET: if TARGET is lltype.Float: arg = float(arg) elif ((isinstance(SOURCE, lltype.Number) or SOURCE is lltype.Bool) and (isinstance(TARGET, lltype.Number) or TARGET is lltype.Bool)): arg = cast(TARGET, arg) real_args = real_args + (arg,) to_free = to_free + (freeme,) res = call_external_function(*real_args) for i, TARGET in unrolling_arg_tps: if to_free[i]: lltype.free(to_free[i], flavor='raw') if rarithmetic.r_int is not r_int: if result is INT: return cast(lltype.Signed, res) elif result is UINT: return cast(lltype.Unsigned, res) return res wrapper._annspecialcase_ = 'specialize:ll' wrapper._always_inline_ = 'try' # for debugging, stick ll func ptr to that wrapper._ptr = funcptr wrapper = func_with_new_name(wrapper, name) if calling_conv != "c": wrapper = jit.dont_look_inside(wrapper) return wrapper
err = rwin32._GetLastError() # careful, setraw() overwrites GetLastError. # We must read it first, before the errno handling. rthread.tlfield_rpy_lasterror.setraw(err) elif save_err & rffi.RFFI_SAVE_WSALASTERROR: from rpython.rlib import rthread, _rsocket_rffi err = _rsocket_rffi._WSAGetLastError() rthread.tlfield_rpy_lasterror.setraw(err) if save_err & rffi.RFFI_SAVE_ERRNO: from rpython.rlib import rthread rthread.tlfield_rpy_errno.setraw(_get_errno()) if os.name == 'nt': is_valid_fd = jit.dont_look_inside(rffi.llexternal( "_PyVerify_fd", [rffi.INT], rffi.INT, compilation_info=errno_eci, )) def validate_fd(fd): if not is_valid_fd(fd): from errno import EBADF raise OSError(EBADF, 'Bad file descriptor') else: def is_valid_fd(fd): return 1 def validate_fd(fd): pass def closerange(fd_low, fd_high): # this behaves like os.closerange() from Python 2.6. for fd in xrange(fd_low, fd_high):
def alterMethods(cls): """ Alter Monte methods on behalf of AutoHelp. Return the signatures of the altered methods. NOT_RPYTHON """ atoms = {} imports = set() execNames = {"Refused": Refused} def nextName(nameIndex=[0]): name = "_%d" % nameIndex[0] nameIndex[0] += 1 return name def namedLiteral(lit): name = nextName() execNames[name] = lit return name dispatchClauses = [] methods = harvestMethods(cls) for attr, (f, verb, args, kwargs, rv) in methods.iteritems(): assignments = [] if isStarArgs(args): atomTest = "atom.verb == %r" % verb call = "self.%s(args)" % attr else: atom = getAtom(verb, len(args)) atomName = namedLiteral(atom) ds = f.__doc__ if ds is not None: ds = ds.decode("utf-8") atoms[atom] = ds atomTest = "atom is %s" % atomName argNames = [] for i, arg in enumerate(args): argName = nextName() argNames.append(argName) assignments.append("%s = args[%d]" % (argName, i)) if arg != "Any": unwrapperModule = wrappers[arg] pred = "is" + arg imports.add("from %s import %s" % (unwrapperModule, pred)) atomTest += " and %s(args[%d])" % (pred, i) unwrapper = "unwrap" + arg imports.add("from %s import %s" % (unwrapperModule, unwrapper)) assignments.append("%s = %s(%s)" % (argName, unwrapper, argName)) else: imports.add("from typhon.objects.refs import resolution") assignments.append("%s = resolution(%s)" % (argName, argName)) for k, v in kwargs.iteritems(): # Look up the default value. We're going to pop this in and # use None as a sentinel value in .extractStringKey(). ~ C. default = getKwargDefault(f, k) defaultName = namedLiteral(default) kwargName = nextName() argNames.append("%s=%s" % (k, kwargName)) assignments.append("%s = namedArgs.extractStringKey(%r, None)" % (kwargName, k.decode("utf-8"))) # If the kwarg is None, then it wasn't passed in; use the # default. Otherwise, invoke the unwrapper if one exists. assignments.append("if %s is None: %s = %s" % (kwargName, kwargName, defaultName)) if v != "Any": unwrapperModule = wrappers[v] unwrapper = "unwrap" + v imports.add("from %s import %s" % (unwrapperModule, unwrapper)) assignments.append("else: %s = %s(%s)" % (kwargName, unwrapper, kwargName)) call = "self.%s(%s)" % (attr, ",".join(argNames)) retvals = [] if rv == "Any": # No wrapping. retvals.append("return rv") elif rv == "Void": # Enforced correctness. Disobedience will not be tolerated. retvals.append("assert rv is None, 'habanero'") retvals.append("from typhon.objects.constants import NullObject") retvals.append("return NullObject") else: wrapperModule = wrappers[rv] wrapper = "wrap" + rv imports.add("from %s import %s" % (wrapperModule, wrapper)) retvals.append("return %s(rv)" % wrapper) # We need to use newlines for the assignments since kwarg assignments # are conditional. dispatchClauses.append(""" if %s: %s rv = %s %s """ % (atomTest, "\n ".join(assignments), call, ";".join(retvals))) setattr(cls, attr, f) # Temporary. Soon, all classes shall receive AutoHelp, and no class will # have a handwritten recv(). if dispatchClauses: exec py.code.Source(""" def recvNamed(self, atom, args, namedArgs): %s %s rv = self.mirandaMethods(atom, args, namedArgs) if rv is None: raise Refused(self, atom, args) else: return rv """ % (";".join(imports), "\n".join(dispatchClauses))).compile() in execNames recvNamed = execNames["recvNamed"] # This is the place for fixing the "too many constants" JIT # translation error. If there's too many dispatch clauses in # .recvNamed(), then the JIT transformation fails because too many # constant values are being mixed in. This number is determined # empirically; uncomment the next line to take measurements: # print "Made %d dispatch clauses for %r" % (len(dispatchClauses), cls) # Here's a worksheet. Please keep it up-to-date when altering this # section of code. ~ C. # * At last run, the longest working list of clauses had length 40 # * And the shortest list which broke translation had length 46 # * Assuming that each clause has a constant integral number of # constants, the number of constants per clause is 6 # * The maximum number of allowed constants is 255 # * Therefore, the longest working list can have maximum length 42 if len(dispatchClauses) >= 42: # Too many constants; forbid the JIT from recursing into us. recvNamed = dont_look_inside(recvNamed) cls.recvNamed = recvNamed return atoms
def llexternal(name, args, result, _callable=None, compilation_info=ExternalCompilationInfo(), sandboxsafe=False, releasegil='auto', _nowrapper=False, calling_conv='c', elidable_function=False, macro=None, random_effects_on_gcobjs='auto', save_err=RFFI_ERR_NONE): """Build an external function that will invoke the C function 'name' with the given 'args' types and 'result' type. You get by default a wrapper that casts between number types as needed to match the arguments. You can also pass an RPython string when a CCHARP argument is expected, and the C function receives a 'const char*' pointing to a read-only null-terminated character of arrays, as usual for C. The C function can have callbacks, but they must be specified explicitly as constant RPython functions. We don't support yet C functions that invoke callbacks passed otherwise (e.g. set by a previous C call). releasegil: whether it's ok to release the GIL around the call. Default is yes, unless sandboxsafe is set, in which case we consider that the function is really short-running and don't bother releasing the GIL. An explicit True or False overrides this logic. """ if _callable is not None: assert callable(_callable) ext_type = lltype.FuncType(args, result) if _callable is None: if macro is not None: if macro is True: macro = name _callable = generate_macro_wrapper( name, macro, ext_type, compilation_info) else: _callable = ll2ctypes.LL2CtypesCallable(ext_type, calling_conv) else: assert macro is None, "'macro' is useless if you specify '_callable'" if elidable_function: _callable._elidable_function_ = True kwds = {} has_callback = False for ARG in args: if _isfunctype(ARG): has_callback = True if has_callback: kwds['_callbacks'] = callbackholder = CallbackHolder() else: callbackholder = None if releasegil in (False, True): # invoke the around-handlers, which release the GIL, if and only if # the C function is thread-safe. invoke_around_handlers = releasegil else: # default case: # invoke the around-handlers only for "not too small" external calls; # sandboxsafe is a hint for "too-small-ness" (e.g. math functions). # Also, _nowrapper functions cannot release the GIL, by default. invoke_around_handlers = not sandboxsafe and not _nowrapper if random_effects_on_gcobjs not in (False, True): random_effects_on_gcobjs = ( invoke_around_handlers or # because it can release the GIL has_callback) # because the callback can do it assert not (elidable_function and random_effects_on_gcobjs) funcptr = lltype.functionptr(ext_type, name, external='C', compilation_info=compilation_info, _callable=_callable, _safe_not_sandboxed=sandboxsafe, _debugexc=True, # on top of llinterp canraise=False, random_effects_on_gcobjs= random_effects_on_gcobjs, calling_conv=calling_conv, **kwds) if isinstance(_callable, ll2ctypes.LL2CtypesCallable): _callable.funcptr = funcptr if _nowrapper: assert save_err == RFFI_ERR_NONE return funcptr if invoke_around_handlers: # The around-handlers are releasing the GIL in a threaded pypy. # We need tons of care to ensure that no GC operation and no # exception checking occurs while the GIL is released. # The actual call is done by this small piece of non-inlinable # generated code in order to avoid seeing any GC pointer: # neither '*args' nor the GC objects originally passed in as # argument to wrapper(), if any (e.g. RPython strings). argnames = ', '.join(['a%d' % i for i in range(len(args))]) source = py.code.Source(""" from rpython.rlib import rgil def call_external_function(%(argnames)s): rgil.release() # NB. it is essential that no exception checking occurs here! if %(save_err)d: from rpython.rlib import rposix rposix._errno_before(%(save_err)d) res = funcptr(%(argnames)s) if %(save_err)d: from rpython.rlib import rposix rposix._errno_after(%(save_err)d) rgil.acquire() return res """ % locals()) miniglobals = {'funcptr': funcptr, '__name__': __name__, # for module name propagation } exec source.compile() in miniglobals call_external_function = miniglobals['call_external_function'] call_external_function._dont_inline_ = True call_external_function._annspecialcase_ = 'specialize:ll' call_external_function._gctransformer_hint_close_stack_ = True # # '_call_aroundstate_target_' is used by the JIT to generate a # CALL_RELEASE_GIL directly to 'funcptr'. This doesn't work if # 'funcptr' might be a C macro, though. if macro is None: call_external_function._call_aroundstate_target_ = funcptr, save_err # call_external_function = func_with_new_name(call_external_function, 'ccall_' + name) # don't inline, as a hack to guarantee that no GC pointer is alive # anywhere in call_external_function else: # if we don't have to invoke the GIL handling, we can just call # the low-level function pointer carelessly if macro is None and save_err == RFFI_ERR_NONE: call_external_function = funcptr else: # ...well, unless it's a macro, in which case we still have # to hide it from the JIT... argnames = ', '.join(['a%d' % i for i in range(len(args))]) source = py.code.Source(""" def call_external_function(%(argnames)s): if %(save_err)d: from rpython.rlib import rposix rposix._errno_before(%(save_err)d) res = funcptr(%(argnames)s) if %(save_err)d: from rpython.rlib import rposix rposix._errno_after(%(save_err)d) return res """ % locals()) miniglobals = {'funcptr': funcptr, '__name__': __name__, } exec source.compile() in miniglobals call_external_function = miniglobals['call_external_function'] call_external_function = func_with_new_name(call_external_function, 'ccall_' + name) call_external_function = jit.dont_look_inside( call_external_function) unrolling_arg_tps = unrolling_iterable(enumerate(args)) def wrapper(*args): real_args = () to_free = () for i, TARGET in unrolling_arg_tps: arg = args[i] freeme = None if TARGET == CCHARP: if arg is None: arg = lltype.nullptr(CCHARP.TO) # None => (char*)NULL freeme = arg elif isinstance(arg, str): arg = str2charp(arg) # XXX leaks if a str2charp() fails with MemoryError # and was not the first in this function freeme = arg elif TARGET == CWCHARP: if arg is None: arg = lltype.nullptr(CWCHARP.TO) # None => (wchar_t*)NULL freeme = arg elif isinstance(arg, unicode): arg = unicode2wcharp(arg) # XXX leaks if a unicode2wcharp() fails with MemoryError # and was not the first in this function freeme = arg elif TARGET is VOIDP: if arg is None: arg = lltype.nullptr(VOIDP.TO) elif isinstance(arg, str): arg = str2charp(arg) freeme = arg elif isinstance(arg, unicode): arg = unicode2wcharp(arg) freeme = arg elif _isfunctype(TARGET) and not _isllptr(arg): # XXX pass additional arguments use_gil = invoke_around_handlers arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg, callbackholder, use_gil)) else: SOURCE = lltype.typeOf(arg) if SOURCE != TARGET: if TARGET is lltype.Float: arg = float(arg) elif ((isinstance(SOURCE, lltype.Number) or SOURCE is lltype.Bool) and (isinstance(TARGET, lltype.Number) or TARGET is lltype.Bool)): arg = cast(TARGET, arg) real_args = real_args + (arg,) to_free = to_free + (freeme,) res = call_external_function(*real_args) for i, TARGET in unrolling_arg_tps: if to_free[i]: lltype.free(to_free[i], flavor='raw') if rarithmetic.r_int is not r_int: if result is INT: return cast(lltype.Signed, res) elif result is UINT: return cast(lltype.Unsigned, res) return res wrapper._annspecialcase_ = 'specialize:ll' wrapper._always_inline_ = 'try' # for debugging, stick ll func ptr to that wrapper._ptr = funcptr wrapper = func_with_new_name(wrapper, name) if calling_conv != "c": wrapper = jit.dont_look_inside(wrapper) return wrapper
def alterMethods(cls): """ Alter Monte methods on behalf of AutoHelp. Return the signatures of the altered methods. NOT_RPYTHON """ atoms = [] imports = set() execNames = {"Refused": Refused} def nextName(nameIndex=[0]): name = "_%d" % nameIndex[0] nameIndex[0] += 1 return name def namedLiteral(lit): name = nextName() execNames[name] = lit return name dispatchClauses = [] methods = harvestMethods(cls) for attr, (f, verb, args, kwargs, rv) in methods.iteritems(): assignments = [] if isStarArgs(args): atomTest = "atom.verb == %r" % verb call = "self.%s(args)" % attr else: atom = getAtom(verb, len(args)) atomName = namedLiteral(atom) atoms.append(atom) atomTest = "atom is %s" % atomName argNames = [] for i, arg in enumerate(args): argName = nextName() argNames.append(argName) assignments.append("%s = args[%d]" % (argName, i)) if arg != "Any": unwrapperModule = wrappers[arg] pred = "is" + arg imports.add("from %s import %s" % (unwrapperModule, pred)) atomTest += " and %s(args[%d])" % (pred, i) unwrapper = "unwrap" + arg imports.add("from %s import %s" % (unwrapperModule, unwrapper)) assignments.append("%s = %s(%s)" % (argName, unwrapper, argName)) else: imports.add("from typhon.objects.refs import resolution") assignments.append("%s = resolution(%s)" % (argName, argName)) for k, v in kwargs.iteritems(): # Look up the default value. We're going to pop this in and # use None as a sentinel value in .extractStringKey(). ~ C. default = getKwargDefault(f, k) defaultName = namedLiteral(default) kwargName = nextName() argNames.append("%s=%s" % (k, kwargName)) assignments.append("%s = namedArgs.extractStringKey(%r, None)" % (kwargName, k.decode("utf-8"))) # If the kwarg is None, then it wasn't passed in; use the # default. Otherwise, invoke the unwrapper if one exists. assignments.append("if %s is None: %s = %s" % (kwargName, kwargName, defaultName)) if v != "Any": unwrapperModule = wrappers[v] unwrapper = "unwrap" + v imports.add("from %s import %s" % (unwrapperModule, unwrapper)) assignments.append("else: %s = %s(%s)" % (kwargName, unwrapper, kwargName)) call = "self.%s(%s)" % (attr, ",".join(argNames)) retvals = [] if rv == "Any": # No wrapping. retvals.append("return rv") elif rv == "Void": # Enforced correctness. Disobedience will not be tolerated. retvals.append("assert rv is None, 'habanero'") retvals.append("from typhon.objects.constants import NullObject") retvals.append("return NullObject") else: wrapperModule = wrappers[rv] wrapper = "wrap" + rv imports.add("from %s import %s" % (wrapperModule, wrapper)) retvals.append("return %s(rv)" % wrapper) # We need to use newlines for the assignments since kwarg assignments # are conditional. dispatchClauses.append(""" if %s: %s rv = %s %s """ % (atomTest, "\n ".join(assignments), call, ";".join(retvals))) setattr(cls, attr, f) # Temporary. Soon, all classes shall receive AutoHelp, and no class will # have a handwritten recv(). if dispatchClauses: exec py.code.Source(""" def recvNamed(self, atom, args, namedArgs): %s %s rv = self.mirandaMethods(atom, args, namedArgs) if rv is None: raise Refused(self, atom, args) else: return rv """ % (";".join(imports), "\n".join(dispatchClauses))).compile() in execNames recvNamed = execNames["recvNamed"] # This is the place for fixing the "too many constants" JIT # translation error. If there's too many dispatch clauses in # .recvNamed(), then the JIT transformation fails because too many # constant values are being mixed in. This number is determined # empirically; uncomment the next line to take measurements: # print "Made %d dispatch clauses for %r" % (len(dispatchClauses), cls) # Here's a worksheet. Please keep it up-to-date when altering this # section of code. ~ C. # * At last run, the longest working list of clauses had length 40 # * And the shortest list which broke translation had length 46 # * Assuming that each clause has a constant integral number of # constants, the number of constants per clause is 6 # * The maximum number of allowed constants is 255 # * Therefore, the longest working list can have maximum length 42 if len(dispatchClauses) >= 42: # Too many constants; forbid the JIT from recursing into us. recvNamed = dont_look_inside(recvNamed) cls.recvNamed = recvNamed return atoms
rthread.tlfield_alt_lasterror.setraw(err) else: rthread.tlfield_rpy_lasterror.setraw(err) if save_err & rffi.RFFI_SAVE_ERRNO: from rpython.rlib import rthread if save_err & rffi.RFFI_ALT_ERRNO: rthread.tlfield_alt_errno.setraw(_get_errno()) else: rthread.tlfield_rpy_errno.setraw(_get_errno()) if os.name == 'nt': is_valid_fd = jit.dont_look_inside( rffi.llexternal( "_PyVerify_fd", [rffi.INT], rffi.INT, compilation_info=errno_eci, )) def validate_fd(fd): if not is_valid_fd(fd): from errno import EBADF raise OSError(EBADF, 'Bad file descriptor') else: def is_valid_fd(fd): return 1 def validate_fd(fd): pass
def llexternal(name, args, result, _callable=None, compilation_info=ExternalCompilationInfo(), sandboxsafe=False, releasegil='auto', _nowrapper=False, calling_conv=None, elidable_function=False, macro=None, random_effects_on_gcobjs='auto', save_err=RFFI_ERR_NONE): """Build an external function that will invoke the C function 'name' with the given 'args' types and 'result' type. You get by default a wrapper that casts between number types as needed to match the arguments. You can also pass an RPython string when a CCHARP argument is expected, and the C function receives a 'const char*' pointing to a read-only null-terminated character of arrays, as usual for C. The C function can have callbacks, but they must be specified explicitly as constant RPython functions. We don't support yet C functions that invoke callbacks passed otherwise (e.g. set by a previous C call). releasegil: whether it's ok to release the GIL around the call. Default is yes, unless sandboxsafe is set, in which case we consider that the function is really short-running and don't bother releasing the GIL. An explicit True or False overrides this logic. calling_conv: if 'unknown' or 'win', the C function is not directly seen by the JIT. If 'c', it can be seen (depending on releasegil=False). For tests only, or if _nowrapper, it defaults to 'c'. """ if calling_conv is None: if sys.platform == 'win32' and not _nowrapper: calling_conv = 'unknown' else: calling_conv = 'c' if _callable is not None: assert callable(_callable) ext_type = lltype.FuncType(args, result) if _callable is None: if macro is not None: if macro is True: macro = name _callable = generate_macro_wrapper( name, macro, ext_type, compilation_info) else: _callable = ll2ctypes.LL2CtypesCallable(ext_type, 'c' if calling_conv == 'unknown' else calling_conv) else: assert macro is None, "'macro' is useless if you specify '_callable'" if elidable_function: _callable._elidable_function_ = True kwds = {} has_callback = False for ARG in args: if _isfunctype(ARG): has_callback = True if has_callback: kwds['_callbacks'] = callbackholder = CallbackHolder() else: callbackholder = None if releasegil in (False, True): # invoke the around-handlers, which release the GIL, if and only if # the C function is thread-safe. invoke_around_handlers = releasegil else: # default case: # invoke the around-handlers only for "not too small" external calls; # sandboxsafe is a hint for "too-small-ness" (e.g. math functions). # Also, _nowrapper functions cannot release the GIL, by default. invoke_around_handlers = not sandboxsafe and not _nowrapper if random_effects_on_gcobjs not in (False, True): random_effects_on_gcobjs = ( invoke_around_handlers or # because it can release the GIL has_callback) # because the callback can do it assert not (elidable_function and random_effects_on_gcobjs) funcptr = lltype.functionptr(ext_type, name, external='C', compilation_info=compilation_info, _callable=_callable, _safe_not_sandboxed=sandboxsafe, _debugexc=True, # on top of llinterp canraise=False, random_effects_on_gcobjs= random_effects_on_gcobjs, calling_conv=calling_conv, **kwds) if isinstance(_callable, ll2ctypes.LL2CtypesCallable): _callable.funcptr = funcptr if _nowrapper: assert save_err == RFFI_ERR_NONE return funcptr if invoke_around_handlers: # The around-handlers are releasing the GIL in a threaded pypy. # We need tons of care to ensure that no GC operation and no # exception checking occurs while the GIL is released. # The actual call is done by this small piece of non-inlinable # generated code in order to avoid seeing any GC pointer: # neither '*args' nor the GC objects originally passed in as # argument to wrapper(), if any (e.g. RPython strings). argnames = ', '.join(['a%d' % i for i in range(len(args))]) source = py.code.Source(""" from rpython.rlib import rgil def call_external_function(%(argnames)s): rgil.release() # NB. it is essential that no exception checking occurs here! if %(save_err)d: from rpython.rlib import rposix rposix._errno_before(%(save_err)d) res = funcptr(%(argnames)s) if %(save_err)d: from rpython.rlib import rposix rposix._errno_after(%(save_err)d) rgil.acquire() return res """ % locals()) miniglobals = {'funcptr': funcptr, '__name__': __name__, # for module name propagation } exec source.compile() in miniglobals call_external_function = miniglobals['call_external_function'] call_external_function._dont_inline_ = True call_external_function._annspecialcase_ = 'specialize:ll' call_external_function._gctransformer_hint_close_stack_ = True # # '_call_aroundstate_target_' is used by the JIT to generate a # CALL_RELEASE_GIL directly to 'funcptr'. This doesn't work if # 'funcptr' might be a C macro, though. if macro is None: call_external_function._call_aroundstate_target_ = funcptr, save_err # call_external_function = func_with_new_name(call_external_function, 'ccall_' + name) # don't inline, as a hack to guarantee that no GC pointer is alive # anywhere in call_external_function else: # if we don't have to invoke the GIL handling, we can just call # the low-level function pointer carelessly # ...well, unless it's a macro, in which case we still have # to hide it from the JIT... need_wrapper = (macro is not None or save_err != RFFI_ERR_NONE) # ...and unless we're on Windows and the calling convention is # 'win' or 'unknown' if calling_conv != 'c': need_wrapper = True # if not need_wrapper: call_external_function = funcptr else: argnames = ', '.join(['a%d' % i for i in range(len(args))]) source = py.code.Source(""" def call_external_function(%(argnames)s): if %(save_err)d: from rpython.rlib import rposix rposix._errno_before(%(save_err)d) res = funcptr(%(argnames)s) if %(save_err)d: from rpython.rlib import rposix rposix._errno_after(%(save_err)d) return res """ % locals()) miniglobals = {'funcptr': funcptr, '__name__': __name__, } exec source.compile() in miniglobals call_external_function = miniglobals['call_external_function'] call_external_function = func_with_new_name(call_external_function, 'ccall_' + name) call_external_function = jit.dont_look_inside( call_external_function) def _oops(): raise AssertionError("can't pass (any more) a unicode string" " directly to a VOIDP argument") _oops._annspecialcase_ = 'specialize:memo' nb_args = len(args) unrolling_arg_tps = unrolling_iterable(enumerate(args)) def wrapper(*args): assert len(args) == nb_args real_args = () # XXX 'to_free' leaks if an allocation fails with MemoryError # and was not the first in this function to_free = () for i, TARGET in unrolling_arg_tps: arg = args[i] if TARGET == CCHARP or TARGET is VOIDP: if arg is None: arg = lltype.nullptr(CCHARP.TO) # None => (char*)NULL to_free = to_free + (arg, '\x04') elif isinstance(arg, str): tup = get_nonmovingbuffer_final_null(arg) to_free = to_free + tup arg = tup[0] elif isinstance(arg, unicode): _oops() elif TARGET == CWCHARP: if arg is None: arg = lltype.nullptr(CWCHARP.TO) # None => (wchar_t*)NULL to_free = to_free + (arg,) elif isinstance(arg, unicode): arg = unicode2wcharp(arg) to_free = to_free + (arg,) elif _isfunctype(TARGET) and not _isllptr(arg): # XXX pass additional arguments use_gil = invoke_around_handlers arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg, callbackholder, use_gil)) else: SOURCE = lltype.typeOf(arg) if SOURCE != TARGET: if TARGET is lltype.Float: arg = float(arg) elif ((isinstance(SOURCE, lltype.Number) or SOURCE is lltype.Bool) and (isinstance(TARGET, lltype.Number) or TARGET is lltype.Bool)): arg = cast(TARGET, arg) real_args = real_args + (arg,) res = call_external_function(*real_args) for i, TARGET in unrolling_arg_tps: arg = args[i] if TARGET == CCHARP or TARGET is VOIDP: if arg is None: to_free = to_free[2:] elif isinstance(arg, str): free_nonmovingbuffer(arg, to_free[0], to_free[1]) to_free = to_free[2:] elif TARGET == CWCHARP: if arg is None: to_free = to_free[1:] elif isinstance(arg, unicode): free_wcharp(to_free[0]) to_free = to_free[1:] assert len(to_free) == 0 if rarithmetic.r_int is not r_int: if result is INT: return cast(lltype.Signed, res) elif result is UINT: return cast(lltype.Unsigned, res) return res wrapper._annspecialcase_ = 'specialize:ll' wrapper._always_inline_ = 'try' # for debugging, stick ll func ptr to that wrapper._ptr = funcptr wrapper = func_with_new_name(wrapper, name) return wrapper