Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 314e3fb

Browse files
committed
Change the graph structure to contain the code generator object for
embedded code objects (e.g. functions) rather than the generated code object. This change means that the compiler generates code for everything at the end, rather then generating code for each function as it finds it. Implementation note: _convert_LOAD_CONST in pyassem.py must be change to call getCode(). Other changes follow. Several changes creates extra edges between basic blocks to reflect control flow for loops and exceptions. These missing edges had gone unnoticed because they do not affect the current compilation process. pyassem.py: Add _enable_debug() and _disable_debug() methods that print instructions and blocks to stdout as they are generated. Add edges between blocks for instructions like SETUP_LOOP, FOR_LOOP, etc. Add pruneNext to get rid of bogus edges remaining after unconditional transfer ops (e.g. JUMP_FORWARD) Change repr of Block to omit block length. pycodegen.py: Make sure a new block is started after FOR_LOOP, etc. Change assert implementation to use RAISE_VARARGS 1 when there is no user-specified failure output. misc.py: Implement __contains__ and copy for Set.
1 parent a59ac0a commit 314e3fb

6 files changed

Lines changed: 266 additions & 42 deletions

File tree

Lib/compiler/misc.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ def __init__(self):
1414
self.elts = {}
1515
def __len__(self):
1616
return len(self.elts)
17+
def __contains__(self, elt):
18+
return self.elts.has_key(elt)
1719
def add(self, elt):
1820
self.elts[elt] = elt
1921
def elements(self):
@@ -22,6 +24,10 @@ def has_elt(self, elt):
2224
return self.elts.has_key(elt)
2325
def remove(self, elt):
2426
del self.elts[elt]
27+
def copy(self):
28+
c = Set()
29+
c.elts.update(self.elts)
30+
return c
2531

2632
class Stack:
2733
def __init__(self):

Lib/compiler/pyassem.py

Lines changed: 108 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@
33
import dis
44
import new
55
import string
6+
import sys
67
import types
78

89
from compiler import misc
910

11+
def xxx_sort(l):
12+
l = l[:]
13+
def sorter(a, b):
14+
return cmp(a.bid, b.bid)
15+
l.sort(sorter)
16+
return l
17+
1018
class FlowGraph:
1119
def __init__(self):
1220
self.current = self.entry = Block()
@@ -16,20 +24,35 @@ def __init__(self):
1624
self.blocks.add(self.exit)
1725

1826
def startBlock(self, block):
27+
if self._debug:
28+
if self.current:
29+
print "end", repr(self.current)
30+
print " ", self.current.get_children()
31+
print repr(block)
1932
self.current = block
2033

21-
def nextBlock(self, block=None):
22-
if block is None:
23-
block = self.newBlock()
34+
def nextBlock(self, block=None, force=0):
2435
# XXX think we need to specify when there is implicit transfer
25-
# from one block to the next
36+
# from one block to the next. might be better to represent this
37+
# with explicit JUMP_ABSOLUTE instructions that are optimized
38+
# out when they are unnecessary.
2639
#
2740
# I think this strategy works: each block has a child
2841
# designated as "next" which is returned as the last of the
2942
# children. because the nodes in a graph are emitted in
3043
# reverse post order, the "next" block will always be emitted
3144
# immediately after its parent.
3245
# Worry: maintaining this invariant could be tricky
46+
if block is None:
47+
block = self.newBlock()
48+
49+
# Note: If the current block ends with an unconditional
50+
# control transfer, then it is incorrect to add an implicit
51+
# transfer to the block graph. The current code requires
52+
# these edges to get the blocks emitted in the right order,
53+
# however. :-( If a client needs to remove these edges, call
54+
# pruneEdges().
55+
3356
self.current.addNext(block)
3457
self.startBlock(block)
3558

@@ -41,13 +64,24 @@ def newBlock(self):
4164
def startExitBlock(self):
4265
self.startBlock(self.exit)
4366

67+
_debug = 0
68+
69+
def _enable_debug(self):
70+
self._debug = 1
71+
72+
def _disable_debug(self):
73+
self._debug = 0
74+
4475
def emit(self, *inst):
45-
# XXX should jump instructions implicitly call nextBlock?
76+
if self._debug:
77+
print "\t", inst
4678
if inst[0] == 'RETURN_VALUE':
4779
self.current.addOutEdge(self.exit)
80+
if len(inst) == 2 and isinstance(inst[1], Block):
81+
self.current.addOutEdge(inst[1])
4882
self.current.emit(inst)
4983

50-
def getBlocks(self):
84+
def getBlocksInOrder(self):
5185
"""Return the blocks in reverse postorder
5286
5387
i.e. each node appears before all of its successors
@@ -64,13 +98,57 @@ def getBlocks(self):
6498
# hack alert
6599
if not self.exit in order:
66100
order.append(self.exit)
101+
102+
## for b in order:
103+
## print repr(b)
104+
## print "\t", b.get_children()
105+
## print b
106+
## print
107+
67108
return order
68109

110+
def getBlocks(self):
111+
return self.blocks.elements()
112+
113+
def getRoot(self):
114+
"""Return nodes appropriate for use with dominator"""
115+
return self.entry
116+
117+
def getContainedGraphs(self):
118+
l = []
119+
for b in self.getBlocks():
120+
l.extend(b.getContainedGraphs())
121+
return l
122+
123+
_uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS',
124+
'JUMP_ABSOLUTE', 'JUMP_FORWARD')
125+
126+
def pruneNext(self):
127+
"""Remove bogus edge for unconditional transfers
128+
129+
Each block has a next edge that accounts for implicit control
130+
transfers, e.g. from a JUMP_IF_FALSE to the block that will be
131+
executed if the test is true.
132+
133+
These edges must remain for the current assembler code to
134+
work. If they are removed, the dfs_postorder gets things in
135+
weird orders. However, they shouldn't be there for other
136+
purposes, e.g. conversion to SSA form. This method will
137+
remove the next edge when it follows an unconditional control
138+
transfer.
139+
"""
140+
try:
141+
op, arg = self.insts[-1]
142+
except (IndexError, TypeError):
143+
return
144+
if op in self._uncond_transfer:
145+
self.next = []
146+
69147
def dfs_postorder(b, seen):
70148
"""Depth-first search of tree rooted at b, return in postorder"""
71149
order = []
72150
seen[b] = b
73-
for c in b.children():
151+
for c in b.get_children():
74152
if seen.has_key(c):
75153
continue
76154
order = order + dfs_postorder(c, seen)
@@ -91,10 +169,9 @@ def __init__(self, label=''):
91169

92170
def __repr__(self):
93171
if self.label:
94-
return "<block %s id=%d len=%d>" % (self.label, self.bid,
95-
len(self.insts))
172+
return "<block %s id=%d>" % (self.label, self.bid)
96173
else:
97-
return "<block id=%d len=%d>" % (self.bid, len(self.insts))
174+
return "<block id=%d>" % (self.bid)
98175

99176
def __str__(self):
100177
insts = map(str, self.insts)
@@ -120,9 +197,26 @@ def addNext(self, block):
120197
self.next.append(block)
121198
assert len(self.next) == 1, map(str, self.next)
122199

123-
def children(self):
200+
def get_children(self):
201+
if self.next and self.next[0] in self.outEdges:
202+
self.outEdges.remove(self.next[0])
124203
return self.outEdges.elements() + self.next
125204

205+
def getContainedGraphs(self):
206+
"""Return all graphs contained within this block.
207+
208+
For example, a MAKE_FUNCTION block will contain a reference to
209+
the graph for the function body.
210+
"""
211+
contained = []
212+
for inst in self.insts:
213+
if len(inst) == 1:
214+
continue
215+
op = inst[1]
216+
if hasattr(op, 'graph'):
217+
contained.append(op.graph)
218+
return contained
219+
126220
# flags for code objects
127221
CO_OPTIMIZED = 0x0001
128222
CO_NEWLOCALS = 0x0002
@@ -204,7 +298,7 @@ def flattenGraph(self):
204298
pc = 0
205299
begin = {}
206300
end = {}
207-
for b in self.getBlocks():
301+
for b in self.getBlocksInOrder():
208302
begin[b] = pc
209303
for inst in b.getInstructions():
210304
insts.append(inst)
@@ -274,6 +368,8 @@ def _lookupName(self, name, list):
274368

275369
_converters = {}
276370
def _convert_LOAD_CONST(self, arg):
371+
if hasattr(arg, 'getCode'):
372+
arg = arg.getCode()
277373
return self._lookupName(arg, self.consts)
278374

279375
def _convert_LOAD_FAST(self, arg):

Lib/compiler/pycodegen.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def _visitFuncOrLambda(self, node, isLambda):
160160
self.set_lineno(node)
161161
for default in node.defaults:
162162
self.visit(default)
163-
self.emit('LOAD_CONST', gen.getCode())
163+
self.emit('LOAD_CONST', gen)
164164
self.emit('MAKE_FUNCTION', len(node.defaults))
165165

166166
def visitClass(self, node):
@@ -195,7 +195,7 @@ def visitIf(self, node):
195195
self.emit('POP_TOP')
196196
self.visit(suite)
197197
self.emit('JUMP_FORWARD', end)
198-
self.nextBlock(nextTest)
198+
self.startBlock(nextTest)
199199
self.emit('POP_TOP')
200200
if node.else_:
201201
self.visit(node.else_)
@@ -243,10 +243,11 @@ def visitFor(self, node):
243243
self.nextBlock(start)
244244
self.set_lineno(node)
245245
self.emit('FOR_LOOP', anchor)
246+
self.nextBlock()
246247
self.visit(node.assign)
247248
self.visit(node.body)
248249
self.emit('JUMP_ABSOLUTE', start)
249-
self.nextBlock(anchor)
250+
self.startBlock(anchor)
250251
self.emit('POP_BLOCK')
251252
if node.else_:
252253
self.visit(node.else_)
@@ -304,7 +305,7 @@ def visitCompare(self, node):
304305
if len(node.ops) > 1:
305306
end = self.newBlock()
306307
self.emit('JUMP_FORWARD', end)
307-
self.nextBlock(cleanup)
308+
self.startBlock(cleanup)
308309
self.emit('ROT_TWO')
309310
self.emit('POP_TOP')
310311
self.nextBlock(end)
@@ -344,11 +345,11 @@ def visitListComp(self, node):
344345
if cont:
345346
skip_one = self.newBlock()
346347
self.emit('JUMP_FORWARD', skip_one)
347-
self.nextBlock(cont)
348+
self.startBlock(cont)
348349
self.emit('POP_TOP')
349350
self.nextBlock(skip_one)
350351
self.emit('JUMP_ABSOLUTE', start)
351-
self.nextBlock(anchor)
352+
self.startBlock(anchor)
352353
self.delName(append)
353354

354355
self.__list_count = self.__list_count - 1
@@ -363,6 +364,7 @@ def visitListCompFor(self, node):
363364
self.emit('SET_LINENO', node.lineno)
364365
self.nextBlock(start)
365366
self.emit('FOR_LOOP', anchor)
367+
self.nextBlock()
366368
self.visit(node.assign)
367369
return start, anchor
368370

@@ -390,9 +392,13 @@ def visitAssert(self, node):
390392
self.visit(node.test)
391393
self.emit('JUMP_IF_TRUE', end)
392394
self.nextBlock()
395+
self.emit('POP_TOP')
393396
self.emit('LOAD_GLOBAL', 'AssertionError')
394-
self.visit(node.fail)
395-
self.emit('RAISE_VARARGS', 2)
397+
if node.fail:
398+
self.visit(node.fail)
399+
self.emit('RAISE_VARARGS', 2)
400+
else:
401+
self.emit('RAISE_VARARGS', 1)
396402
self.nextBlock(end)
397403
self.emit('POP_TOP')
398404

@@ -419,10 +425,11 @@ def visitTryExcept(self, node):
419425
lElse = end
420426
self.set_lineno(node)
421427
self.emit('SETUP_EXCEPT', handlers)
428+
self.nextBlock()
422429
self.visit(node.body)
423430
self.emit('POP_BLOCK')
424431
self.emit('JUMP_FORWARD', lElse)
425-
self.nextBlock(handlers)
432+
self.startBlock(handlers)
426433

427434
last = len(node.handlers) - 1
428435
for i in range(len(node.handlers)):
@@ -446,6 +453,8 @@ def visitTryExcept(self, node):
446453
self.emit('JUMP_FORWARD', end)
447454
if expr:
448455
self.nextBlock(next)
456+
else:
457+
self.nextBlock()
449458
self.emit('POP_TOP')
450459
self.emit('END_FINALLY')
451460
if node.else_:
@@ -457,6 +466,7 @@ def visitTryFinally(self, node):
457466
final = self.newBlock()
458467
self.set_lineno(node)
459468
self.emit('SETUP_FINALLY', final)
469+
self.nextBlock()
460470
self.visit(node.body)
461471
self.emit('POP_BLOCK')
462472
self.emit('LOAD_CONST', None)

Tools/compiler/compiler/misc.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ def __init__(self):
1414
self.elts = {}
1515
def __len__(self):
1616
return len(self.elts)
17+
def __contains__(self, elt):
18+
return self.elts.has_key(elt)
1719
def add(self, elt):
1820
self.elts[elt] = elt
1921
def elements(self):
@@ -22,6 +24,10 @@ def has_elt(self, elt):
2224
return self.elts.has_key(elt)
2325
def remove(self, elt):
2426
del self.elts[elt]
27+
def copy(self):
28+
c = Set()
29+
c.elts.update(self.elts)
30+
return c
2531

2632
class Stack:
2733
def __init__(self):

0 commit comments

Comments
 (0)