Commit b22011f8 authored by Vitja Makarov's avatar Vitja Makarov

Merge pull request #80 from vitek/_defnode_refactor

DefNode refactoring and initial closure function call inlining
parents 262bc9fb d8fa1dba
...@@ -3864,6 +3864,103 @@ class SimpleCallNode(CallNode): ...@@ -3864,6 +3864,103 @@ class SimpleCallNode(CallNode):
code.funcstate.release_temp(self.opt_arg_struct) code.funcstate.release_temp(self.opt_arg_struct)
class InlinedDefNodeCallNode(CallNode):
# Inline call to defnode
#
# function PyCFunctionNode
# function_name NameNode
# args [ExprNode]
subexprs = ['args', 'function_name']
is_temp = 1
type = py_object_type
function = None
function_name = None
def can_be_inlined(self):
func_type= self.function.def_node
if func_type.star_arg or func_type.starstar_arg:
return False
if len(func_type.args) != len(self.args):
return False
return True
def analyse_types(self, env):
self.function_name.analyse_types(env)
for arg in self.args:
arg.analyse_types(env)
func_type = self.function.def_node
actual_nargs = len(self.args)
# Coerce arguments
some_args_in_temps = False
for i in xrange(actual_nargs):
formal_type = func_type.args[i].type
arg = self.args[i].coerce_to(formal_type, env)
if arg.is_temp:
if i > 0:
# first argument in temp doesn't impact subsequent arguments
some_args_in_temps = True
elif arg.type.is_pyobject and not env.nogil:
if arg.nonlocally_immutable():
# plain local variables are ok
pass
else:
# we do not safely own the argument's reference,
# but we must make sure it cannot be collected
# before we return from the function, so we create
# an owned temp reference to it
if i > 0: # first argument doesn't matter
some_args_in_temps = True
arg = arg.coerce_to_temp(env)
self.args[i] = arg
if some_args_in_temps:
# if some args are temps and others are not, they may get
# constructed in the wrong order (temps first) => make
# sure they are either all temps or all not temps (except
# for the last argument, which is evaluated last in any
# case)
for i in xrange(actual_nargs-1):
arg = self.args[i]
if arg.nonlocally_immutable():
# locals, C functions, unassignable types are safe.
pass
elif arg.type.is_cpp_class:
# Assignment has side effects, avoid.
pass
elif env.nogil and arg.type.is_pyobject:
# can't copy a Python reference into a temp in nogil
# env (this is safe: a construction would fail in
# nogil anyway)
pass
else:
#self.args[i] = arg.coerce_to_temp(env)
# instead: issue a warning
if i > 0:
warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
break
def generate_result_code(self, code):
arg_code = [self.function_name.py_result()]
func_type = self.function.def_node
for arg, proto_arg in zip(self.args, func_type.args):
if arg.type.is_pyobject:
arg_code.append(arg.result_as(proto_arg.type))
else:
arg_code.append(arg.result())
arg_code = ', '.join(arg_code)
code.putln(
"%s = %s(%s); %s" % (
self.result(),
self.function.def_node.entry.pyfunc_cname,
arg_code,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class PythonCapiFunctionNode(ExprNode): class PythonCapiFunctionNode(ExprNode):
subexprs = [] subexprs = []
def __init__(self, pos, py_name, cname, func_type, utility_code = None): def __init__(self, pos, py_name, cname, func_type, utility_code = None):
...@@ -6252,15 +6349,16 @@ class GeneratorExpressionNode(LambdaNode): ...@@ -6252,15 +6349,16 @@ class GeneratorExpressionNode(LambdaNode):
super(GeneratorExpressionNode, self).analyse_declarations(env) super(GeneratorExpressionNode, self).analyse_declarations(env)
# No pymethdef required # No pymethdef required
self.def_node.pymethdef_required = False self.def_node.pymethdef_required = False
self.def_node.py_wrapper_required = False
self.def_node.is_cyfunction = False self.def_node.is_cyfunction = False
# Force genexpr signature # Force genexpr signature
self.def_node.entry.signature = TypeSlots.pyfunction_noargs self.def_node.entry.signature = TypeSlots.pyfunction_noargs
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln( code.putln(
'%s = %s(%s, NULL); %s' % ( '%s = %s(%s); %s' % (
self.result(), self.result(),
self.def_node.entry.func_cname, self.def_node.entry.pyfunc_cname,
self.self_result_code(), self.self_result_code(),
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
......
...@@ -327,6 +327,32 @@ class NameReference(object): ...@@ -327,6 +327,32 @@ class NameReference(object):
return '%s(entry=%r)' % (self.__class__.__name__, self.entry) return '%s(entry=%r)' % (self.__class__.__name__, self.entry)
class ControlFlowState(list):
# Keeps track of Node's entry assignments
#
# cf_is_null [boolean] It is uninitialized
# cf_maybe_null [boolean] May be uninitialized
# is_single [boolean] Has only one assignment at this point
cf_maybe_null = False
cf_is_null = False
is_single = False
def __init__(self, state):
if Uninitialized in state:
state.discard(Uninitialized)
self.cf_maybe_null = True
if not state:
self.cf_is_null = True
else:
if len(state) == 1:
self.is_single = True
super(ControlFlowState, self).__init__(state)
def one(self):
return self[0]
class GVContext(object): class GVContext(object):
"""Graphviz subgraph object.""" """Graphviz subgraph object."""
...@@ -530,11 +556,10 @@ def check_definitions(flow, compiler_directives): ...@@ -530,11 +556,10 @@ def check_definitions(flow, compiler_directives):
messages.report() messages.report()
# Remove Uninitialized from cf_state
for node in assmt_nodes: for node in assmt_nodes:
node.cf_state.discard(Uninitialized) node.cf_state = ControlFlowState(node.cf_state)
for node in references: for node in references:
node.cf_state.discard(Uninitialized) node.cf_state = ControlFlowState(node.cf_state)
class AssignmentCollector(TreeVisitor): class AssignmentCollector(TreeVisitor):
...@@ -632,13 +657,7 @@ class ControlFlowAnalysis(CythonTransform): ...@@ -632,13 +657,7 @@ class ControlFlowAnalysis(CythonTransform):
return node return node
def visit_DefNode(self, node): def visit_DefNode(self, node):
## XXX: no target name node here
node.used = True node.used = True
entry = node.entry
if entry.is_anonymous:
entry = self.env.lookup(node.name)
if entry:
self.flow.mark_assignment(node, object_expr_not_none, entry)
return self.visit_FuncDefNode(node) return self.visit_FuncDefNode(node)
def visit_GeneratorBodyDefNode(self, node): def visit_GeneratorBodyDefNode(self, node):
......
...@@ -19,6 +19,7 @@ funcdoc_prefix = pyrex_prefix + "doc_" ...@@ -19,6 +19,7 @@ funcdoc_prefix = pyrex_prefix + "doc_"
enum_prefix = pyrex_prefix + "e_" enum_prefix = pyrex_prefix + "e_"
func_prefix = pyrex_prefix + "f_" func_prefix = pyrex_prefix + "f_"
pyfunc_prefix = pyrex_prefix + "pf_" pyfunc_prefix = pyrex_prefix + "pf_"
pywrap_prefix = pyrex_prefix + "pw_"
genbody_prefix = pyrex_prefix + "gb_" genbody_prefix = pyrex_prefix + "gb_"
gstab_prefix = pyrex_prefix + "getsets_" gstab_prefix = pyrex_prefix + "getsets_"
prop_get_prefix = pyrex_prefix + "getprop_" prop_get_prefix = pyrex_prefix + "getprop_"
......
...@@ -1308,7 +1308,6 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1308,7 +1308,6 @@ class FuncDefNode(StatNode, BlockNode):
# with fused argument types with a FusedCFuncDefNode # with fused argument types with a FusedCFuncDefNode
py_func = None py_func = None
assmt = None
needs_closure = False needs_closure = False
needs_outer_scope = False needs_outer_scope = False
pymethdef_required = False pymethdef_required = False
...@@ -1405,14 +1404,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1405,14 +1404,7 @@ class FuncDefNode(StatNode, BlockNode):
if 'cython_unused' not in self.modifiers: if 'cython_unused' not in self.modifiers:
self.modifiers = self.modifiers + ['cython_unused'] self.modifiers = self.modifiers + ['cython_unused']
preprocessor_guard = None preprocessor_guard = self.get_preprocessor_guard()
if self.entry.is_special and not is_buffer_slot:
slot = TypeSlots.method_name_to_slot.get(self.entry.name)
if slot:
preprocessor_guard = slot.preprocessor_guard_code()
if (self.entry.name == '__long__' and
not self.entry.scope.lookup_here('__int__')):
preprocessor_guard = None
profile = code.globalstate.directives['profile'] profile = code.globalstate.directives['profile']
if profile and lenv.nogil: if profile and lenv.nogil:
...@@ -1459,7 +1451,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1459,7 +1451,7 @@ class FuncDefNode(StatNode, BlockNode):
self.generate_argument_declarations(lenv, code) self.generate_argument_declarations(lenv, code)
for entry in lenv.var_entries: for entry in lenv.var_entries:
if not entry.in_closure: if not (entry.in_closure or entry.is_arg):
code.put_var_declaration(entry) code.put_var_declaration(entry)
# Initialize the return variable __pyx_r # Initialize the return variable __pyx_r
...@@ -1555,7 +1547,8 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1555,7 +1547,8 @@ class FuncDefNode(StatNode, BlockNode):
is_cdef = isinstance(self, CFuncDefNode) is_cdef = isinstance(self, CFuncDefNode)
for entry in lenv.arg_entries: for entry in lenv.arg_entries:
if entry.type.is_pyobject: if entry.type.is_pyobject:
if (acquire_gil or entry.assignments) and not entry.in_closure: if ((acquire_gil or len(entry.cf_assignments) > 1) and
not entry.in_closure):
code.put_var_incref(entry) code.put_var_incref(entry)
# Note: defaults are always increffed. For def functions, we # Note: defaults are always increffed. For def functions, we
...@@ -1565,6 +1558,9 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1565,6 +1558,9 @@ class FuncDefNode(StatNode, BlockNode):
if is_cdef and entry.type.is_memoryviewslice: if is_cdef and entry.type.is_memoryviewslice:
code.put_incref_memoryviewslice(entry.cname, code.put_incref_memoryviewslice(entry.cname,
have_gil=not lenv.nogil) have_gil=not lenv.nogil)
for entry in lenv.var_entries:
if entry.is_arg and len(entry.cf_assignments) > 1:
code.put_var_incref(entry)
# ----- Initialise local buffer auxiliary variables # ----- Initialise local buffer auxiliary variables
for entry in lenv.var_entries + lenv.arg_entries: for entry in lenv.var_entries + lenv.arg_entries:
...@@ -1709,14 +1705,15 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1709,14 +1705,15 @@ class FuncDefNode(StatNode, BlockNode):
if entry.type.is_memoryviewslice: if entry.type.is_memoryviewslice:
code.put_xdecref_memoryviewslice(entry.cname, code.put_xdecref_memoryviewslice(entry.cname,
have_gil=not lenv.nogil) have_gil=not lenv.nogil)
elif entry.type.is_pyobject: elif entry.type.is_pyobject:
code.put_var_decref(entry) if not entry.is_arg or len(entry.cf_assignments) > 1:
code.put_var_decref(entry)
# Decref any increfed args # Decref any increfed args
for entry in lenv.arg_entries: for entry in lenv.arg_entries:
if entry.type.is_pyobject: if entry.type.is_pyobject:
if (acquire_gil or entry.assignments) and not entry.in_closure: if ((acquire_gil or len(entry.cf_assignments) > 1) and
not entry.in_closure):
code.put_var_decref(entry) code.put_var_decref(entry)
if entry.type.is_memoryviewslice: if entry.type.is_memoryviewslice:
code.put_xdecref_memoryviewslice(entry.cname, code.put_xdecref_memoryviewslice(entry.cname,
...@@ -1820,9 +1817,6 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1820,9 +1817,6 @@ class FuncDefNode(StatNode, BlockNode):
for arg in self.args: for arg in self.args:
if not arg.is_dynamic: if not arg.is_dynamic:
arg.generate_assignment_code(code) arg.generate_assignment_code(code)
# For Python class methods, create and store function object
if self.assmt:
self.assmt.generate_execution_code(code)
# #
# Special code for the __getbuffer__ function # Special code for the __getbuffer__ function
...@@ -1853,6 +1847,21 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1853,6 +1847,21 @@ class FuncDefNode(StatNode, BlockNode):
code.putln("__Pyx_DECREF(Py_None); %s->obj = NULL;" % info) code.putln("__Pyx_DECREF(Py_None); %s->obj = NULL;" % info)
code.putln("}") code.putln("}")
def get_preprocessor_guard(self):
is_buffer_slot = ((self.entry.name == "__getbuffer__" and
self.entry.scope.is_c_class_scope) or
(self.entry.name == "__releasebuffer__" and
self.entry.scope.is_c_class_scope))
if self.entry.is_special and not is_buffer_slot:
slot = TypeSlots.method_name_to_slot.get(self.entry.name)
if slot:
preprocessor_guard = slot.preprocessor_guard_code()
if (self.entry.name == '__long__' and
not self.entry.scope.lookup_here('__int__')):
preprocessor_guard = None
return preprocessor_guard
class CFuncDefNode(FuncDefNode): class CFuncDefNode(FuncDefNode):
# C function definition. # C function definition.
# #
...@@ -2548,7 +2557,9 @@ def __pyx_fused_cpdef(signatures, args, kwargs): ...@@ -2548,7 +2557,9 @@ def __pyx_fused_cpdef(signatures, args, kwargs):
py_func.analyse_declarations(env) py_func.analyse_declarations(env)
# ... and its body # ... and its body
py_func.scope = env py_func.scope = env
ParseTreeTransforms.AnalyseDeclarationsTransform(None)(py_func)
# Will be analysed later by underlying AnalyseDeclarationsTransform
#ParseTreeTransforms.AnalyseDeclarationsTransform(None)(py_func)
e, orig_e = py_func.entry, orig_py_func.entry e, orig_e = py_func.entry, orig_py_func.entry
...@@ -2607,6 +2618,8 @@ class PyArgDeclNode(Node): ...@@ -2607,6 +2618,8 @@ class PyArgDeclNode(Node):
# entry Symtab.Entry # entry Symtab.Entry
# annotation ExprNode or None Py3 argument annotation # annotation ExprNode or None Py3 argument annotation
child_attrs = [] child_attrs = []
is_self_arg = False
is_type_arg = False
def generate_function_definitions(self, env, code): def generate_function_definitions(self, env, code):
self.entry.generate_function_definitions(env, code) self.entry.generate_function_definitions(env, code)
...@@ -2633,8 +2646,6 @@ class DefNode(FuncDefNode): ...@@ -2633,8 +2646,6 @@ class DefNode(FuncDefNode):
# The following subnode is constructed internally # The following subnode is constructed internally
# when the def statement is inside a Python class definition. # when the def statement is inside a Python class definition.
# #
# assmt AssignmentNode Function construction/assignment
#
# fused_py_func DefNode The original fused cpdef DefNode # fused_py_func DefNode The original fused cpdef DefNode
# (in case this is a specialization) # (in case this is a specialization)
# specialized_cpdefs [DefNode] list of specialized cpdef DefNodes # specialized_cpdefs [DefNode] list of specialized cpdef DefNodes
...@@ -2645,9 +2656,6 @@ class DefNode(FuncDefNode): ...@@ -2645,9 +2656,6 @@ class DefNode(FuncDefNode):
child_attrs = ["args", "star_arg", "starstar_arg", "body", "decorators"] child_attrs = ["args", "star_arg", "starstar_arg", "body", "decorators"]
lambda_name = None lambda_name = None
assmt = None
num_kwonly_args = 0
num_required_kw_args = 0
reqd_kw_flags_cname = "0" reqd_kw_flags_cname = "0"
is_wrapper = 0 is_wrapper = 0
no_assignment_synthesis = 0 no_assignment_synthesis = 0
...@@ -2663,6 +2671,9 @@ class DefNode(FuncDefNode): ...@@ -2663,6 +2671,9 @@ class DefNode(FuncDefNode):
fused_py_func = False fused_py_func = False
specialized_cpdefs = None specialized_cpdefs = None
py_wrapper = None
py_wrapper_required = True
func_cname = None
def __init__(self, pos, **kwds): def __init__(self, pos, **kwds):
FuncDefNode.__init__(self, pos, **kwds) FuncDefNode.__init__(self, pos, **kwds)
...@@ -2781,6 +2792,16 @@ class DefNode(FuncDefNode): ...@@ -2781,6 +2792,16 @@ class DefNode(FuncDefNode):
self.return_type = self.entry.signature.return_type() self.return_type = self.entry.signature.return_type()
self.create_local_scope(env) self.create_local_scope(env)
self.py_wrapper = DefNodeWrapper(
self.pos,
target=self,
name=self.entry.name,
args=self.args,
star_arg=self.star_arg,
starstar_arg=self.starstar_arg,
return_type=self.return_type)
self.py_wrapper.analyse_declarations(env)
def analyse_argument_types(self, env): def analyse_argument_types(self, env):
directive_locals = self.directive_locals = env.directives['locals'] directive_locals = self.directive_locals = env.directives['locals']
allow_none_for_extension_args = env.directives['allow_none_for_extension_args'] allow_none_for_extension_args = env.directives['allow_none_for_extension_args']
...@@ -2926,17 +2947,6 @@ class DefNode(FuncDefNode): ...@@ -2926,17 +2947,6 @@ class DefNode(FuncDefNode):
"(%d declared, %s expected)" % ( "(%d declared, %s expected)" % (
desc, self.name, len(self.args), expected_str)) desc, self.name, len(self.args), expected_str))
def signature_has_nongeneric_args(self):
argcount = len(self.args)
if argcount == 0 or (
argcount == 1 and (self.args[0].is_self_arg or
self.args[0].is_type_arg)):
return 0
return 1
def signature_has_generic_args(self):
return self.entry.signature.has_generic_args
def declare_pyfunction(self, env): def declare_pyfunction(self, env):
#print "DefNode.declare_pyfunction:", self.name, "in", env ### #print "DefNode.declare_pyfunction:", self.name, "in", env ###
name = self.name name = self.name
...@@ -2950,8 +2960,7 @@ class DefNode(FuncDefNode): ...@@ -2950,8 +2960,7 @@ class DefNode(FuncDefNode):
entry = env.declare_pyfunction(name, self.pos, allow_redefine=not self.is_wrapper) entry = env.declare_pyfunction(name, self.pos, allow_redefine=not self.is_wrapper)
self.entry = entry self.entry = entry
prefix = env.next_id(env.scope_prefix) prefix = env.next_id(env.scope_prefix)
entry.func_cname = Naming.pyfunc_prefix + prefix + name self.entry.pyfunc_cname = Naming.pyfunc_prefix + prefix + name
entry.pymethdef_cname = Naming.pymethdef_prefix + prefix + name
if Options.docstrings: if Options.docstrings:
entry.doc = embed_position(self.pos, self.doc) entry.doc = embed_position(self.pos, self.doc)
entry.doc_cname = Naming.funcdoc_prefix + prefix + name entry.doc_cname = Naming.funcdoc_prefix + prefix + name
...@@ -2967,6 +2976,7 @@ class DefNode(FuncDefNode): ...@@ -2967,6 +2976,7 @@ class DefNode(FuncDefNode):
entry = env.declare_lambda_function(self.lambda_name, self.pos) entry = env.declare_lambda_function(self.lambda_name, self.pos)
entry.doc = None entry.doc = None
self.entry = entry self.entry = entry
self.entry.pyfunc_cname = entry.cname
def declare_arguments(self, env): def declare_arguments(self, env):
for arg in self.args: for arg in self.args:
...@@ -2981,10 +2991,6 @@ class DefNode(FuncDefNode): ...@@ -2981,10 +2991,6 @@ class DefNode(FuncDefNode):
arg.entry.is_arg = 1 arg.entry.is_arg = 1
arg.entry.used = 1 arg.entry.used = 1
arg.entry.is_self_arg = arg.is_self_arg arg.entry.is_self_arg = arg.is_self_arg
if arg.hdr_type:
if arg.is_self_arg or arg.is_type_arg or \
(arg.type.is_extension_type and not arg.hdr_type.is_extension_type):
arg.entry.is_declared_generic = 1
self.declare_python_arg(env, self.star_arg) self.declare_python_arg(env, self.star_arg)
self.declare_python_arg(env, self.starstar_arg) self.declare_python_arg(env, self.starstar_arg)
...@@ -3005,14 +3011,13 @@ class DefNode(FuncDefNode): ...@@ -3005,14 +3011,13 @@ class DefNode(FuncDefNode):
self.local_scope.directives = env.directives self.local_scope.directives = env.directives
self.analyse_default_values(env) self.analyse_default_values(env)
if self.needs_assignment_synthesis(env): if not self.needs_assignment_synthesis(env) and self.decorators:
# Shouldn't we be doing this at the module level too?
self.synthesize_assignment_node(env)
elif self.decorators:
for decorator in self.decorators[::-1]: for decorator in self.decorators[::-1]:
decorator.decorator.analyse_expressions(env) decorator.decorator.analyse_expressions(env)
def needs_assignment_synthesis(self, env, code=None): def needs_assignment_synthesis(self, env, code=None):
if self.is_wrapper:
return False
if self.specialized_cpdefs or self.is_staticmethod: if self.specialized_cpdefs or self.is_staticmethod:
return True return True
if self.no_assignment_synthesis: if self.no_assignment_synthesis:
...@@ -3027,122 +3032,272 @@ class DefNode(FuncDefNode): ...@@ -3027,122 +3032,272 @@ class DefNode(FuncDefNode):
return code.globalstate.directives['binding'] return code.globalstate.directives['binding']
return env.is_py_class_scope or env.is_closure_scope return env.is_py_class_scope or env.is_closure_scope
def synthesize_assignment_node(self, env): def error_value(self):
import ExprNodes return self.entry.signature.error_value
def caller_will_check_exceptions(self):
return 1
def generate_function_definitions(self, env, code):
# Before closure cnames are mangled
if self.py_wrapper_required:
# func_cname might be modified by @cname
self.py_wrapper.func_cname = self.entry.func_cname
self.py_wrapper.generate_function_definitions(env, code)
FuncDefNode.generate_function_definitions(self, env, code)
if self.fused_py_func: def generate_function_header(self, code, with_pymethdef, proto_only=0):
if proto_only:
if self.py_wrapper_required:
self.py_wrapper.generate_function_header(
code, with_pymethdef, True)
return return
arg_code_list = []
if self.entry.signature.has_dummy_arg:
if self.needs_outer_scope or self.defaults_struct:
self_arg = 'PyObject *%s' % Naming.self_cname
else:
self_arg = 'CYTHON_UNUSED PyObject *%s' % Naming.self_cname
arg_code_list.append(self_arg)
genv = env def arg_decl_code(arg):
while genv.is_py_class_scope or genv.is_c_class_scope: entry = arg.entry
genv = genv.outer_scope if entry.in_closure:
cname = entry.original_cname
else:
cname = entry.cname
decl = entry.type.declaration_code(cname)
if entry.cf_used:
return decl
return 'CYTHON_UNUSED ' + decl
for arg in self.args:
arg_code_list.append(arg_decl_code(arg))
if self.star_arg:
arg_code_list.append(arg_decl_code(self.star_arg))
if self.starstar_arg:
arg_code_list.append(arg_decl_code(self.starstar_arg))
arg_code = ', '.join(arg_code_list)
dc = self.return_type.declaration_code(self.entry.pyfunc_cname)
decls_code = code.globalstate['decls']
preprocessor_guard = self.get_preprocessor_guard()
if preprocessor_guard:
decls_code.putln(preprocessor_guard)
decls_code.putln(
"static %s(%s); /* proto */" % (dc, arg_code))
if preprocessor_guard:
decls_code.putln("#endif")
code.putln("static %s(%s) {" % (dc, arg_code))
def generate_argument_declarations(self, env, code):
pass
def generate_keyword_list(self, code):
pass
def generate_argument_parsing_code(self, env, code):
# Move arguments into closure if required
def put_into_closure(entry):
if entry.in_closure:
code.putln('%s = %s;' % (entry.cname, entry.original_cname))
code.put_var_incref(entry)
code.put_var_giveref(entry)
for arg in self.args:
put_into_closure(arg.entry)
for arg in self.star_arg, self.starstar_arg:
if arg:
put_into_closure(arg.entry)
def generate_argument_type_tests(self, code):
pass
class DefNodeWrapper(FuncDefNode):
# DefNode python wrapper code generator
defnode = None
target = None # Target DefNode
def __init__(self, *args, **kwargs):
FuncDefNode.__init__(self, *args, **kwargs)
self.num_kwonly_args = self.target.num_kwonly_args
self.num_required_kw_args = self.target.num_required_kw_args
self.num_required_args = self.target.num_required_args
self.self_in_stararg = self.target.self_in_stararg
self.signature = None
def analyse_declarations(self, env):
target_entry = self.target.entry
name = self.name
prefix = env.next_id(env.scope_prefix)
target_entry.func_cname = Naming.pywrap_prefix + prefix + name
target_entry.pymethdef_cname = Naming.pymethdef_prefix + prefix + name
if genv.is_closure_scope: self.signature = target_entry.signature
rhs = self.py_cfunc_node = ExprNodes.InnerFunctionNode(
self.pos, def_node=self, def signature_has_nongeneric_args(self):
pymethdef_cname=self.entry.pymethdef_cname, argcount = len(self.args)
code_object=ExprNodes.CodeObjectNode(self)) if argcount == 0 or (
argcount == 1 and (self.args[0].is_self_arg or
self.args[0].is_type_arg)):
return 0
return 1
def signature_has_generic_args(self):
return self.signature.has_generic_args
def generate_function_body(self, code):
args = []
if self.signature.has_dummy_arg:
args.append(Naming.self_cname)
for arg in self.args:
if arg.hdr_type:
args.append(arg.type.cast_code(arg.entry.cname))
else:
args.append(arg.entry.cname)
if self.star_arg:
args.append(self.star_arg.entry.cname)
if self.starstar_arg:
args.append(self.starstar_arg.entry.cname)
args = ', '.join(args)
if not self.return_type.is_void:
code.put('%s = ' % Naming.retval_cname)
code.putln('%s(%s);' % (
self.target.entry.pyfunc_cname, args))
def generate_function_definitions(self, env, code):
lenv = self.target.local_scope
# Generate C code for header and body of function
code.putln("")
code.putln("/* Python wrapper */")
preprocessor_guard = self.target.get_preprocessor_guard()
if preprocessor_guard:
code.putln(preprocessor_guard)
code.enter_cfunc_scope()
code.return_from_error_cleanup_label = code.new_label()
with_pymethdef = (self.target.needs_assignment_synthesis(lenv, code) or
self.target.pymethdef_required)
self.generate_function_header(code, with_pymethdef)
self.generate_argument_declarations(lenv, code)
self.generate_keyword_list(code)
tempvardecl_code = code.insertion_point()
if self.return_type.is_pyobject:
retval_init = ' = 0'
else: else:
rhs = ExprNodes.PyCFunctionNode( retval_init = ''
self.pos, if not self.return_type.is_void:
def_node=self, code.putln('%s%s;' % (
pymethdef_cname=self.entry.pymethdef_cname, self.return_type.declaration_code(Naming.retval_cname),
binding=env.directives['binding'], retval_init))
specialized_cpdefs=self.specialized_cpdefs, code.put_declare_refcount_context()
code_object=ExprNodes.CodeObjectNode(self)) code.put_setup_refcount_context('%s (wrapper)' % self.name)
if env.is_py_class_scope: self.generate_argument_parsing_code(lenv, code)
rhs.binding = True self.generate_argument_type_tests(code)
self.generate_function_body(code)
self.is_cyfunction = rhs.binding # ----- Go back and insert temp variable declarations
tempvardecl_code.put_temp_declarations(code.funcstate)
if self.decorators: # ----- Error cleanup
for decorator in self.decorators[::-1]: if code.error_label in code.labels_used:
rhs = ExprNodes.SimpleCallNode( code.put_goto(code.return_label)
decorator.pos, code.put_label(code.error_label)
function = decorator.decorator, for cname, type in code.funcstate.all_managed_temps():
args = [rhs]) code.put_xdecref(cname, type)
self.assmt = SingleAssignmentNode(self.pos, # ----- Non-error return cleanup
lhs = ExprNodes.NameNode(self.pos, name = self.name), code.put_label(code.return_label)
rhs = rhs) for entry in lenv.var_entries:
self.assmt.analyse_declarations(env) if entry.is_arg and entry.type.is_pyobject:
self.assmt.analyse_expressions(env) code.put_var_decref(entry)
code.put_finish_refcount_context()
if not self.return_type.is_void:
code.putln("return %s;" % Naming.retval_cname)
code.putln('}')
code.exit_cfunc_scope()
if preprocessor_guard:
code.putln("#endif /*!(%s)*/" % preprocessor_guard)
def generate_function_header(self, code, with_pymethdef, proto_only=0): def generate_function_header(self, code, with_pymethdef, proto_only=0):
arg_code_list = [] arg_code_list = []
sig = self.entry.signature sig = self.signature
if sig.has_dummy_arg or self.self_in_stararg: if sig.has_dummy_arg or self.self_in_stararg:
arg_code_list.append( arg_code_list.append(
"PyObject *%s" % Naming.self_cname) "PyObject *%s" % Naming.self_cname)
for arg in self.args: for arg in self.args:
if not arg.is_generic: if not arg.is_generic:
if arg.is_self_arg or arg.is_type_arg: if arg.is_self_arg or arg.is_type_arg:
arg_code_list.append("PyObject *%s" % arg.hdr_cname) arg_code_list.append("PyObject *%s" % arg.hdr_cname)
else: else:
decl = arg.hdr_type.declaration_code(arg.hdr_cname) arg_code_list.append(
entry = self.local_scope.lookup(arg.name) arg.hdr_type.declaration_code(arg.hdr_cname))
if not entry.cf_used: entry = self.target.entry
arg_code_list.append('CYTHON_UNUSED ' + decl) if not entry.is_special and sig.method_flags() == [TypeSlots.method_noargs]:
else:
arg_code_list.append(decl)
if not self.entry.is_special and sig.method_flags() == [TypeSlots.method_noargs]:
arg_code_list.append("CYTHON_UNUSED PyObject *unused") arg_code_list.append("CYTHON_UNUSED PyObject *unused")
if (self.entry.scope.is_c_class_scope and self.entry.name == "__ipow__"): if entry.scope.is_c_class_scope and entry.name == "__ipow__":
arg_code_list.append("CYTHON_UNUSED PyObject *unused") arg_code_list.append("CYTHON_UNUSED PyObject *unused")
if sig.has_generic_args: if sig.has_generic_args:
arg_code_list.append( arg_code_list.append(
"PyObject *%s, PyObject *%s" "PyObject *%s, PyObject *%s"
% (Naming.args_cname, Naming.kwds_cname)) % (Naming.args_cname, Naming.kwds_cname))
arg_code = ", ".join(arg_code_list) arg_code = ", ".join(arg_code_list)
dc = self.return_type.declaration_code(self.entry.func_cname) dc = self.return_type.declaration_code(entry.func_cname)
mf = " ".join(self.modifiers).upper() header = "static %s(%s)" % (dc, arg_code)
if mf: mf += " "
header = "static %s%s(%s)" % (mf, dc, arg_code)
code.putln("%s; /*proto*/" % header) code.putln("%s; /*proto*/" % header)
if proto_only: if proto_only:
if self.fused_py_func: if self.target.fused_py_func:
# If we are the specialized version of the cpdef, we still # If we are the specialized version of the cpdef, we still
# want the prototype for the "fused cpdef", in case we're # want the prototype for the "fused cpdef", in case we're
# checking to see if our method was overridden in Python # checking to see if our method was overridden in Python
self.fused_py_func.generate_function_header( self.target.fused_py_func.generate_function_header(
code, with_pymethdef, proto_only=True) code, with_pymethdef, proto_only=True)
return return
if (Options.docstrings and self.entry.doc and if (Options.docstrings and entry.doc and
not self.fused_py_func and not self.target.fused_py_func and
not self.entry.scope.is_property_scope and not entry.scope.is_property_scope and
(not self.entry.is_special or self.entry.wrapperbase_cname)): (not entry.is_special or entry.wrapperbase_cname)):
# h_code = code.globalstate['h_code'] # h_code = code.globalstate['h_code']
docstr = self.entry.doc docstr = entry.doc
if docstr.is_unicode: if docstr.is_unicode:
docstr = docstr.utf8encode() docstr = docstr.utf8encode()
code.putln( code.putln(
'static char %s[] = "%s";' % ( 'static char %s[] = "%s";' % (
self.entry.doc_cname, entry.doc_cname,
split_string_literal(escape_byte_string(docstr)))) split_string_literal(escape_byte_string(docstr))))
if self.entry.is_special: if entry.is_special:
code.putln( code.putln(
"struct wrapperbase %s;" % self.entry.wrapperbase_cname) "struct wrapperbase %s;" % entry.wrapperbase_cname)
if with_pymethdef or self.fused_py_func: if with_pymethdef or self.target.fused_py_func:
code.put( code.put(
"static PyMethodDef %s = " % "static PyMethodDef %s = " %
self.entry.pymethdef_cname) entry.pymethdef_cname)
code.put_pymethoddef(self.entry, ";", allow_skip=False) code.put_pymethoddef(self.target.entry, ";", allow_skip=False)
code.putln("%s {" % header) code.putln("%s {" % header)
def generate_argument_declarations(self, env, code): def generate_argument_declarations(self, env, code):
for arg in self.args: for arg in self.args:
if arg.is_generic: # or arg.needs_conversion: if arg.is_generic:
if arg.needs_conversion: if arg.needs_conversion:
code.putln("PyObject *%s = 0;" % arg.hdr_cname) code.putln("PyObject *%s = 0;" % arg.hdr_cname)
elif not arg.entry.in_closure: else:
code.put_var_declaration(arg.entry) code.put_var_declaration(arg.entry)
for entry in env.var_entries:
if entry.is_arg:
code.put_var_declaration(entry)
def generate_keyword_list(self, code): def generate_keyword_list(self, code):
if self.signature_has_generic_args() and \ if self.signature_has_generic_args() and \
...@@ -3159,7 +3314,7 @@ class DefNode(FuncDefNode): ...@@ -3159,7 +3314,7 @@ class DefNode(FuncDefNode):
def generate_argument_parsing_code(self, env, code): def generate_argument_parsing_code(self, env, code):
# Generate fast equivalent of PyArg_ParseTuple call for # Generate fast equivalent of PyArg_ParseTuple call for
# generic arguments, if any, including args/kwargs # generic arguments, if any, including args/kwargs
if self.entry.signature.has_dummy_arg and not self.self_in_stararg: if self.signature.has_dummy_arg and not self.self_in_stararg:
# get rid of unused argument warning # get rid of unused argument warning
code.putln("%s = %s;" % (Naming.self_cname, Naming.self_cname)) code.putln("%s = %s;" % (Naming.self_cname, Naming.self_cname))
...@@ -3175,9 +3330,6 @@ class DefNode(FuncDefNode): ...@@ -3175,9 +3330,6 @@ class DefNode(FuncDefNode):
if not arg.type.is_pyobject: if not arg.type.is_pyobject:
if not arg.type.create_from_py_utility_code(env): if not arg.type.create_from_py_utility_code(env):
pass # will fail later pass # will fail later
elif arg.is_self_arg and arg.entry.in_closure:
# must store 'self' in the closure explicitly for extension types
self.generate_arg_assignment(arg, arg.hdr_cname, code)
if not self.signature_has_generic_args(): if not self.signature_has_generic_args():
if has_star_or_kw_args: if has_star_or_kw_args:
...@@ -3220,41 +3372,17 @@ class DefNode(FuncDefNode): ...@@ -3220,41 +3372,17 @@ class DefNode(FuncDefNode):
code.put_var_xdecref_clear(self.starstar_arg.entry) code.put_var_xdecref_clear(self.starstar_arg.entry)
else: else:
code.put_var_decref_clear(self.starstar_arg.entry) code.put_var_decref_clear(self.starstar_arg.entry)
code.put_add_traceback(self.entry.qualified_name) code.put_add_traceback(self.target.entry.qualified_name)
# The arguments are put into the closure one after the
# other, so when type errors are found, all references in
# the closure instance must be properly ref-counted to
# facilitate generic closure instance deallocation. In
# the case of an argument type error, it's best to just
# DECREF+clear the already handled references, as this
# frees their references as early as possible.
for arg in self.args:
if arg.type.is_pyobject and arg.entry.in_closure:
code.put_var_xdecref_clear(arg.entry)
if self.needs_closure:
code.put_decref(Naming.cur_scope_cname, self.local_scope.scope_class.type)
code.put_finish_refcount_context() code.put_finish_refcount_context()
code.putln("return %s;" % self.error_value()) code.putln("return %s;" % self.error_value())
if code.label_used(end_label): if code.label_used(end_label):
code.put_label(end_label) code.put_label(end_label)
# fix refnanny view on closure variables here, instead of
# doing it separately for each arg parsing special case
if self.star_arg and self.star_arg.entry.in_closure:
code.put_var_giveref(self.star_arg.entry)
if self.starstar_arg and self.starstar_arg.entry.in_closure:
code.put_var_giveref(self.starstar_arg.entry)
for arg in self.args:
if arg.type.is_pyobject and arg.entry.in_closure:
code.put_var_giveref(arg.entry)
def generate_arg_assignment(self, arg, item, code, incref_closure=True): def generate_arg_assignment(self, arg, item, code, incref_closure=True):
if arg.type.is_pyobject: if arg.type.is_pyobject:
if arg.is_generic: if arg.is_generic:
item = PyrexTypes.typecast(arg.type, PyrexTypes.py_object_type, item) item = PyrexTypes.typecast(arg.type, PyrexTypes.py_object_type, item)
entry = arg.entry entry = arg.entry
if incref_closure and entry.in_closure:
code.put_incref(item, PyrexTypes.py_object_type)
code.putln("%s = %s;" % (entry.cname, item)) code.putln("%s = %s;" % (entry.cname, item))
else: else:
func = arg.type.from_py_function func = arg.type.from_py_function
...@@ -3509,8 +3637,6 @@ class DefNode(FuncDefNode): ...@@ -3509,8 +3637,6 @@ class DefNode(FuncDefNode):
code.putln("if (unlikely(!%s)) {" % self.star_arg.entry.cname) code.putln("if (unlikely(!%s)) {" % self.star_arg.entry.cname)
if self.starstar_arg: if self.starstar_arg:
code.put_decref_clear(self.starstar_arg.entry.cname, py_object_type) code.put_decref_clear(self.starstar_arg.entry.cname, py_object_type)
if self.needs_closure:
code.put_decref(Naming.cur_scope_cname, self.local_scope.scope_class.type)
code.put_finish_refcount_context() code.put_finish_refcount_context()
code.putln('return %s;' % self.error_value()) code.putln('return %s;' % self.error_value())
code.putln('}') code.putln('}')
...@@ -3527,10 +3653,10 @@ class DefNode(FuncDefNode): ...@@ -3527,10 +3653,10 @@ class DefNode(FuncDefNode):
code.putln("PyObject* values[%d] = {%s};" % ( code.putln("PyObject* values[%d] = {%s};" % (
max_args, ','.join('0'*max_args))) max_args, ','.join('0'*max_args)))
if self.defaults_struct: if self.target.defaults_struct:
code.putln('%s *%s = __Pyx_CyFunction_Defaults(%s, %s);' % ( code.putln('%s *%s = __Pyx_CyFunction_Defaults(%s, %s);' % (
self.defaults_struct, Naming.dynamic_args_cname, self.target.defaults_struct, Naming.dynamic_args_cname,
self.defaults_struct, Naming.self_cname)) self.target.defaults_struct, Naming.self_cname))
# assign borrowed Python default values to the values array, # assign borrowed Python default values to the values array,
# so that they can be overwritten by received arguments below # so that they can be overwritten by received arguments below
...@@ -3697,10 +3823,6 @@ class DefNode(FuncDefNode): ...@@ -3697,10 +3823,6 @@ class DefNode(FuncDefNode):
for arg in self.args: for arg in self.args:
if arg.needs_conversion: if arg.needs_conversion:
self.generate_arg_conversion(arg, code) self.generate_arg_conversion(arg, code)
elif not arg.is_self_arg and arg.entry.in_closure:
if arg.type.is_pyobject:
code.put_incref(arg.hdr_cname, py_object_type)
code.putln('%s = %s;' % (arg.entry.cname, arg.hdr_cname))
def generate_arg_conversion(self, arg, code): def generate_arg_conversion(self, arg, code):
# Generate conversion code for one argument. # Generate conversion code for one argument.
...@@ -3768,10 +3890,7 @@ class DefNode(FuncDefNode): ...@@ -3768,10 +3890,7 @@ class DefNode(FuncDefNode):
self.generate_arg_none_check(arg, code) self.generate_arg_none_check(arg, code)
def error_value(self): def error_value(self):
return self.entry.signature.error_value return self.signature.error_value
def caller_will_check_exceptions(self):
return 1
class GeneratorDefNode(DefNode): class GeneratorDefNode(DefNode):
...@@ -7837,6 +7956,8 @@ class CnameDecoratorNode(StatNode): ...@@ -7837,6 +7956,8 @@ class CnameDecoratorNode(StatNode):
e.cname = self.cname e.cname = self.cname
e.func_cname = self.cname e.func_cname = self.cname
e.used = True e.used = True
if e.pyfunc_cname and '.' in e.pyfunc_cname:
e.pyfunc_cname = self.mangle(e.pyfunc_cname)
elif is_struct_or_enum: elif is_struct_or_enum:
e.cname = e.type.cname = self.cname e.cname = e.type.cname = self.cname
else: else:
...@@ -7853,12 +7974,16 @@ class CnameDecoratorNode(StatNode): ...@@ -7853,12 +7974,16 @@ class CnameDecoratorNode(StatNode):
for name, entry in scope.entries.iteritems(): for name, entry in scope.entries.iteritems():
if entry.func_cname: if entry.func_cname:
cname = entry.cname entry.func_cname = self.mangle(entry.cname)
if '.' in cname: if entry.pyfunc_cname:
# remove __pyx_base from func_cname old = entry.pyfunc_cname
cname = cname.split('.')[-1] entry.pyfunc_cname = self.mangle(entry.pyfunc_cname)
entry.func_cname = '%s_%s' % (self.cname, cname) def mangle(self, cname):
if '.' in cname:
# remove __pyx_base from func_cname
cname = cname.split('.')[-1]
return '%s_%s' % (self.cname, cname)
def analyse_expressions(self, env): def analyse_expressions(self, env):
self.node.analyse_expressions(env) self.node.analyse_expressions(env)
......
...@@ -1643,6 +1643,28 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform): ...@@ -1643,6 +1643,28 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
return node return node
return kwargs return kwargs
class InlineDefNodeCalls(Visitor.CythonTransform):
visit_Node = Visitor.VisitorTransform.recurse_to_children
def visit_SimpleCallNode(self, node):
self.visitchildren(node)
if not self.current_directives.get('optimize.inline_defnode_calls'):
return node
function_name = node.function
if not function_name.is_name:
return node
if not function_name.cf_state.is_single:
return node
function = function_name.cf_state.one().rhs
if not isinstance(function, ExprNodes.PyCFunctionNode):
return node
inlined = ExprNodes.InlinedDefNodeCallNode(
node.pos, function_name=function_name,
function=function, args=node.args)
if inlined.can_be_inlined():
return inlined
return node
class OptimizeBuiltinCalls(Visitor.EnvTransform): class OptimizeBuiltinCalls(Visitor.EnvTransform):
"""Optimize some common methods calls and instantiation patterns """Optimize some common methods calls and instantiation patterns
......
...@@ -106,6 +106,9 @@ directive_defaults = { ...@@ -106,6 +106,9 @@ directive_defaults = {
'warn.unused_arg': False, 'warn.unused_arg': False,
'warn.unused_result': False, 'warn.unused_result': False,
# optimizations
'optimize.inline_defnode_calls': False,
# remove unreachable code # remove unreachable code
'remove_unreachable': True, 'remove_unreachable': True,
......
...@@ -1493,6 +1493,9 @@ if VALUE is not None: ...@@ -1493,6 +1493,9 @@ if VALUE is not None:
if node.py_func: if node.py_func:
node.stats.insert(0, node.py_func) node.stats.insert(0, node.py_func)
self.visit(node.py_func)
if node.py_func.needs_assignment_synthesis(env):
node = [node, self._synthesize_assignment(node.py_func, env)]
else: else:
node.body.analyse_declarations(lenv) node.body.analyse_declarations(lenv)
...@@ -1514,6 +1517,54 @@ if VALUE is not None: ...@@ -1514,6 +1517,54 @@ if VALUE is not None:
self.seen_vars_stack.pop() self.seen_vars_stack.pop()
return node return node
def visit_DefNode(self, node):
node = self.visit_FuncDefNode(node)
env = self.env_stack[-1]
if (not isinstance(node, Nodes.DefNode) or
node.fused_py_func or node.is_generator_body or
not node.needs_assignment_synthesis(env)):
return node
return [node, self._synthesize_assignment(node, env)]
def _synthesize_assignment(self, node, env):
# Synthesize assignment node and put it right after defnode
genv = env
while genv.is_py_class_scope or genv.is_c_class_scope:
genv = genv.outer_scope
if genv.is_closure_scope:
rhs = node.py_cfunc_node = ExprNodes.InnerFunctionNode(
node.pos, def_node=node,
pymethdef_cname=node.entry.pymethdef_cname,
code_object=ExprNodes.CodeObjectNode(node))
else:
rhs = ExprNodes.PyCFunctionNode(
node.pos,
def_node=node,
pymethdef_cname=node.entry.pymethdef_cname,
binding=self.current_directives.get('binding'),
specialized_cpdefs=node.specialized_cpdefs,
code_object=ExprNodes.CodeObjectNode(node))
if env.is_py_class_scope:
rhs.binding = True
node.is_cyfunction = rhs.binding
if node.decorators:
for decorator in node.decorators[::-1]:
rhs = ExprNodes.SimpleCallNode(
decorator.pos,
function = decorator.decorator,
args = [rhs])
assmt = Nodes.SingleAssignmentNode(
node.pos,
lhs=ExprNodes.NameNode(node.pos,name=node.name),
rhs=rhs)
assmt.analyse_declarations(env)
return assmt
def visit_ScopedExprNode(self, node): def visit_ScopedExprNode(self, node):
env = self.env_stack[-1] env = self.env_stack[-1]
node.analyse_declarations(env) node.analyse_declarations(env)
...@@ -1645,11 +1696,13 @@ if VALUE is not None: ...@@ -1645,11 +1696,13 @@ if VALUE is not None:
return None return None
def visit_CnameDecoratorNode(self, node): def visit_CnameDecoratorNode(self, node):
self.visitchildren(node) child_node = self.visit(node.node)
if not child_node:
if not node.node:
return None return None
if type(child_node) is list: # Assignment synthesized
node.child_node = child_node[0]
return [node] + child_node[1:]
node.node = child_node
return node return node
def create_Property(self, entry): def create_Property(self, entry):
...@@ -2065,9 +2118,6 @@ class CreateClosureClasses(CythonTransform): ...@@ -2065,9 +2118,6 @@ class CreateClosureClasses(CythonTransform):
return from_closure, in_closure return from_closure, in_closure
def create_class_from_scope(self, node, target_module_scope, inner_node=None): def create_class_from_scope(self, node, target_module_scope, inner_node=None):
# skip generator body
if node.is_generator_body:
return
# move local variables into closure # move local variables into closure
if node.is_generator: if node.is_generator:
for entry in node.local_scope.entries.values(): for entry in node.local_scope.entries.values():
...@@ -2160,6 +2210,10 @@ class CreateClosureClasses(CythonTransform): ...@@ -2160,6 +2210,10 @@ class CreateClosureClasses(CythonTransform):
self.path.pop() self.path.pop()
return node return node
def visit_GeneratorBodyDefNode(self, node):
self.visitchildren(node)
return node
def visit_CFuncDefNode(self, node): def visit_CFuncDefNode(self, node):
self.visitchildren(node) self.visitchildren(node)
return node return node
......
...@@ -137,6 +137,7 @@ def create_pipeline(context, mode, exclude_classes=()): ...@@ -137,6 +137,7 @@ def create_pipeline(context, mode, exclude_classes=()):
from AutoDocTransforms import EmbedSignature from AutoDocTransforms import EmbedSignature
from Optimize import FlattenInListTransform, SwitchTransform, IterationTransform from Optimize import FlattenInListTransform, SwitchTransform, IterationTransform
from Optimize import EarlyReplaceBuiltinCalls, OptimizeBuiltinCalls from Optimize import EarlyReplaceBuiltinCalls, OptimizeBuiltinCalls
from Optimize import InlineDefNodeCalls
from Optimize import ConstantFolding, FinalOptimizePhase from Optimize import ConstantFolding, FinalOptimizePhase
from Optimize import DropRefcountingTransform from Optimize import DropRefcountingTransform
from Buffer import IntroduceBufferAuxiliaryVars from Buffer import IntroduceBufferAuxiliaryVars
...@@ -185,6 +186,7 @@ def create_pipeline(context, mode, exclude_classes=()): ...@@ -185,6 +186,7 @@ def create_pipeline(context, mode, exclude_classes=()):
MarkOverflowingArithmetic(context), MarkOverflowingArithmetic(context),
IntroduceBufferAuxiliaryVars(context), IntroduceBufferAuxiliaryVars(context),
_check_c_declarations, _check_c_declarations,
InlineDefNodeCalls(context),
AnalyseExpressionsTransform(context), AnalyseExpressionsTransform(context),
FindInvalidUseOfFusedTypes(context), FindInvalidUseOfFusedTypes(context),
CreateClosureClasses(context), ## After all lookups and type inference CreateClosureClasses(context), ## After all lookups and type inference
......
...@@ -156,6 +156,7 @@ class Entry(object): ...@@ -156,6 +156,7 @@ class Entry(object):
from_closure = 0 from_closure = 0
is_declared_generic = 0 is_declared_generic = 0
is_readonly = 0 is_readonly = 0
pyfunc_cname = None
func_cname = None func_cname = None
func_modifiers = [] func_modifiers = []
final_func_cname = None final_func_cname = None
......
# cython: optimize.inline_defnode_calls=True
# mode: run
cimport cython
@cython.test_fail_if_path_exists('//SimpleCallNode')
@cython.test_assert_path_exists('//InlinedDefNodeCallNode')
def simple_noargs():
"""
>>> simple_noargs()
123
"""
def inner():
return 123
return inner()
@cython.test_fail_if_path_exists('//SimpleCallNode')
@cython.test_assert_path_exists('//InlinedDefNodeCallNode')
def test_coerce(a, int b):
"""
>>> test_coerce(2, 2)
4
"""
def inner(int a, b):
return a * b
return inner(a, b)
cdef class Foo(object):
def __repr__(self):
return '<Foo>'
@cython.test_fail_if_path_exists('//SimpleCallNode')
@cython.test_assert_path_exists('//InlinedDefNodeCallNode')
def test_func_signature(a):
"""
>>> test_func_signature(Foo())
<Foo>
"""
def inner(Foo a):
return a
return inner(a)
@cython.test_fail_if_path_exists('//SimpleCallNode')
@cython.test_assert_path_exists('//InlinedDefNodeCallNode')
def test_func_signature2(a, b):
"""
>>> test_func_signature2(Foo(), 123)
(<Foo>, 123)
"""
def inner(Foo a, b):
return a, b
return inner(a, b)
# Starred args and default values are not yet supported for inlining
@cython.test_assert_path_exists('//SimpleCallNode')
def test_defaults(a, b):
"""
>>> test_defaults(1, 2)
(1, 2, 123)
"""
def inner(a, b=b, c=123):
return a, b, c
return inner(a)
@cython.test_assert_path_exists('//SimpleCallNode')
def test_starred(a):
"""
>>> test_starred(123)
(123, (), {})
"""
def inner(a, *args, **kwargs):
return a, args, kwargs
return inner(a)
...@@ -406,10 +406,10 @@ cdef object some_float_value(): ...@@ -406,10 +406,10 @@ cdef object some_float_value():
return 2.0 return 2.0
@cython.test_fail_if_path_exists('//NameNode[@type.is_pyobject = True]')
@cython.test_assert_path_exists('//NameNode[@type.is_pyobject]',
'//NameNode[@type.is_pyobject = False]')
@infer_types(None) @infer_types(None)
@cython.test_fail_if_path_exists('//DefNode//NameNode[@type.is_pyobject = True]')
@cython.test_assert_path_exists('//DefNode//NameNode[@type.is_pyobject]',
'//DefNode//NameNode[@type.is_pyobject = False]')
def double_loop(): def double_loop():
""" """
>>> double_loop() == 1.0 * 10 >>> double_loop() == 1.0 * 10
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment