Permalink
Browse files

pyassem: Move functions to prepare for refactoring.

  • Loading branch information...
Andy Chu
Andy Chu committed Mar 20, 2018
1 parent 9a867fe commit c2c125c305a5013d6a0d08ab6458302291a2de99
Showing with 172 additions and 165 deletions.
  1. +172 −165 opy/compiler2/pyassem.py
View
@@ -9,159 +9,9 @@
HAS_JREL = set(dis.opname[op] for op in dis.hasjrel)
HAS_JABS = set(dis.opname[op] for op in dis.hasjabs)
OPNUM = dict((name, i) for i, name in enumerate(dis.opname))
def _NameToIndex(name, L):
"""Return index of name in list, appending if necessary
This routine uses a list instead of a dictionary, because a
dictionary can't store two different keys if the keys have the
same value but different types, e.g. 2 and 2L. The compiler
must treat these two separately, so it does an explicit type
comparison before comparing the values.
"""
t = type(name)
for i, item in enumerate(L):
if t == type(item) and item == name:
return i
end = len(L)
L.append(name)
return end
def ComputeStackDepth(blocks, entry_block, exit_block):
"""Compute the max stack depth.
Approach is to compute the stack effect of each basic block.
Then find the path through the code with the largest total
effect.
"""
depth = {}
exit = None
for b in blocks:
depth[b] = TRACKER.findDepth(b.getInstructions())
seen = {}
def max_depth(b, d):
if b in seen:
return d
seen[b] = 1
d = d + depth[b]
children = b.get_children()
if children:
return max([max_depth(c, d) for c in children])
else:
if not b.label == "exit":
return max_depth(exit_block, d)
else:
return d
return max_depth(entry_block, 0)
def FlattenGraph(blocks):
insts = []
pc = 0
begin = {}
end = {}
for b in blocks:
begin[b] = pc
for inst in b.getInstructions():
insts.append(inst)
if len(inst) == 1:
pc = pc + 1
elif inst[0] != "SET_LINENO":
# arg takes 2 bytes
pc = pc + 3
end[b] = pc
pc = 0
for i, inst in enumerate(insts):
if len(inst) == 1:
pc = pc + 1
elif inst[0] != "SET_LINENO":
pc = pc + 3
opname = inst[0]
if opname in HAS_JREL:
oparg = inst[1]
offset = begin[oparg] - pc
insts[i] = opname, offset
elif opname in HAS_JABS:
insts[i] = opname, begin[inst[1]]
return insts
class FlowGraph(object):
def __init__(self):
self.current = self.entry = Block()
self.exit = Block("exit")
self.blocks = set()
self.blocks.add(self.entry)
self.blocks.add(self.exit)
DEBUG = False
def startBlock(self, block):
if self.DEBUG:
if self.current:
print("end", repr(self.current))
print(" next", self.current.next)
print(" prev", self.current.prev)
print(" ", self.current.get_children())
print(repr(block))
self.current = block
def nextBlock(self, block=None):
# XXX think we need to specify when there is implicit transfer
# from one block to the next. might be better to represent this
# with explicit JUMP_ABSOLUTE instructions that are optimized
# out when they are unnecessary.
#
# I think this strategy works: each block has a child
# designated as "next" which is returned as the last of the
# children. because the nodes in a graph are emitted in
# reverse post order, the "next" block will always be emitted
# immediately after its parent.
# Worry: maintaining this invariant could be tricky
if block is None:
block = self.newBlock()
# Note: If the current block ends with an unconditional control
# transfer, then it is techically incorrect to add an implicit
# transfer to the block graph. Doing so results in code generation
# for unreachable blocks. That doesn't appear to be very common
# with Python code and since the built-in compiler doesn't optimize
# it out we don't either.
self.current.addNext(block)
self.startBlock(block)
def newBlock(self):
b = Block()
self.blocks.add(b)
return b
def startExitBlock(self):
self.startBlock(self.exit)
def emit(self, *inst):
if self.DEBUG:
print("\t", inst)
if len(inst) == 2 and isinstance(inst[1], Block):
self.current.addOutEdge(inst[1])
self.current.emit(inst)
def getContainedGraphs(self):
raise AssertionError('unused')
l = []
for b in self.getBlocks():
l.extend(b.getContainedGraphs())
return l
def OrderBlocks(start_block, exit_block):
"""Order blocks so that they are emitted in the right order"""
# Rules:
@@ -229,6 +79,38 @@ def find_next():
return order
def FlattenGraph(blocks):
insts = []
pc = 0
begin = {}
end = {}
for b in blocks:
begin[b] = pc
for inst in b.getInstructions():
insts.append(inst)
if len(inst) == 1:
pc += 1
elif inst[0] != "SET_LINENO":
# arg takes 2 bytes
pc += 3
end[b] = pc
pc = 0
for i, inst in enumerate(insts):
if len(inst) == 1:
pc += 1
elif inst[0] != "SET_LINENO":
pc += 3
opname = inst[0]
if opname in HAS_JREL:
oparg = inst[1]
offset = begin[oparg] - pc
insts[i] = opname, offset
elif opname in HAS_JABS:
insts[i] = opname, begin[inst[1]]
return insts
gBlockCounter = 0
@@ -323,6 +205,75 @@ def getContainedGraphs(self):
return contained
class FlowGraph(object):
def __init__(self):
self.current = self.entry = Block()
self.exit = Block("exit")
self.blocks = set()
self.blocks.add(self.entry)
self.blocks.add(self.exit)
DEBUG = False
def startBlock(self, block):
if self.DEBUG:
if self.current:
print("end", repr(self.current))
print(" next", self.current.next)
print(" prev", self.current.prev)
print(" ", self.current.get_children())
print(repr(block))
self.current = block
def nextBlock(self, block=None):
# XXX think we need to specify when there is implicit transfer
# from one block to the next. might be better to represent this
# with explicit JUMP_ABSOLUTE instructions that are optimized
# out when they are unnecessary.
#
# I think this strategy works: each block has a child
# designated as "next" which is returned as the last of the
# children. because the nodes in a graph are emitted in
# reverse post order, the "next" block will always be emitted
# immediately after its parent.
# Worry: maintaining this invariant could be tricky
if block is None:
block = self.newBlock()
# Note: If the current block ends with an unconditional control
# transfer, then it is techically incorrect to add an implicit
# transfer to the block graph. Doing so results in code generation
# for unreachable blocks. That doesn't appear to be very common
# with Python code and since the built-in compiler doesn't optimize
# it out we don't either.
self.current.addNext(block)
self.startBlock(block)
def newBlock(self):
b = Block()
self.blocks.add(b)
return b
def startExitBlock(self):
self.startBlock(self.exit)
def emit(self, *inst):
if self.DEBUG:
print("\t", inst)
if len(inst) == 2 and isinstance(inst[1], Block):
self.current.addOutEdge(inst[1])
self.current.emit(inst)
def getContainedGraphs(self):
raise AssertionError('unused')
l = []
for b in self.getBlocks():
l.extend(b.getContainedGraphs())
return l
# TODO: Unify FlowGraph and PyFlowGraph.
class PyFlowGraph(FlowGraph):
"""Something that gets turned into a single code object.
@@ -428,6 +379,24 @@ def MakeCodeObject(self):
tuple(cellvars))
def _NameToIndex(name, L):
"""Return index of name in list, appending if necessary
This routine uses a list instead of a dictionary, because a
dictionary can't store two different keys if the keys have the
same value but different types, e.g. 2 and 2L. The compiler
must treat these two separately, so it does an explicit type
comparison before comparing the values.
"""
t = type(name)
for i, item in enumerate(L):
if t == type(item) and item == name:
return i
end = len(L)
L.append(name)
return end
class ArgEncoder(object):
"""Replaces arg objects with indices."""
@@ -600,29 +569,35 @@ class StackDepthTracker(object):
def findDepth(self, insts, debug=0):
depth = 0
maxDepth = 0
for i in insts:
opname = i[0]
for inst in insts:
opname = inst[0]
if debug:
print(i, end=' ')
print(inst, end=' ')
delta = self.effect.get(opname, None)
if delta is not None:
depth = depth + delta
else:
if delta is None:
# now check patterns
for pat, pat_delta in self.patterns:
if opname[:len(pat)] == pat:
delta = pat_delta
depth = depth + delta
break
# if we still haven't found a match
if delta is None:
meth = getattr(self, opname, None)
if meth is not None:
depth = depth + meth(i[1])
if depth > maxDepth:
maxDepth = depth
if delta is None:
meth = getattr(self, opname, None)
if meth is not None:
delta = meth(inst[1])
if delta is None:
# Jumps missing here.
#assert opname in ('POP_JUMP_IF_FALSE', 'SET_LINENO'), opname
delta = 0
depth += delta
maxDepth = max(depth, maxDepth)
if debug:
print(depth, maxDepth)
print('%s %s' % (depth, maxDepth))
return maxDepth
effect = {
@@ -705,3 +680,35 @@ def DUP_TOPX(self, argc):
TRACKER = StackDepthTracker()
def ComputeStackDepth(blocks, entry_block, exit_block):
"""Compute the max stack depth.
Approach is to compute the stack effect of each basic block.
Then find the path through the code with the largest total
effect.
"""
depth = {}
for b in blocks:
depth[b] = TRACKER.findDepth(b.getInstructions())
seen = set()
def max_depth(b, d):
if b in seen:
return d
seen.add(b)
d += depth[b]
children = b.get_children()
if children:
return max(max_depth(c, d) for c in children)
else:
if not b.label == "exit":
return max_depth(exit_block, d)
else:
return d
return max_depth(entry_block, 0)

0 comments on commit c2c125c

Please sign in to comment.