Commit 7e8f9a3b authored by Stefan Behnel's avatar Stefan Behnel

Move the branch-hint generation from Nodes.py into Optimize.py to make it...

Move the branch-hint generation from Nodes.py into Optimize.py to make it available to tree assertion tests (and add such a test).
parent 792d80ba
......@@ -6559,13 +6559,9 @@ class IfStatNode(StatNode):
code.mark_pos(self.pos)
end_label = code.new_label()
last = len(self.if_clauses)
if self.else_clause:
# If the 'else' clause is 'unlikely', then set the preceding 'if' clause to 'likely' to reflect that.
self._set_branch_hint(self.if_clauses[-1], self.else_clause, inverse=True)
else:
if not self.else_clause:
last -= 1 # avoid redundant goto at end of last if-clause
for i, if_clause in enumerate(self.if_clauses):
self._set_branch_hint(if_clause, if_clause.body)
if_clause.generate_execution_code(code, end_label, is_last=i == last)
if self.else_clause:
code.mark_pos(self.else_clause.pos)
......@@ -6574,25 +6570,6 @@ class IfStatNode(StatNode):
code.putln("}")
code.put_label(end_label)
def _set_branch_hint(self, clause, statements_node, inverse=False):
if not statements_node.is_terminator:
return
if isinstance(statements_node, StatListNode):
if not statements_node.stats:
return
statements = statements_node.stats
else:
statements = [statements_node]
# Anything that unconditionally raises exceptions should be considered unlikely.
if isinstance(statements[-1], (RaiseStatNode, ReraiseStatNode)):
if len(statements) > 1:
# Allow simple statements before the 'raise', but no conditions, loops, etc.
non_branch_nodes = (ExprStatNode, AssignmentNode, DelStatNode, GlobalNode, NonlocalNode)
for node in statements[:-1]:
if not isinstance(node, non_branch_nodes):
return
clause.branch_hint = 'likely' if inverse else 'unlikely'
def generate_function_definitions(self, env, code):
for clause in self.if_clauses:
clause.generate_function_definitions(env, code)
......
......@@ -4779,6 +4779,7 @@ class FinalOptimizePhase(Visitor.EnvTransform, Visitor.NodeRefCleanupMixin):
- isinstance -> typecheck for cdef types
- eliminate checks for None and/or types that became redundant after tree changes
- eliminate useless string formatting steps
- inject branch hints for unlikely if-cases that only raise exceptions
- replace Python function calls that look like method calls by a faster PyMethodCallNode
"""
in_loop = False
......@@ -4877,6 +4878,45 @@ class FinalOptimizePhase(Visitor.EnvTransform, Visitor.NodeRefCleanupMixin):
self.in_loop = old_val
return node
def visit_IfStatNode(self, node):
"""Assign 'unlikely' branch hints to if-clauses that only raise exceptions.
"""
self.visitchildren(node)
last_non_unlikely_clause = None
for i, if_clause in enumerate(node.if_clauses):
self._set_ifclause_branch_hint(if_clause, if_clause.body)
if not if_clause.branch_hint:
last_non_unlikely_clause = if_clause
if node.else_clause and last_non_unlikely_clause:
# If the 'else' clause is 'unlikely', then set the preceding 'if' clause to 'likely' to reflect that.
self._set_ifclause_branch_hint(last_non_unlikely_clause, node.else_clause, inverse=True)
return node
def _set_ifclause_branch_hint(self, clause, statements_node, inverse=False):
if not statements_node.is_terminator:
return
if isinstance(statements_node, Nodes.StatListNode):
if not statements_node.stats:
return
statements = statements_node.stats
else:
statements = [statements_node]
# Anything that unconditionally raises exceptions should be considered unlikely.
if isinstance(statements[-1], (Nodes.RaiseStatNode, Nodes.ReraiseStatNode)):
if len(statements) > 1:
# Allow simple statements before the 'raise', but no conditions, loops, etc.
non_branch_nodes = (
Nodes.ExprStatNode,
Nodes.AssignmentNode,
Nodes.DelStatNode,
Nodes.GlobalNode,
Nodes.NonlocalNode,
)
for node in statements[:-1]:
if not isinstance(node, non_branch_nodes):
return
clause.branch_hint = 'likely' if inverse else 'unlikely'
class ConsolidateOverflowCheck(Visitor.CythonTransform):
"""
......
# mode: compile
# tag: if, unlikely
cimport cython
@cython.test_assert_path_exists(
"//IfClauseNode",
"//IfClauseNode[not(@branch_hint)]",
)
def if_simple(x):
if x:
x = 2
@cython.test_assert_path_exists(
"//IfClauseNode",
"//IfClauseNode[not(@branch_hint)]",
)
def if_return(x):
if x:
return 1
raise TypeError()
@cython.test_assert_path_exists(
"//IfClauseNode",
"//IfClauseNode[@branch_hint = 'unlikely']",
)
def if_raise_else(x):
if x:
raise TypeError()
else:
return 1
@cython.test_assert_path_exists(
"//IfClauseNode",
"//IfClauseNode[@branch_hint = 'likely']",
)
def if_else_raise(x):
if x:
return 1
else:
raise TypeError()
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