Add lookup_name() to optimize use of stack frames

The use of com_node() introduces a lot of extra stack frames, enough
to cause a stack overflow compiling test.test_parser with the standard
interpreter recursionlimit.  The com_node() is a convenience function
that hides the dispatch details, but comes at a very high cost.  It is
more efficient to dispatch directly in the callers.  In these cases,
use lookup_node() and call the dispatched node directly.

Also handle yield_stmt in a way that will work with Python 2.1
(suggested by Shane Hathaway)
This commit is contained in:
Jeremy Hylton 2001-08-27 21:02:51 +00:00
parent 058a5adad0
commit 7e30c9bb5a
2 changed files with 42 additions and 10 deletions

View File

@ -274,7 +274,8 @@ class Transformer:
def expr_stmt(self, nodelist): def expr_stmt(self, nodelist):
# augassign testlist | testlist ('=' testlist)* # augassign testlist | testlist ('=' testlist)*
exprNode = self.com_node(nodelist[-1]) en = nodelist[-1]
exprNode = self.lookup_node(en)(en[1:])
if len(nodelist) == 1: if len(nodelist) == 1:
n = Discard(exprNode) n = Discard(exprNode)
n.lineno = exprNode.lineno n.lineno = exprNode.lineno
@ -696,6 +697,17 @@ class Transformer:
# INTERNAL PARSING UTILITIES # INTERNAL PARSING UTILITIES
# #
# The use of com_node() introduces a lot of extra stack frames,
# enough to cause a stack overflow compiling test.test_parser with
# the standard interpreter recursionlimit. The com_node() is a
# convenience function that hides the dispatch details, but comes
# at a very high cost. It is more efficient to dispatch directly
# in the callers. In these cases, use lookup_node() and call the
# dispatched node directly.
def lookup_node(self, node):
return self._dispatch[node[0]]
def com_node(self, node): def com_node(self, node):
# Note: compile.c has handling in com_node for del_stmt, pass_stmt, # Note: compile.c has handling in com_node for del_stmt, pass_stmt,
# break_stmt, stmt, small_stmt, flow_stmt, simple_stmt, # break_stmt, stmt, small_stmt, flow_stmt, simple_stmt,
@ -938,14 +950,16 @@ class Transformer:
"Compile 'NODE (OP NODE)*' into (type, [ node1, ..., nodeN ])." "Compile 'NODE (OP NODE)*' into (type, [ node1, ..., nodeN ])."
l = len(nodelist) l = len(nodelist)
if l == 1: if l == 1:
return self.com_node(nodelist[0]) n = nodelist[0]
return self.lookup_node(n)(n[1:])
items = [] items = []
for i in range(0, l, 2): for i in range(0, l, 2):
items.append(self.com_node(nodelist[i])) n = nodelist[i]
items.append(self.lookup_node(n)(n[1:]))
return constructor(items) return constructor(items)
def com_stmt(self, node): def com_stmt(self, node):
result = self.com_node(node) result = self.lookup_node(node)(node[1:])
assert result is not None assert result is not None
if isinstance(result, Stmt): if isinstance(result, Stmt):
return result return result
@ -1254,7 +1268,6 @@ _legal_node_types = [
symbol.continue_stmt, symbol.continue_stmt,
symbol.return_stmt, symbol.return_stmt,
symbol.raise_stmt, symbol.raise_stmt,
symbol.yield_stmt,
symbol.import_stmt, symbol.import_stmt,
symbol.global_stmt, symbol.global_stmt,
symbol.exec_stmt, symbol.exec_stmt,
@ -1281,6 +1294,9 @@ _legal_node_types = [
symbol.atom, symbol.atom,
] ]
if hasattr(symbol, 'yield_stmt'):
_legal_node_types.append(symbol.yield_stmt)
_assign_types = [ _assign_types = [
symbol.test, symbol.test,
symbol.and_test, symbol.and_test,

View File

@ -274,7 +274,8 @@ class Transformer:
def expr_stmt(self, nodelist): def expr_stmt(self, nodelist):
# augassign testlist | testlist ('=' testlist)* # augassign testlist | testlist ('=' testlist)*
exprNode = self.com_node(nodelist[-1]) en = nodelist[-1]
exprNode = self.lookup_node(en)(en[1:])
if len(nodelist) == 1: if len(nodelist) == 1:
n = Discard(exprNode) n = Discard(exprNode)
n.lineno = exprNode.lineno n.lineno = exprNode.lineno
@ -696,6 +697,17 @@ class Transformer:
# INTERNAL PARSING UTILITIES # INTERNAL PARSING UTILITIES
# #
# The use of com_node() introduces a lot of extra stack frames,
# enough to cause a stack overflow compiling test.test_parser with
# the standard interpreter recursionlimit. The com_node() is a
# convenience function that hides the dispatch details, but comes
# at a very high cost. It is more efficient to dispatch directly
# in the callers. In these cases, use lookup_node() and call the
# dispatched node directly.
def lookup_node(self, node):
return self._dispatch[node[0]]
def com_node(self, node): def com_node(self, node):
# Note: compile.c has handling in com_node for del_stmt, pass_stmt, # Note: compile.c has handling in com_node for del_stmt, pass_stmt,
# break_stmt, stmt, small_stmt, flow_stmt, simple_stmt, # break_stmt, stmt, small_stmt, flow_stmt, simple_stmt,
@ -938,14 +950,16 @@ class Transformer:
"Compile 'NODE (OP NODE)*' into (type, [ node1, ..., nodeN ])." "Compile 'NODE (OP NODE)*' into (type, [ node1, ..., nodeN ])."
l = len(nodelist) l = len(nodelist)
if l == 1: if l == 1:
return self.com_node(nodelist[0]) n = nodelist[0]
return self.lookup_node(n)(n[1:])
items = [] items = []
for i in range(0, l, 2): for i in range(0, l, 2):
items.append(self.com_node(nodelist[i])) n = nodelist[i]
items.append(self.lookup_node(n)(n[1:]))
return constructor(items) return constructor(items)
def com_stmt(self, node): def com_stmt(self, node):
result = self.com_node(node) result = self.lookup_node(node)(node[1:])
assert result is not None assert result is not None
if isinstance(result, Stmt): if isinstance(result, Stmt):
return result return result
@ -1254,7 +1268,6 @@ _legal_node_types = [
symbol.continue_stmt, symbol.continue_stmt,
symbol.return_stmt, symbol.return_stmt,
symbol.raise_stmt, symbol.raise_stmt,
symbol.yield_stmt,
symbol.import_stmt, symbol.import_stmt,
symbol.global_stmt, symbol.global_stmt,
symbol.exec_stmt, symbol.exec_stmt,
@ -1281,6 +1294,9 @@ _legal_node_types = [
symbol.atom, symbol.atom,
] ]
if hasattr(symbol, 'yield_stmt'):
_legal_node_types.append(symbol.yield_stmt)
_assign_types = [ _assign_types = [
symbol.test, symbol.test,
symbol.and_test, symbol.and_test,