Commit 27403cbf authored by Stefan Behnel's avatar Stefan Behnel

simple optimisation that statically maps keyword arguments to positional...

simple optimisation that statically maps keyword arguments to positional arguments when called signature is known
parent ec25d3bc
......@@ -1173,6 +1173,63 @@ class DropRefcountingTransform(Visitor.VisitorTransform):
return (base.name, index_val)
class SimplifyCalls(Visitor.EnvTransform):
"""
Statically map keyword arguments in C calls to positional arguments.
"""
def visit_GeneralCallNode(self, node):
self.visitchildren(node)
arg_tuple = node.positional_args
if not isinstance(arg_tuple, ExprNodes.TupleNode):
return node
args = arg_tuple.args
kwargs = node.keyword_args
function = node.function
entry = function.entry
if not entry:
if not function.is_name:
# TODO: optimise methods as well
return node
entry = self.current_env().lookup(function.name)
if not entry:
return node
if not entry.is_cfunction or not entry.type:
return node
declared_args = entry.type.args
if len(declared_args) < len(args):
# will lead to an error elsewhere
return node
matched_pos_args = set([arg.name for arg in declared_args[:len(args)]])
unmatched_args = declared_args[len(args):]
matched_kwargs = set()
args = list(args)
# TODO: match keywords out-of-order and move values
# into ordered temps if necessary
for decl_arg, arg in zip(unmatched_args, kwargs.key_value_pairs):
name = arg.key.value
if name in matched_pos_args:
# keyword argument passed twice => should fail elsewhere
return node
if decl_arg.name == name:
matched_kwargs.add(name)
args.append(arg.value)
else:
break
if not matched_kwargs:
return node
if len(kwargs.key_value_pairs) == len(matched_kwargs):
return ExprNodes.SimpleCallNode(
node.pos,
function=function, args=args)
arg_tuple.args = args
kwargs.key_value_pairs = [
item for item in kwargs.key_value_pairs
if item.key.value not in matched_kwargs ]
return node
class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
"""Optimize some common calls to builtin types *before* the type
analysis phase and *after* the declarations analysis phase.
......
......@@ -141,7 +141,7 @@ def create_pipeline(context, mode, exclude_classes=()):
from AutoDocTransforms import EmbedSignature
from Optimize import FlattenInListTransform, SwitchTransform, IterationTransform
from Optimize import EarlyReplaceBuiltinCalls, OptimizeBuiltinCalls
from Optimize import InlineDefNodeCalls
from Optimize import InlineDefNodeCalls, SimplifyCalls
from Optimize import ConstantFolding, FinalOptimizePhase
from Optimize import DropRefcountingTransform
from Optimize import ConsolidateOverflowCheck
......@@ -182,6 +182,7 @@ def create_pipeline(context, mode, exclude_classes=()):
AnalyseDeclarationsTransform(context),
AutoTestDictTransform(context),
EmbedSignature(context),
SimplifyCalls(context),
EarlyReplaceBuiltinCalls(context), ## Necessary?
TransformBuiltinMethods(context), ## Necessary?
MarkParallelAssignments(context),
......
cimport cython
cdef cfunc(a,b,c,d):
return (a,b,c,d)
cpdef cpfunc(a,b,c,d):
return (a,b,c,d)
sideeffect = []
cdef side_effect(x):
sideeffect.append(x)
return x
@cython.test_fail_if_path_exists('//GeneralCallNode')
@cython.test_assert_path_exists('//SimpleCallNode')
def cfunc_all_keywords():
"""
>>> cfunc_all_keywords()
(1, 2, 3, 4)
"""
return cfunc(a=1, b=2, c=3, d=4)
@cython.test_fail_if_path_exists('//GeneralCallNode')
@cython.test_assert_path_exists('//SimpleCallNode')
def cfunc_some_keywords():
"""
>>> cfunc_some_keywords()
(1, 2, 3, 4)
"""
return cfunc(1, 2, c=3, d=4)
'''
@cython.test_fail_if_path_exists('//GeneralCallNode')
@cython.test_assert_path_exists('//SimpleCallNode')
def cfunc_some_keywords_unordered():
"""
>>> cfunc_some_keywords_unordered()
(1, 2, 3, 4)
"""
return cfunc(1, 2, d=4, d=3)
@cython.test_fail_if_path_exists('//GeneralCallNode')
@cython.test_assert_path_exists('//SimpleCallNode')
def cfunc_some_keywords_unordered_sideeffect():
"""
>>> del sideeffect[:]
>>> cfunc_some_keywords_unordered_sideeffect()
(1, 2, 3, 4)
>>> sideeffect
[4, 3]
"""
return cfunc(1, 2, d=side_effect(4), d=side_effect(3))
'''
@cython.test_fail_if_path_exists('//GeneralCallNode')
@cython.test_assert_path_exists('//SimpleCallNode')
def cpfunc_all_keywords():
"""
>>> cpfunc_all_keywords()
(1, 2, 3, 4)
"""
return cpfunc(a=1, b=2, c=3, d=4)
@cython.test_fail_if_path_exists('//GeneralCallNode')
@cython.test_assert_path_exists('//SimpleCallNode')
def cpfunc_some_keywords():
"""
>>> cpfunc_some_keywords()
(1, 2, 3, 4)
"""
return cpfunc(1, 2, c=3, d=4)
#@cython.test_fail_if_path_exists('//GeneralCallNode')
#@cython.test_assert_path_exists('//SimpleCallNode')
def cpfunc_some_keywords_unordered():
"""
>>> cpfunc_some_keywords_unordered()
(1, 2, 3, 4)
"""
return cpfunc(1, 2, d=4, c=3)
#@cython.test_fail_if_path_exists('//GeneralCallNode')
#@cython.test_assert_path_exists('//SimpleCallNode')
def cpfunc_some_keywords_unordered_sideeffect():
"""
>>> del sideeffect[:]
>>> cpfunc_some_keywords_unordered_sideeffect()
(1, 2, 3, 4)
>>> sideeffect
[4, 3]
"""
return cpfunc(1, 2, d=side_effect(4), c=side_effect(3))
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