pyassem.py:
Fix annoying bugs in flow graph layout code. In some cases the implicit control transfers weren't honored. In other cases, JUMP_FORWARD instructions jumped backwards. Remove unused arg from nextBlock(). pycodegen.py Add optional force kwarg to set_lineno() that will emit a SET_LINENO even if it is the same as the previous lineno. Use explicit LOAD_FAST and STORE_FAST to access list comp implicit variables. (The symbol table doesn't know about them.)
This commit is contained in:
parent
35cf0a3f90
commit
542b11acfd
|
@ -27,11 +27,12 @@ class FlowGraph:
|
||||||
if self._debug:
|
if self._debug:
|
||||||
if self.current:
|
if self.current:
|
||||||
print "end", repr(self.current)
|
print "end", repr(self.current)
|
||||||
|
print " next", self.current.next
|
||||||
print " ", self.current.get_children()
|
print " ", self.current.get_children()
|
||||||
print repr(block)
|
print repr(block)
|
||||||
self.current = block
|
self.current = block
|
||||||
|
|
||||||
def nextBlock(self, block=None, force=0):
|
def nextBlock(self, block=None):
|
||||||
# XXX think we need to specify when there is implicit transfer
|
# XXX think we need to specify when there is implicit transfer
|
||||||
# from one block to the next. might be better to represent this
|
# from one block to the next. might be better to represent this
|
||||||
# with explicit JUMP_ABSOLUTE instructions that are optimized
|
# with explicit JUMP_ABSOLUTE instructions that are optimized
|
||||||
|
@ -95,12 +96,110 @@ class FlowGraph:
|
||||||
b.addNext(self.exit)
|
b.addNext(self.exit)
|
||||||
order = dfs_postorder(self.entry, {})
|
order = dfs_postorder(self.entry, {})
|
||||||
order.reverse()
|
order.reverse()
|
||||||
|
self.fixupOrder(order, self.exit)
|
||||||
# hack alert
|
# hack alert
|
||||||
if not self.exit in order:
|
if not self.exit in order:
|
||||||
order.append(self.exit)
|
order.append(self.exit)
|
||||||
|
|
||||||
return order
|
return order
|
||||||
|
|
||||||
|
def fixupOrder(self, blocks, default_next):
|
||||||
|
"""Fixup bad order introduced by DFS."""
|
||||||
|
|
||||||
|
# XXX This is a total mess. There must be a better way to get
|
||||||
|
# the code blocks in the right order.
|
||||||
|
|
||||||
|
self.fixupOrderHonorNext(blocks, default_next)
|
||||||
|
self.fixupOrderForward(blocks, default_next)
|
||||||
|
|
||||||
|
def fixupOrderHonorNext(self, blocks, default_next):
|
||||||
|
"""Fix one problem with DFS.
|
||||||
|
|
||||||
|
The DFS uses child block, but doesn't know about the special
|
||||||
|
"next" block. As a result, the DFS can order blocks so that a
|
||||||
|
block isn't next to the right block for implicit control
|
||||||
|
transfers.
|
||||||
|
"""
|
||||||
|
index = {}
|
||||||
|
for i in range(len(blocks)):
|
||||||
|
index[blocks[i]] = i
|
||||||
|
|
||||||
|
for i in range(0, len(blocks) - 1):
|
||||||
|
b = blocks[i]
|
||||||
|
n = blocks[i + 1]
|
||||||
|
if not b.next or b.next[0] == default_next or b.next[0] == n:
|
||||||
|
continue
|
||||||
|
# The blocks are in the wrong order. Find the chain of
|
||||||
|
# blocks to insert where they belong.
|
||||||
|
cur = b
|
||||||
|
chain = []
|
||||||
|
elt = cur
|
||||||
|
while elt.next and elt.next[0] != default_next:
|
||||||
|
chain.append(elt.next[0])
|
||||||
|
elt = elt.next[0]
|
||||||
|
# Now remove the blocks in the chain from the current
|
||||||
|
# block list, so that they can be re-inserted.
|
||||||
|
l = []
|
||||||
|
for b in chain:
|
||||||
|
assert index[b] > i
|
||||||
|
l.append((index[b], b))
|
||||||
|
l.sort()
|
||||||
|
l.reverse()
|
||||||
|
for j, b in l:
|
||||||
|
del blocks[index[b]]
|
||||||
|
# Insert the chain in the proper location
|
||||||
|
blocks[i:i + 1] = [cur] + chain
|
||||||
|
# Finally, re-compute the block indexes
|
||||||
|
for i in range(len(blocks)):
|
||||||
|
index[blocks[i]] = i
|
||||||
|
|
||||||
|
def fixupOrderForward(self, blocks, default_next):
|
||||||
|
"""Make sure all JUMP_FORWARDs jump forward"""
|
||||||
|
index = {}
|
||||||
|
chains = []
|
||||||
|
cur = []
|
||||||
|
for b in blocks:
|
||||||
|
index[b] = len(chains)
|
||||||
|
cur.append(b)
|
||||||
|
if b.next and b.next[0] == default_next:
|
||||||
|
chains.append(cur)
|
||||||
|
cur = []
|
||||||
|
chains.append(cur)
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
constraints = []
|
||||||
|
|
||||||
|
for i in range(len(chains)):
|
||||||
|
l = chains[i]
|
||||||
|
for b in l:
|
||||||
|
for c in b.get_children():
|
||||||
|
if index[c] < i:
|
||||||
|
forward_p = 0
|
||||||
|
for inst in b.insts:
|
||||||
|
if inst[0] == 'JUMP_FORWARD':
|
||||||
|
if inst[1] == c:
|
||||||
|
forward_p = 1
|
||||||
|
if not forward_p:
|
||||||
|
continue
|
||||||
|
constraints.append((index[c], i))
|
||||||
|
|
||||||
|
if not constraints:
|
||||||
|
break
|
||||||
|
|
||||||
|
# XXX just do one for now
|
||||||
|
# do swaps to get things in the right order
|
||||||
|
goes_before, a_chain = constraints[0]
|
||||||
|
assert a_chain > goes_before
|
||||||
|
c = chains[a_chain]
|
||||||
|
chains.remove(c)
|
||||||
|
chains.insert(goes_before, c)
|
||||||
|
|
||||||
|
|
||||||
|
del blocks[:]
|
||||||
|
for c in chains:
|
||||||
|
for b in c:
|
||||||
|
blocks.append(b)
|
||||||
|
|
||||||
def getBlocks(self):
|
def getBlocks(self):
|
||||||
return self.blocks.elements()
|
return self.blocks.elements()
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ class CodeGenerator:
|
||||||
else:
|
else:
|
||||||
self.emit(prefix + '_GLOBAL', name)
|
self.emit(prefix + '_GLOBAL', name)
|
||||||
|
|
||||||
def set_lineno(self, node):
|
def set_lineno(self, node, force=0):
|
||||||
"""Emit SET_LINENO if node has lineno attribute and it is
|
"""Emit SET_LINENO if node has lineno attribute and it is
|
||||||
different than the last lineno emitted.
|
different than the last lineno emitted.
|
||||||
|
|
||||||
|
@ -205,7 +205,8 @@ class CodeGenerator:
|
||||||
then, this method works around missing line numbers.
|
then, this method works around missing line numbers.
|
||||||
"""
|
"""
|
||||||
lineno = getattr(node, 'lineno', None)
|
lineno = getattr(node, 'lineno', None)
|
||||||
if lineno is not None and lineno != self.last_lineno:
|
if lineno is not None and (lineno != self.last_lineno
|
||||||
|
or force):
|
||||||
self.emit('SET_LINENO', lineno)
|
self.emit('SET_LINENO', lineno)
|
||||||
self.last_lineno = lineno
|
self.last_lineno = lineno
|
||||||
return 1
|
return 1
|
||||||
|
@ -413,9 +414,6 @@ class CodeGenerator:
|
||||||
__list_count = 0
|
__list_count = 0
|
||||||
|
|
||||||
def visitListComp(self, node):
|
def visitListComp(self, node):
|
||||||
# XXX would it be easier to transform the AST into the form it
|
|
||||||
# would have if the list comp were expressed as a series of
|
|
||||||
# for and if stmts and an explicit append?
|
|
||||||
self.set_lineno(node)
|
self.set_lineno(node)
|
||||||
# setup list
|
# setup list
|
||||||
append = "$append%d" % self.__list_count
|
append = "$append%d" % self.__list_count
|
||||||
|
@ -423,10 +421,10 @@ class CodeGenerator:
|
||||||
self.emit('BUILD_LIST', 0)
|
self.emit('BUILD_LIST', 0)
|
||||||
self.emit('DUP_TOP')
|
self.emit('DUP_TOP')
|
||||||
self.emit('LOAD_ATTR', 'append')
|
self.emit('LOAD_ATTR', 'append')
|
||||||
self.storeName(append)
|
self.emit('STORE_FAST', append)
|
||||||
l = len(node.quals)
|
|
||||||
stack = []
|
stack = []
|
||||||
for i, for_ in zip(range(l), node.quals):
|
for i, for_ in zip(range(len(node.quals)), node.quals):
|
||||||
start, anchor = self.visit(for_)
|
start, anchor = self.visit(for_)
|
||||||
cont = None
|
cont = None
|
||||||
for if_ in for_.ifs:
|
for if_ in for_.ifs:
|
||||||
|
@ -434,8 +432,8 @@ class CodeGenerator:
|
||||||
cont = self.newBlock()
|
cont = self.newBlock()
|
||||||
self.visit(if_, cont)
|
self.visit(if_, cont)
|
||||||
stack.insert(0, (start, cont, anchor))
|
stack.insert(0, (start, cont, anchor))
|
||||||
|
|
||||||
self.loadName(append)
|
self.emit('LOAD_FAST', append)
|
||||||
self.visit(node.expr)
|
self.visit(node.expr)
|
||||||
self.emit('CALL_FUNCTION', 1)
|
self.emit('CALL_FUNCTION', 1)
|
||||||
self.emit('POP_TOP')
|
self.emit('POP_TOP')
|
||||||
|
@ -449,12 +447,11 @@ class CodeGenerator:
|
||||||
self.nextBlock(skip_one)
|
self.nextBlock(skip_one)
|
||||||
self.emit('JUMP_ABSOLUTE', start)
|
self.emit('JUMP_ABSOLUTE', start)
|
||||||
self.startBlock(anchor)
|
self.startBlock(anchor)
|
||||||
self.delName(append)
|
self.emit('DELETE_FAST', append)
|
||||||
|
|
||||||
self.__list_count = self.__list_count - 1
|
self.__list_count = self.__list_count - 1
|
||||||
|
|
||||||
def visitListCompFor(self, node):
|
def visitListCompFor(self, node):
|
||||||
self.set_lineno(node)
|
|
||||||
start = self.newBlock()
|
start = self.newBlock()
|
||||||
anchor = self.newBlock()
|
anchor = self.newBlock()
|
||||||
|
|
||||||
|
@ -468,7 +465,7 @@ class CodeGenerator:
|
||||||
return start, anchor
|
return start, anchor
|
||||||
|
|
||||||
def visitListCompIf(self, node, branch):
|
def visitListCompIf(self, node, branch):
|
||||||
self.set_lineno(node)
|
self.set_lineno(node, force=1)
|
||||||
self.visit(node.test)
|
self.visit(node.test)
|
||||||
self.emit('JUMP_IF_FALSE', branch)
|
self.emit('JUMP_IF_FALSE', branch)
|
||||||
self.newBlock()
|
self.newBlock()
|
||||||
|
@ -672,6 +669,7 @@ class CodeGenerator:
|
||||||
print node
|
print node
|
||||||
|
|
||||||
def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'):
|
def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'):
|
||||||
|
## print >> sys.stderr, "AssSequence", op, findOp(node), node
|
||||||
if findOp(node) != 'OP_DELETE':
|
if findOp(node) != 'OP_DELETE':
|
||||||
self.emit(op, len(node.nodes))
|
self.emit(op, len(node.nodes))
|
||||||
for child in node.nodes:
|
for child in node.nodes:
|
||||||
|
|
|
@ -27,11 +27,12 @@ class FlowGraph:
|
||||||
if self._debug:
|
if self._debug:
|
||||||
if self.current:
|
if self.current:
|
||||||
print "end", repr(self.current)
|
print "end", repr(self.current)
|
||||||
|
print " next", self.current.next
|
||||||
print " ", self.current.get_children()
|
print " ", self.current.get_children()
|
||||||
print repr(block)
|
print repr(block)
|
||||||
self.current = block
|
self.current = block
|
||||||
|
|
||||||
def nextBlock(self, block=None, force=0):
|
def nextBlock(self, block=None):
|
||||||
# XXX think we need to specify when there is implicit transfer
|
# XXX think we need to specify when there is implicit transfer
|
||||||
# from one block to the next. might be better to represent this
|
# from one block to the next. might be better to represent this
|
||||||
# with explicit JUMP_ABSOLUTE instructions that are optimized
|
# with explicit JUMP_ABSOLUTE instructions that are optimized
|
||||||
|
@ -95,12 +96,110 @@ class FlowGraph:
|
||||||
b.addNext(self.exit)
|
b.addNext(self.exit)
|
||||||
order = dfs_postorder(self.entry, {})
|
order = dfs_postorder(self.entry, {})
|
||||||
order.reverse()
|
order.reverse()
|
||||||
|
self.fixupOrder(order, self.exit)
|
||||||
# hack alert
|
# hack alert
|
||||||
if not self.exit in order:
|
if not self.exit in order:
|
||||||
order.append(self.exit)
|
order.append(self.exit)
|
||||||
|
|
||||||
return order
|
return order
|
||||||
|
|
||||||
|
def fixupOrder(self, blocks, default_next):
|
||||||
|
"""Fixup bad order introduced by DFS."""
|
||||||
|
|
||||||
|
# XXX This is a total mess. There must be a better way to get
|
||||||
|
# the code blocks in the right order.
|
||||||
|
|
||||||
|
self.fixupOrderHonorNext(blocks, default_next)
|
||||||
|
self.fixupOrderForward(blocks, default_next)
|
||||||
|
|
||||||
|
def fixupOrderHonorNext(self, blocks, default_next):
|
||||||
|
"""Fix one problem with DFS.
|
||||||
|
|
||||||
|
The DFS uses child block, but doesn't know about the special
|
||||||
|
"next" block. As a result, the DFS can order blocks so that a
|
||||||
|
block isn't next to the right block for implicit control
|
||||||
|
transfers.
|
||||||
|
"""
|
||||||
|
index = {}
|
||||||
|
for i in range(len(blocks)):
|
||||||
|
index[blocks[i]] = i
|
||||||
|
|
||||||
|
for i in range(0, len(blocks) - 1):
|
||||||
|
b = blocks[i]
|
||||||
|
n = blocks[i + 1]
|
||||||
|
if not b.next or b.next[0] == default_next or b.next[0] == n:
|
||||||
|
continue
|
||||||
|
# The blocks are in the wrong order. Find the chain of
|
||||||
|
# blocks to insert where they belong.
|
||||||
|
cur = b
|
||||||
|
chain = []
|
||||||
|
elt = cur
|
||||||
|
while elt.next and elt.next[0] != default_next:
|
||||||
|
chain.append(elt.next[0])
|
||||||
|
elt = elt.next[0]
|
||||||
|
# Now remove the blocks in the chain from the current
|
||||||
|
# block list, so that they can be re-inserted.
|
||||||
|
l = []
|
||||||
|
for b in chain:
|
||||||
|
assert index[b] > i
|
||||||
|
l.append((index[b], b))
|
||||||
|
l.sort()
|
||||||
|
l.reverse()
|
||||||
|
for j, b in l:
|
||||||
|
del blocks[index[b]]
|
||||||
|
# Insert the chain in the proper location
|
||||||
|
blocks[i:i + 1] = [cur] + chain
|
||||||
|
# Finally, re-compute the block indexes
|
||||||
|
for i in range(len(blocks)):
|
||||||
|
index[blocks[i]] = i
|
||||||
|
|
||||||
|
def fixupOrderForward(self, blocks, default_next):
|
||||||
|
"""Make sure all JUMP_FORWARDs jump forward"""
|
||||||
|
index = {}
|
||||||
|
chains = []
|
||||||
|
cur = []
|
||||||
|
for b in blocks:
|
||||||
|
index[b] = len(chains)
|
||||||
|
cur.append(b)
|
||||||
|
if b.next and b.next[0] == default_next:
|
||||||
|
chains.append(cur)
|
||||||
|
cur = []
|
||||||
|
chains.append(cur)
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
constraints = []
|
||||||
|
|
||||||
|
for i in range(len(chains)):
|
||||||
|
l = chains[i]
|
||||||
|
for b in l:
|
||||||
|
for c in b.get_children():
|
||||||
|
if index[c] < i:
|
||||||
|
forward_p = 0
|
||||||
|
for inst in b.insts:
|
||||||
|
if inst[0] == 'JUMP_FORWARD':
|
||||||
|
if inst[1] == c:
|
||||||
|
forward_p = 1
|
||||||
|
if not forward_p:
|
||||||
|
continue
|
||||||
|
constraints.append((index[c], i))
|
||||||
|
|
||||||
|
if not constraints:
|
||||||
|
break
|
||||||
|
|
||||||
|
# XXX just do one for now
|
||||||
|
# do swaps to get things in the right order
|
||||||
|
goes_before, a_chain = constraints[0]
|
||||||
|
assert a_chain > goes_before
|
||||||
|
c = chains[a_chain]
|
||||||
|
chains.remove(c)
|
||||||
|
chains.insert(goes_before, c)
|
||||||
|
|
||||||
|
|
||||||
|
del blocks[:]
|
||||||
|
for c in chains:
|
||||||
|
for b in c:
|
||||||
|
blocks.append(b)
|
||||||
|
|
||||||
def getBlocks(self):
|
def getBlocks(self):
|
||||||
return self.blocks.elements()
|
return self.blocks.elements()
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ class CodeGenerator:
|
||||||
else:
|
else:
|
||||||
self.emit(prefix + '_GLOBAL', name)
|
self.emit(prefix + '_GLOBAL', name)
|
||||||
|
|
||||||
def set_lineno(self, node):
|
def set_lineno(self, node, force=0):
|
||||||
"""Emit SET_LINENO if node has lineno attribute and it is
|
"""Emit SET_LINENO if node has lineno attribute and it is
|
||||||
different than the last lineno emitted.
|
different than the last lineno emitted.
|
||||||
|
|
||||||
|
@ -205,7 +205,8 @@ class CodeGenerator:
|
||||||
then, this method works around missing line numbers.
|
then, this method works around missing line numbers.
|
||||||
"""
|
"""
|
||||||
lineno = getattr(node, 'lineno', None)
|
lineno = getattr(node, 'lineno', None)
|
||||||
if lineno is not None and lineno != self.last_lineno:
|
if lineno is not None and (lineno != self.last_lineno
|
||||||
|
or force):
|
||||||
self.emit('SET_LINENO', lineno)
|
self.emit('SET_LINENO', lineno)
|
||||||
self.last_lineno = lineno
|
self.last_lineno = lineno
|
||||||
return 1
|
return 1
|
||||||
|
@ -413,9 +414,6 @@ class CodeGenerator:
|
||||||
__list_count = 0
|
__list_count = 0
|
||||||
|
|
||||||
def visitListComp(self, node):
|
def visitListComp(self, node):
|
||||||
# XXX would it be easier to transform the AST into the form it
|
|
||||||
# would have if the list comp were expressed as a series of
|
|
||||||
# for and if stmts and an explicit append?
|
|
||||||
self.set_lineno(node)
|
self.set_lineno(node)
|
||||||
# setup list
|
# setup list
|
||||||
append = "$append%d" % self.__list_count
|
append = "$append%d" % self.__list_count
|
||||||
|
@ -423,10 +421,10 @@ class CodeGenerator:
|
||||||
self.emit('BUILD_LIST', 0)
|
self.emit('BUILD_LIST', 0)
|
||||||
self.emit('DUP_TOP')
|
self.emit('DUP_TOP')
|
||||||
self.emit('LOAD_ATTR', 'append')
|
self.emit('LOAD_ATTR', 'append')
|
||||||
self.storeName(append)
|
self.emit('STORE_FAST', append)
|
||||||
l = len(node.quals)
|
|
||||||
stack = []
|
stack = []
|
||||||
for i, for_ in zip(range(l), node.quals):
|
for i, for_ in zip(range(len(node.quals)), node.quals):
|
||||||
start, anchor = self.visit(for_)
|
start, anchor = self.visit(for_)
|
||||||
cont = None
|
cont = None
|
||||||
for if_ in for_.ifs:
|
for if_ in for_.ifs:
|
||||||
|
@ -434,8 +432,8 @@ class CodeGenerator:
|
||||||
cont = self.newBlock()
|
cont = self.newBlock()
|
||||||
self.visit(if_, cont)
|
self.visit(if_, cont)
|
||||||
stack.insert(0, (start, cont, anchor))
|
stack.insert(0, (start, cont, anchor))
|
||||||
|
|
||||||
self.loadName(append)
|
self.emit('LOAD_FAST', append)
|
||||||
self.visit(node.expr)
|
self.visit(node.expr)
|
||||||
self.emit('CALL_FUNCTION', 1)
|
self.emit('CALL_FUNCTION', 1)
|
||||||
self.emit('POP_TOP')
|
self.emit('POP_TOP')
|
||||||
|
@ -449,12 +447,11 @@ class CodeGenerator:
|
||||||
self.nextBlock(skip_one)
|
self.nextBlock(skip_one)
|
||||||
self.emit('JUMP_ABSOLUTE', start)
|
self.emit('JUMP_ABSOLUTE', start)
|
||||||
self.startBlock(anchor)
|
self.startBlock(anchor)
|
||||||
self.delName(append)
|
self.emit('DELETE_FAST', append)
|
||||||
|
|
||||||
self.__list_count = self.__list_count - 1
|
self.__list_count = self.__list_count - 1
|
||||||
|
|
||||||
def visitListCompFor(self, node):
|
def visitListCompFor(self, node):
|
||||||
self.set_lineno(node)
|
|
||||||
start = self.newBlock()
|
start = self.newBlock()
|
||||||
anchor = self.newBlock()
|
anchor = self.newBlock()
|
||||||
|
|
||||||
|
@ -468,7 +465,7 @@ class CodeGenerator:
|
||||||
return start, anchor
|
return start, anchor
|
||||||
|
|
||||||
def visitListCompIf(self, node, branch):
|
def visitListCompIf(self, node, branch):
|
||||||
self.set_lineno(node)
|
self.set_lineno(node, force=1)
|
||||||
self.visit(node.test)
|
self.visit(node.test)
|
||||||
self.emit('JUMP_IF_FALSE', branch)
|
self.emit('JUMP_IF_FALSE', branch)
|
||||||
self.newBlock()
|
self.newBlock()
|
||||||
|
@ -672,6 +669,7 @@ class CodeGenerator:
|
||||||
print node
|
print node
|
||||||
|
|
||||||
def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'):
|
def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'):
|
||||||
|
## print >> sys.stderr, "AssSequence", op, findOp(node), node
|
||||||
if findOp(node) != 'OP_DELETE':
|
if findOp(node) != 'OP_DELETE':
|
||||||
self.emit(op, len(node.nodes))
|
self.emit(op, len(node.nodes))
|
||||||
for child in node.nodes:
|
for child in node.nodes:
|
||||||
|
|
Loading…
Reference in New Issue