Changeset 391 for python/trunk/Lib/compiler/pyassem.py
- Timestamp:
- Mar 19, 2014, 11:31:01 PM (11 years ago)
- Location:
- python/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
python/trunk
-
Property svn:mergeinfo
set to
/python/vendor/Python-2.7.6 merged eligible /python/vendor/current merged eligible
-
Property svn:mergeinfo
set to
-
python/trunk/Lib/compiler/pyassem.py
r2 r391 22 22 print "end", repr(self.current) 23 23 print " next", self.current.next 24 print " prev", self.current.prev 24 25 print " ", self.current.get_children() 25 26 print repr(block) … … 41 42 block = self.newBlock() 42 43 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. 50 50 self.current.addNext(block) 51 51 self.startBlock(block) … … 70 70 if self._debug: 71 71 print "\t", inst 72 if inst[0] in ['RETURN_VALUE', 'YIELD_VALUE']:73 self.current.addOutEdge(self.exit)74 72 if len(inst) == 2 and isinstance(inst[1], Block): 75 73 self.current.addOutEdge(inst[1]) … … 81 79 i.e. each node appears before all of its successors 82 80 """ 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) 97 82 return order 98 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 get103 # 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 special112 "next" block. As a result, the DFS can order blocks so that a113 block isn't next to the right block for implicit control114 transfers.115 """116 index = {}117 for i in range(len(blocks)):118 index[blocks[i]] = i119 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 continue125 # The blocks are in the wrong order. Find the chain of126 # blocks to insert where they belong.127 cur = b128 chain = []129 elt = cur130 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 current134 # block list, so that they can be re-inserted.135 l = []136 for b in chain:137 assert index[b] > i138 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 location144 blocks[i:i + 1] = [cur] + chain145 # Finally, re-compute the block indexes146 for i in range(len(blocks)):147 index[blocks[i]] = i148 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 = 0171 for inst in b.insts:172 if inst[0] == 'JUMP_FORWARD':173 if inst[1] == c:174 forward_p = 1175 if not forward_p:176 continue177 constraints.append((index[c], i))178 179 if not constraints:180 break181 182 # XXX just do one for now183 # do swaps to get things in the right order184 goes_before, a_chain = constraints[0]185 assert a_chain > goes_before186 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 83 195 84 def getBlocks(self): … … 206 95 return l 207 96 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 210 105 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: 214 113 continue 215 order = order + dfs_postorder(c, seen) 216 order.append(b) 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 # Make sure every block appears in dominators, even if no 126 # other block must precede it. 127 dominators.setdefault(b, set()) 128 # preceding blocks dominate following blocks 129 for c in b.get_followers(): 130 while 1: 131 dominators.setdefault(c, set()).add(b) 132 # Any block that has a next pointer leading to c is also 133 # dominated because the whole chain will be emitted at once. 134 # Walk backwards and add them all. 135 if c.prev and c.prev[0] is not b: 136 c = c.prev[0] 137 else: 138 break 139 140 def find_next(): 141 # Find a block that can be emitted next. 142 for b in remaining: 143 for c in dominators[b]: 144 if c in remaining: 145 break # can't emit yet, dominated by a remaining block 146 else: 147 return b 148 assert 0, 'circular dependency, cannot find next block' 149 150 b = start_block 151 while 1: 152 order.append(b) 153 remaining.discard(b) 154 if b.next: 155 b = b.next[0] 156 continue 157 elif b is not exit_block and not b.has_unconditional_transfer(): 158 order.append(exit_block) 159 if not remaining: 160 break 161 b = find_next() 217 162 return order 163 218 164 219 165 class Block: … … 222 168 def __init__(self, label=''): 223 169 self.insts = [] 224 self.inEdges = misc.Set() 225 self.outEdges = misc.Set() 170 self.outEdges = set() 226 171 self.label = label 227 172 self.bid = Block._count 228 173 self.next = [] 174 self.prev = [] 229 175 Block._count = Block._count + 1 230 176 … … 242 188 def emit(self, inst): 243 189 op = inst[0] 244 if op[:4] == 'JUMP':245 self.outEdges.add(inst[1])246 190 self.insts.append(inst) 247 191 248 192 def getInstructions(self): 249 193 return self.insts 250 251 def addInEdge(self, block):252 self.inEdges.add(block)253 194 254 195 def addOutEdge(self, block): … … 258 199 self.next.append(block) 259 200 assert len(self.next) == 1, map(str, self.next) 260 261 _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS', 'YIELD_VALUE', 262 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP') 263 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 """ 201 block.prev.append(self) 202 assert len(block.prev) == 1, map(str, block.prev) 203 204 _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS', 205 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP', 206 ) 207 208 def has_unconditional_transfer(self): 209 """Returns True if there is an unconditional transfer to an other block 210 at the end of this block. This means there is no risk for the bytecode 211 executer to go past this block's bytecode.""" 278 212 try: 279 213 op, arg = self.insts[-1] 280 214 except (IndexError, ValueError): 281 215 return 282 if op in self._uncond_transfer: 283 self.next = [] 216 return op in self._uncond_transfer 284 217 285 218 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 219 return list(self.outEdges) + self.next 220 221 def get_followers(self): 222 """Get the whole list of followers, including the next block.""" 223 followers = set(self.next) 224 # Blocks that must be emitted *after* this one, because of 225 # bytecode offsets (e.g. relative jumps) pointing to them. 226 for inst in self.insts: 227 if inst[0] in PyFlowGraph.hasjrel: 228 followers.add(inst[1]) 229 return followers 289 230 290 231 def getContainedGraphs(self): … … 447 388 pc = pc + 3 448 389 opname = inst[0] 449 if self.hasjrel.has_elt(opname):390 if opname in self.hasjrel: 450 391 oparg = inst[1] 451 392 offset = begin[oparg] - pc 452 393 insts[i] = opname, offset 453 elif self.hasjabs.has_elt(opname):394 elif opname in self.hasjabs: 454 395 insts[i] = opname, begin[inst[1]] 455 396 self.stage = FLAT 456 397 457 hasjrel = misc.Set()398 hasjrel = set() 458 399 for i in dis.hasjrel: 459 400 hasjrel.add(dis.opname[i]) 460 hasjabs = misc.Set()401 hasjabs = set() 461 402 for i in dis.hasjabs: 462 403 hasjabs.add(dis.opname[i]) … … 745 686 'POP_TOP': -1, 746 687 'DUP_TOP': 1, 747 'LIST_APPEND': -2, 688 'LIST_APPEND': -1, 689 'SET_ADD': -1, 690 'MAP_ADD': -2, 748 691 'SLICE+1': -1, 749 692 'SLICE+2': -1, … … 794 737 def BUILD_LIST(self, count): 795 738 return -count+1 739 def BUILD_SET(self, count): 740 return -count+1 796 741 def CALL_FUNCTION(self, argc): 797 742 hi, lo = divmod(argc, 256)
Note:
See TracChangeset
for help on using the changeset viewer.