@@ -21,6 +21,7 @@ def startBlock(self, block):
2121 if self .current :
2222 print "end" , repr (self .current )
2323 print " next" , self .current .next
24+ print " prev" , self .current .prev
2425 print " " , self .current .get_children ()
2526 print repr (block )
2627 self .current = block
@@ -40,13 +41,12 @@ def nextBlock(self, block=None):
4041 if block is None :
4142 block = self .newBlock ()
4243
43- # Note: If the current block ends with an unconditional
44- # control transfer, then it is incorrect to add an implicit
45- # transfer to the block graph. The current code requires
46- # these edges to get the blocks emitted in the right order,
47- # however. :-( If a client needs to remove these edges, call
48- # pruneEdges().
49-
44+ # Note: If the current block ends with an unconditional control
45+ # transfer, then it is techically incorrect to add an implicit
46+ # transfer to the block graph. Doing so results in code generation
47+ # for unreachable blocks. That doesn't appear to be very common
48+ # with Python code and since the built-in compiler doesn't optimize
49+ # it out we don't either.
5050 self .current .addNext (block )
5151 self .startBlock (block )
5252
@@ -69,8 +69,6 @@ def _disable_debug(self):
6969 def emit (self , * inst ):
7070 if self ._debug :
7171 print "\t " , inst
72- if inst [0 ] in ['RETURN_VALUE' , 'YIELD_VALUE' ]:
73- self .current .addOutEdge (self .exit )
7472 if len (inst ) == 2 and isinstance (inst [1 ], Block ):
7573 self .current .addOutEdge (inst [1 ])
7674 self .current .emit (inst )
@@ -80,118 +78,9 @@ def getBlocksInOrder(self):
8078
8179 i.e. each node appears before all of its successors
8280 """
83- # XXX make sure every node that doesn't have an explicit next
84- # is set so that next points to exit
85- for b in self .blocks .elements ():
86- if b is self .exit :
87- continue
88- if not b .next :
89- b .addNext (self .exit )
90- order = dfs_postorder (self .entry , {})
91- order .reverse ()
92- self .fixupOrder (order , self .exit )
93- # hack alert
94- if not self .exit in order :
95- order .append (self .exit )
96-
81+ order = order_blocks (self .entry , self .exit )
9782 return order
9883
99- def fixupOrder (self , blocks , default_next ):
100- """Fixup bad order introduced by DFS."""
101-
102- # XXX This is a total mess. There must be a better way to get
103- # the code blocks in the right order.
104-
105- self .fixupOrderHonorNext (blocks , default_next )
106- self .fixupOrderForward (blocks , default_next )
107-
108- def fixupOrderHonorNext (self , blocks , default_next ):
109- """Fix one problem with DFS.
110-
111- The DFS uses child block, but doesn't know about the special
112- "next" block. As a result, the DFS can order blocks so that a
113- block isn't next to the right block for implicit control
114- transfers.
115- """
116- index = {}
117- for i in range (len (blocks )):
118- index [blocks [i ]] = i
119-
120- for i in range (0 , len (blocks ) - 1 ):
121- b = blocks [i ]
122- n = blocks [i + 1 ]
123- if not b .next or b .next [0 ] == default_next or b .next [0 ] == n :
124- continue
125- # The blocks are in the wrong order. Find the chain of
126- # blocks to insert where they belong.
127- cur = b
128- chain = []
129- elt = cur
130- while elt .next and elt .next [0 ] != default_next :
131- chain .append (elt .next [0 ])
132- elt = elt .next [0 ]
133- # Now remove the blocks in the chain from the current
134- # block list, so that they can be re-inserted.
135- l = []
136- for b in chain :
137- assert index [b ] > i
138- l .append ((index [b ], b ))
139- l .sort ()
140- l .reverse ()
141- for j , b in l :
142- del blocks [index [b ]]
143- # Insert the chain in the proper location
144- blocks [i :i + 1 ] = [cur ] + chain
145- # Finally, re-compute the block indexes
146- for i in range (len (blocks )):
147- index [blocks [i ]] = i
148-
149- def fixupOrderForward (self , blocks , default_next ):
150- """Make sure all JUMP_FORWARDs jump forward"""
151- index = {}
152- chains = []
153- cur = []
154- for b in blocks :
155- index [b ] = len (chains )
156- cur .append (b )
157- if b .next and b .next [0 ] == default_next :
158- chains .append (cur )
159- cur = []
160- chains .append (cur )
161-
162- while 1 :
163- constraints = []
164-
165- for i in range (len (chains )):
166- l = chains [i ]
167- for b in l :
168- for c in b .get_children ():
169- if index [c ] < i :
170- forward_p = 0
171- for inst in b .insts :
172- if inst [0 ] == 'JUMP_FORWARD' :
173- if inst [1 ] == c :
174- forward_p = 1
175- if not forward_p :
176- continue
177- constraints .append ((index [c ], i ))
178-
179- if not constraints :
180- break
181-
182- # XXX just do one for now
183- # do swaps to get things in the right order
184- goes_before , a_chain = constraints [0 ]
185- assert a_chain > goes_before
186- c = chains [a_chain ]
187- chains .remove (c )
188- chains .insert (goes_before , c )
189-
190- del blocks [:]
191- for c in chains :
192- for b in c :
193- blocks .append (b )
194-
19584 def getBlocks (self ):
19685 return self .blocks .elements ()
19786
@@ -205,27 +94,81 @@ def getContainedGraphs(self):
20594 l .extend (b .getContainedGraphs ())
20695 return l
20796
208- def dfs_postorder (b , seen ):
209- """Depth-first search of tree rooted at b, return in postorder"""
97+
98+ def order_blocks (start_block , exit_block ):
99+ """Order blocks so that they are emitted in the right order"""
100+ # Rules:
101+ # - when a block has a next block, the next block must be emitted just after
102+ # - when a block has followers (relative jumps), it must be emitted before
103+ # them
104+ # - all reachable blocks must be emitted
210105 order = []
211- seen [b ] = b
212- for c in b .get_children ():
213- if c in seen :
106+
107+ # Find all the blocks to be emitted.
108+ remaining = set ()
109+ todo = [start_block ]
110+ while todo :
111+ b = todo .pop ()
112+ if b in remaining :
113+ continue
114+ remaining .add (b )
115+ for c in b .get_children ():
116+ if c not in remaining :
117+ todo .append (c )
118+
119+ # A block is dominated by another block if that block must be emitted
120+ # before it.
121+ dominators = {}
122+ for b in remaining :
123+ if __debug__ and b .next :
124+ assert b is b .next [0 ].prev [0 ], (b , b .next )
125+ # preceeding blocks dominate following blocks
126+ for c in b .get_followers ():
127+ while 1 :
128+ dominators .setdefault (c , set ()).add (b )
129+ # Any block that has a next pointer leading to c is also
130+ # dominated because the whole chain will be emitted at once.
131+ # Walk backwards and add them all.
132+ if c .prev and c .prev [0 ] is not b :
133+ c = c .prev [0 ]
134+ else :
135+ break
136+
137+ def find_next ():
138+ # Find a block that can be emitted next.
139+ for b in remaining :
140+ for c in dominators [b ]:
141+ if c in remaining :
142+ break # can't emit yet, dominated by a remaining block
143+ else :
144+ return b
145+ assert 0 , 'circular dependency, cannot find next block'
146+
147+ b = start_block
148+ while 1 :
149+ order .append (b )
150+ remaining .discard (b )
151+ if b .next :
152+ b = b .next [0 ]
214153 continue
215- order = order + dfs_postorder (c , seen )
216- order .append (b )
154+ elif b is not exit_block and not b .has_unconditional_transfer ():
155+ order .append (exit_block )
156+ if not remaining :
157+ break
158+ b = find_next ()
217159 return order
218160
161+
219162class Block :
220163 _count = 0
221164
222165 def __init__ (self , label = '' ):
223166 self .insts = []
224- self .inEdges = misc .Set ()
225- self .outEdges = misc .Set ()
167+ self .outEdges = set ()
226168 self .label = label
227169 self .bid = Block ._count
228170 self .next = []
171+ self .prev = []
229172 Block ._count = Block ._count + 1
230173
231174 def __repr__ (self ):
@@ -241,51 +184,46 @@ def __str__(self):
241184
242185 def emit (self , inst ):
243186 op = inst [0 ]
244- if op [:4 ] == 'JUMP' :
245- self .outEdges .add (inst [1 ])
246187 self .insts .append (inst )
247188
248189 def getInstructions (self ):
249190 return self .insts
250191
251- def addInEdge (self , block ):
252- self .inEdges .add (block )
253-
254192 def addOutEdge (self , block ):
255193 self .outEdges .add (block )
256194
257195 def addNext (self , block ):
258196 self .next .append (block )
259197 assert len (self .next ) == 1 , map (str , self .next )
198+ block .prev .append (self )
199+ assert len (block .prev ) == 1 , map (str , block .prev )
260200
261- _uncond_transfer = ('RETURN_VALUE' , 'RAISE_VARARGS' , 'YIELD_VALUE' ,
262- 'JUMP_ABSOLUTE' , 'JUMP_FORWARD' , 'CONTINUE_LOOP' )
201+ _uncond_transfer = ('RETURN_VALUE' , 'RAISE_VARARGS' ,
202+ 'JUMP_ABSOLUTE' , 'JUMP_FORWARD' , 'CONTINUE_LOOP' ,
203+ )
263204
264- def pruneNext (self ):
265- """Remove bogus edge for unconditional transfers
266-
267- Each block has a next edge that accounts for implicit control
268- transfers, e.g. from a JUMP_IF_FALSE to the block that will be
269- executed if the test is true.
270-
271- These edges must remain for the current assembler code to
272- work. If they are removed, the dfs_postorder gets things in
273- weird orders. However, they shouldn't be there for other
274- purposes, e.g. conversion to SSA form. This method will
275- remove the next edge when it follows an unconditional control
276- transfer.
277- """
205+ def has_unconditional_transfer (self ):
206+ """Returns True if there is an unconditional transfer to an other block
207+ at the end of this block. This means there is no risk for the bytecode
208+ executer to go past this block's bytecode."""
278209 try :
279210 op , arg = self .insts [- 1 ]
280211 except (IndexError , ValueError ):
281212 return
282- if op in self ._uncond_transfer :
283- self .next = []
213+ return op in self ._uncond_transfer
284214
285215 def get_children (self ):
286- if self .next and self .next [0 ] in self .outEdges :
287- self .outEdges .remove (self .next [0 ])
288- return self .outEdges .elements () + self .next
216+ return list (self .outEdges ) + self .next
217+
218+ def get_followers (self ):
219+ """Get the whole list of followers, including the next block."""
220+ followers = set (self .next )
221+ # Blocks that must be emitted *after* this one, because of
222+ # bytecode offsets (e.g. relative jumps) pointing to them.
223+ for inst in self .insts :
224+ if inst [0 ] in PyFlowGraph .hasjrel :
225+ followers .add (inst [1 ])
226+ return followers
289227
290228 def getContainedGraphs (self ):
291229 """Return all graphs contained within this block.
@@ -446,18 +384,18 @@ def flattenGraph(self):
446384 elif inst [0 ] != "SET_LINENO" :
447385 pc = pc + 3
448386 opname = inst [0 ]
449- if self .hasjrel . has_elt ( opname ) :
387+ if opname in self .hasjrel :
450388 oparg = inst [1 ]
451389 offset = begin [oparg ] - pc
452390 insts [i ] = opname , offset
453- elif self .hasjabs . has_elt ( opname ) :
391+ elif opname in self .hasjabs :
454392 insts [i ] = opname , begin [inst [1 ]]
455393 self .stage = FLAT
456394
457- hasjrel = misc . Set ()
395+ hasjrel = set ()
458396 for i in dis .hasjrel :
459397 hasjrel .add (dis .opname [i ])
460- hasjabs = misc . Set ()
398+ hasjabs = set ()
461399 for i in dis .hasjabs :
462400 hasjabs .add (dis .opname [i ])
463401
0 commit comments