Ignore:
Timestamp:
Mar 19, 2014, 11:31:01 PM (11 years ago)
Author:
dmik
Message:

python: Merge vendor 2.7.6 to trunk.

Location:
python/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • python/trunk

  • python/trunk/Lib/compiler/pyassem.py

    r2 r391  
    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)
     
    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)
     
    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])
     
    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
    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 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)
    19483
    19584    def getBlocks(self):
     
    20695        return l
    20796
    208 def dfs_postorder(b, seen):
    209     """Depth-first search of tree rooted at b, return in postorder"""
     97
     98def 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:
    214113            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()
    217162    return order
     163
    218164
    219165class Block:
     
    222168    def __init__(self, label=''):
    223169        self.insts = []
    224         self.inEdges = misc.Set()
    225         self.outEdges = misc.Set()
     170        self.outEdges = set()
    226171        self.label = label
    227172        self.bid = Block._count
    228173        self.next = []
     174        self.prev = []
    229175        Block._count = Block._count + 1
    230176
     
    242188    def emit(self, inst):
    243189        op = inst[0]
    244         if op[:4] == 'JUMP':
    245             self.outEdges.add(inst[1])
    246190        self.insts.append(inst)
    247191
    248192    def getInstructions(self):
    249193        return self.insts
    250 
    251     def addInEdge(self, block):
    252         self.inEdges.add(block)
    253194
    254195    def addOutEdge(self, block):
     
    258199        self.next.append(block)
    259200        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."""
    278212        try:
    279213            op, arg = self.insts[-1]
    280214        except (IndexError, ValueError):
    281215            return
    282         if op in self._uncond_transfer:
    283             self.next = []
     216        return op in self._uncond_transfer
    284217
    285218    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
    289230
    290231    def getContainedGraphs(self):
     
    447388                pc = pc + 3
    448389            opname = inst[0]
    449             if self.hasjrel.has_elt(opname):
     390            if opname in self.hasjrel:
    450391                oparg = inst[1]
    451392                offset = begin[oparg] - pc
    452393                insts[i] = opname, offset
    453             elif self.hasjabs.has_elt(opname):
     394            elif opname in self.hasjabs:
    454395                insts[i] = opname, begin[inst[1]]
    455396        self.stage = FLAT
    456397
    457     hasjrel = misc.Set()
     398    hasjrel = set()
    458399    for i in dis.hasjrel:
    459400        hasjrel.add(dis.opname[i])
    460     hasjabs = misc.Set()
     401    hasjabs = set()
    461402    for i in dis.hasjabs:
    462403        hasjabs.add(dis.opname[i])
     
    745686        'POP_TOP': -1,
    746687        'DUP_TOP': 1,
    747         'LIST_APPEND': -2,
     688        'LIST_APPEND': -1,
     689        'SET_ADD': -1,
     690        'MAP_ADD': -2,
    748691        'SLICE+1': -1,
    749692        'SLICE+2': -1,
     
    794737    def BUILD_LIST(self, count):
    795738        return -count+1
     739    def BUILD_SET(self, count):
     740        return -count+1
    796741    def CALL_FUNCTION(self, argc):
    797742        hi, lo = divmod(argc, 256)
Note: See TracChangeset for help on using the changeset viewer.