[Python-checkins] CVS: python/dist/src/Tools/compiler/compiler pycodegen.py,1.26,1.27 pyassem.py,1.13,1.14 misc.py,1.6,1.7

Jeremy Hylton python-dev@python.org
Sun, 5 Nov 2000 19:43:14 -0800


Update of /cvsroot/python/python/dist/src/Tools/compiler/compiler
In directory slayer.i.sourceforge.net:/tmp/cvs-serv19430

Modified Files:
	pycodegen.py pyassem.py misc.py 
Log Message:
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.





Index: pycodegen.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Tools/compiler/compiler/pycodegen.py,v
retrieving revision 1.26
retrieving revision 1.27
diff -C2 -r1.26 -r1.27
*** pycodegen.py	2000/10/13 21:58:13	1.26
--- pycodegen.py	2000/11/06 03:43:11	1.27
***************
*** 161,165 ****
          for default in node.defaults:
              self.visit(default)
!         self.emit('LOAD_CONST', gen.getCode())
          self.emit('MAKE_FUNCTION', len(node.defaults))
  
--- 161,165 ----
          for default in node.defaults:
              self.visit(default)
!         self.emit('LOAD_CONST', gen)
          self.emit('MAKE_FUNCTION', len(node.defaults))
  
***************
*** 196,200 ****
              self.visit(suite)
              self.emit('JUMP_FORWARD', end)
!             self.nextBlock(nextTest)
              self.emit('POP_TOP')
          if node.else_:
--- 196,200 ----
              self.visit(suite)
              self.emit('JUMP_FORWARD', end)
!             self.startBlock(nextTest)
              self.emit('POP_TOP')
          if node.else_:
***************
*** 244,251 ****
          self.set_lineno(node)
          self.emit('FOR_LOOP', anchor)
          self.visit(node.assign)
          self.visit(node.body)
          self.emit('JUMP_ABSOLUTE', start)
!         self.nextBlock(anchor)
          self.emit('POP_BLOCK')
          if node.else_:
--- 244,252 ----
          self.set_lineno(node)
          self.emit('FOR_LOOP', anchor)
+         self.nextBlock()
          self.visit(node.assign)
          self.visit(node.body)
          self.emit('JUMP_ABSOLUTE', start)
!         self.startBlock(anchor)
          self.emit('POP_BLOCK')
          if node.else_:
***************
*** 305,309 ****
              end = self.newBlock()
              self.emit('JUMP_FORWARD', end)
!             self.nextBlock(cleanup)
              self.emit('ROT_TWO')
              self.emit('POP_TOP')
--- 306,310 ----
              end = self.newBlock()
              self.emit('JUMP_FORWARD', end)
!             self.startBlock(cleanup)
              self.emit('ROT_TWO')
              self.emit('POP_TOP')
***************
*** 345,353 ****
                  skip_one = self.newBlock()
                  self.emit('JUMP_FORWARD', skip_one)
!                 self.nextBlock(cont)
                  self.emit('POP_TOP')
                  self.nextBlock(skip_one)
              self.emit('JUMP_ABSOLUTE', start)
!             self.nextBlock(anchor)
          self.delName(append)
          
--- 346,354 ----
                  skip_one = self.newBlock()
                  self.emit('JUMP_FORWARD', skip_one)
!                 self.startBlock(cont)
                  self.emit('POP_TOP')
                  self.nextBlock(skip_one)
              self.emit('JUMP_ABSOLUTE', start)
!             self.startBlock(anchor)
          self.delName(append)
          
***************
*** 364,367 ****
--- 365,369 ----
          self.nextBlock(start)
          self.emit('FOR_LOOP', anchor)
+         self.nextBlock()
          self.visit(node.assign)
          return start, anchor
***************
*** 391,397 ****
          self.emit('JUMP_IF_TRUE', end)
          self.nextBlock()
          self.emit('LOAD_GLOBAL', 'AssertionError')
!         self.visit(node.fail)
!         self.emit('RAISE_VARARGS', 2)
          self.nextBlock(end)
          self.emit('POP_TOP')
--- 393,403 ----
          self.emit('JUMP_IF_TRUE', end)
          self.nextBlock()
+         self.emit('POP_TOP')
          self.emit('LOAD_GLOBAL', 'AssertionError')
!         if node.fail:
!             self.visit(node.fail)
!             self.emit('RAISE_VARARGS', 2)
!         else:
!             self.emit('RAISE_VARARGS', 1)
          self.nextBlock(end)
          self.emit('POP_TOP')
***************
*** 420,427 ****
          self.set_lineno(node)
          self.emit('SETUP_EXCEPT', handlers)
          self.visit(node.body)
          self.emit('POP_BLOCK')
          self.emit('JUMP_FORWARD', lElse)
!         self.nextBlock(handlers)
          
          last = len(node.handlers) - 1
--- 426,434 ----
          self.set_lineno(node)
          self.emit('SETUP_EXCEPT', handlers)
+         self.nextBlock()
          self.visit(node.body)
          self.emit('POP_BLOCK')
          self.emit('JUMP_FORWARD', lElse)
!         self.startBlock(handlers)
          
          last = len(node.handlers) - 1
***************
*** 447,450 ****
--- 454,459 ----
              if expr:
                  self.nextBlock(next)
+             else:
+                 self.nextBlock()
              self.emit('POP_TOP')
          self.emit('END_FINALLY')
***************
*** 458,461 ****
--- 467,471 ----
          self.set_lineno(node)
          self.emit('SETUP_FINALLY', final)
+         self.nextBlock()
          self.visit(node.body)
          self.emit('POP_BLOCK')

Index: pyassem.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Tools/compiler/compiler/pyassem.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -r1.13 -r1.14
*** pyassem.py	2000/10/13 21:58:13	1.13
--- pyassem.py	2000/11/06 03:43:11	1.14
***************
*** 4,11 ****
--- 4,19 ----
  import new
  import string
+ import sys
  import types
  
  from compiler import misc
  
+ def xxx_sort(l):
+     l = l[:]
+     def sorter(a, b):
+         return cmp(a.bid, b.bid)
+     l.sort(sorter)
+     return l
+ 
  class FlowGraph:
      def __init__(self):
***************
*** 17,27 ****
  
      def startBlock(self, block):
          self.current = block
  
!     def nextBlock(self, block=None):
!         if block is None:
!             block = self.newBlock()
          # XXX think we need to specify when there is implicit transfer
!         # from one block to the next
          #
          # I think this strategy works: each block has a child
--- 25,40 ----
  
      def startBlock(self, block):
+         if self._debug:
+             if self.current:
+                 print "end", repr(self.current)
+                 print "   ", self.current.get_children()
+             print repr(block)
          self.current = block
  
!     def nextBlock(self, block=None, force=0):
          # 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
***************
*** 31,34 ****
--- 44,57 ----
          # 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 incorrect to add an implicit
+         # transfer to the block graph.  The current code requires
+         # these edges to get the blocks emitted in the right order,
+         # however. :-(  If a client needs to remove these edges, call
+         # pruneEdges().
+         
          self.current.addNext(block)
          self.startBlock(block)
***************
*** 42,52 ****
          self.startBlock(self.exit)
  
      def emit(self, *inst):
!         # XXX should jump instructions implicitly call nextBlock?
          if inst[0] == 'RETURN_VALUE':
              self.current.addOutEdge(self.exit)
          self.current.emit(inst)
  
!     def getBlocks(self):
          """Return the blocks in reverse postorder
  
--- 65,86 ----
          self.startBlock(self.exit)
  
+     _debug = 0
+ 
+     def _enable_debug(self):
+         self._debug = 1
+ 
+     def _disable_debug(self):
+         self._debug = 0
+ 
      def emit(self, *inst):
!         if self._debug:
!             print "\t", inst
          if inst[0] == 'RETURN_VALUE':
              self.current.addOutEdge(self.exit)
+         if len(inst) == 2 and isinstance(inst[1], Block):
+             self.current.addOutEdge(inst[1])
          self.current.emit(inst)
  
!     def getBlocksInOrder(self):
          """Return the blocks in reverse postorder
  
***************
*** 65,75 ****
          if not self.exit in order:
              order.append(self.exit)
          return order
  
  def dfs_postorder(b, seen):
      """Depth-first search of tree rooted at b, return in postorder"""
      order = []
      seen[b] = b
!     for c in b.children():
          if seen.has_key(c):
              continue
--- 99,153 ----
          if not self.exit in order:
              order.append(self.exit)
+ 
+ ##        for b in order:
+ ##            print repr(b)
+ ##            print "\t", b.get_children()
+ ##            print b
+ ##            print
+             
          return order
  
+     def getBlocks(self):
+         return self.blocks.elements()
+ 
+     def getRoot(self):
+         """Return nodes appropriate for use with dominator"""
+         return self.entry
+     
+     def getContainedGraphs(self):
+         l = []
+         for b in self.getBlocks():
+             l.extend(b.getContainedGraphs())
+         return l
+ 
+     _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS',
+                         'JUMP_ABSOLUTE', 'JUMP_FORWARD')
+ 
+     def pruneNext(self):
+         """Remove bogus edge for unconditional transfers
+ 
+         Each block has a next edge that accounts for implicit control
+         transfers, e.g. from a JUMP_IF_FALSE to the block that will be
+         executed if the test is true.
+ 
+         These edges must remain for the current assembler code to
+         work. If they are removed, the dfs_postorder gets things in
+         weird orders.  However, they shouldn't be there for other
+         purposes, e.g. conversion to SSA form.  This method will
+         remove the next edge when it follows an unconditional control
+         transfer.
+         """
+         try:
+             op, arg = self.insts[-1]
+         except (IndexError, TypeError):
+             return
+         if op in self._uncond_transfer:
+             self.next = []
+ 
  def dfs_postorder(b, seen):
      """Depth-first search of tree rooted at b, return in postorder"""
      order = []
      seen[b] = b
!     for c in b.get_children():
          if seen.has_key(c):
              continue
***************
*** 92,99 ****
      def __repr__(self):
          if self.label:
!             return "<block %s id=%d len=%d>" % (self.label, self.bid,
!                                                 len(self.insts)) 
          else:
!             return "<block id=%d len=%d>" % (self.bid, len(self.insts))
  
      def __str__(self):
--- 170,176 ----
      def __repr__(self):
          if self.label:
!             return "<block %s id=%d>" % (self.label, self.bid)
          else:
!             return "<block id=%d>" % (self.bid)
  
      def __str__(self):
***************
*** 121,127 ****
          assert len(self.next) == 1, map(str, self.next)
  
!     def children(self):
          return self.outEdges.elements() + self.next
  
  # flags for code objects
  CO_OPTIMIZED = 0x0001
--- 198,221 ----
          assert len(self.next) == 1, map(str, self.next)
  
!     def get_children(self):
!         if self.next and self.next[0] in self.outEdges:
!             self.outEdges.remove(self.next[0])
          return self.outEdges.elements() + self.next
  
+     def getContainedGraphs(self):
+         """Return all graphs contained within this block.
+ 
+         For example, a MAKE_FUNCTION block will contain a reference to
+         the graph for the function body.
+         """
+         contained = []
+         for inst in self.insts:
+             if len(inst) == 1:
+                 continue
+             op = inst[1]
+             if hasattr(op, 'graph'):
+                 contained.append(op.graph)
+         return contained
+ 
  # flags for code objects
  CO_OPTIMIZED = 0x0001
***************
*** 205,209 ****
          begin = {}
          end = {}
!         for b in self.getBlocks():
              begin[b] = pc
              for inst in b.getInstructions():
--- 299,303 ----
          begin = {}
          end = {}
!         for b in self.getBlocksInOrder():
              begin[b] = pc
              for inst in b.getInstructions():
***************
*** 275,278 ****
--- 369,374 ----
      _converters = {}
      def _convert_LOAD_CONST(self, arg):
+         if hasattr(arg, 'getCode'):
+             arg = arg.getCode()
          return self._lookupName(arg, self.consts)
  

Index: misc.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Tools/compiler/compiler/misc.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -r1.6 -r1.7
*** misc.py	2000/03/16 20:02:38	1.6
--- misc.py	2000/11/06 03:43:11	1.7
***************
*** 15,18 ****
--- 15,20 ----
      def __len__(self):
          return len(self.elts)
+     def __contains__(self, elt):
+         return self.elts.has_key(elt)
      def add(self, elt):
          self.elts[elt] = elt
***************
*** 23,26 ****
--- 25,32 ----
      def remove(self, elt):
          del self.elts[elt]
+     def copy(self):
+         c = Set()
+         c.elts.update(self.elts)
+         return c
  
  class Stack: