[pypy-svn] r33938 - in pypy/dist/pypy/translator: cli jvm oosupport

niko at codespeak.net niko at codespeak.net
Tue Oct 31 10:59:58 CET 2006


Author: niko
Date: Tue Oct 31 10:59:57 2006
New Revision: 33938

Modified:
   pypy/dist/pypy/translator/cli/function.py
   pypy/dist/pypy/translator/jvm/conftest.py
   pypy/dist/pypy/translator/jvm/database.py
   pypy/dist/pypy/translator/jvm/generator.py
   pypy/dist/pypy/translator/jvm/genjvm.py
   pypy/dist/pypy/translator/jvm/node.py
   pypy/dist/pypy/translator/jvm/opcodes.py
   pypy/dist/pypy/translator/jvm/option.py
   pypy/dist/pypy/translator/jvm/typesystem.py
   pypy/dist/pypy/translator/oosupport/function.py
   pypy/dist/pypy/translator/oosupport/metavm.py
Log:
Progress towards running jvm tests:
	refactor static calls
	implement a few opcodes in JVM, etc



Modified: pypy/dist/pypy/translator/cli/function.py
==============================================================================
--- pypy/dist/pypy/translator/cli/function.py	(original)
+++ pypy/dist/pypy/translator/cli/function.py	Tue Oct 31 10:59:57 2006
@@ -17,13 +17,20 @@
 from pypy.translator.cli.support import log
 
 class Function(OOFunction, Node, Generator):
-    def _create_generator(self):
+
+    def __init__(self, *args, **kwargs):
+        OOFunction.__init__(self, *args, **kwargs)
+        self._set_args()
+        self._set_locals()
+                 
+    def _create_generator(self, ilasm):
         return self # Function implements the Generator interface
 
     def begin_try(self):
         self.ilasm.begin_try()
 
-    def end_try(self):
+    def end_try(self, target_label):
+        self.ilasm.leave(target_label)
         self.ilasm.end_try()
 
     def begin_catch(self, llexitcase):
@@ -165,3 +172,9 @@
 
     def isinstance(self, class_name):
         self.ilasm.opcode('isinst', class_name)
+
+    def branch_unconditionally(self, target_label):
+        self.ilasm.branch(target_label)
+
+    def branch_conditionally(self, cond, target_label):
+        self.ilasm.branch_if(cond, target_label)

Modified: pypy/dist/pypy/translator/jvm/conftest.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/conftest.py	(original)
+++ pypy/dist/pypy/translator/jvm/conftest.py	Tue Oct 31 10:59:57 2006
@@ -9,15 +9,8 @@
            help='Define the java compiler to use'),
     Option('--java', action='store', dest='java', default='java',
            help='Define the java executable to use'),
-##    Option('--view', action='store_true', dest='view', default=False,
-##           help='View the graphs before they are generated'),
-#    Option('--wd', action='store_true', dest='wd', default=False,
-#           help='Output to current directory instead of /tmp'),
     Option('--noassemble', action='store_true', dest="noasm", default=False,
            help="don't assemble jasmin files"),
-#    Option('--norun', action='store_true', dest="norun", default=False,
-#           help="don't run the compiled executable"),
     Option('--package', action='store', dest='package', default='pypy',
            help='Package to output generated classes into')
-    #Option('--opt', action='XXX', dest='YYY', default=DEF, help='HELP')
     )

Modified: pypy/dist/pypy/translator/jvm/database.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/database.py	(original)
+++ pypy/dist/pypy/translator/jvm/database.py	Tue Oct 31 10:59:57 2006
@@ -5,20 +5,25 @@
 """
 from cStringIO import StringIO
 from pypy.rpython.ootypesystem import ootype
-from pypy.translator.jvm.typesystem import jvm_method_desc
+from pypy.translator.jvm.typesystem import jvm_method_desc, ootype_to_jvm
 from pypy.translator.jvm import node
 import pypy.translator.jvm.generator as jvmgen
 import pypy.translator.jvm.typesystem as jvmtypes
 
 class Database:
-    def __init__(self, ts):
+    def __init__(self, genoo):
         # Public attributes:
-        self.type_system = ts
-
+        self.genoo = genoo
+        
         # Private attributes:
         self._classes = {} # Maps ootype class objects to node.Class objects
         self._counter = 0  # Used to create unique names
-        self._pending = [] # Worklist
+        self._functions = {}     # graph -> jvmgen.Method
+
+        self._function_names = {} # graph --> function_name
+
+        self._pending_nodes = set()  # Worklist
+        self._rendered_nodes = set()
 
     def _make_unique_name(self, nm):
         cnt = self._counter
@@ -40,7 +45,7 @@
         for fieldnm, (fieldty, fielddef) in ooclass._fields.iteritems():
             if ftype is ootype.Void: continue
             fieldnm = self._make_unique_name(fieldnm)
-            fieldty = self.type_system.ootype_to_jvm(ftype)
+            fieldty = self.lltype_to_cts(ftype)
             clobj.add_field(fieldty, fieldnm) # TODO --- fielddef??
             
         # Add methods:
@@ -56,35 +61,92 @@
                 args =  m_meth.graph.getargs()
                 SELF = args[0].concretetype
                 if not ootype.isSubclass(ooclass, SELF): continue
-                mobj = _method_for_graph(clobj, False, mimpl.graph)
+                mobj = _function_for_graph(
+                    clobj, mimpl.name, False, mimpl.graph)
                 clobj.add_method(mobj)
 
         return clobj
-    
-    def _method_for_graph(self, classobj, is_static, graph):
+
+    def _function_for_graph(self, classobj, funcnm, is_static, graph):
         
         """
-        Creates a node.Function object for a particular graph.  Adds the
-        method to 'classobj', which should be a node.Class object.
+        Creates a node.Function object for a particular graph.  Adds
+        the method to 'classobj', which should be a node.Class object.
         """
-
-        # Build up a func object 
-        func_name = self._make_unique_name(graph.name)
         argtypes = [arg.concretetype for arg in graph.getargs()
                     if arg.concretetype is not ootype.Void]
-        jargtypes = [self.type_system.ootype_to_jvm(argty)
-                     for argty in argtypes]
+        jargtypes = [self.lltype_to_cts(argty) for argty in argtypes]
         rettype = graph.getreturnvar().concretetype
-        jrettype = self.type_system.ootype_to_jvm(rettype)
-        funcobj = self._translated[cachekey] = node.Function(
-            classobj, func_name, jargtypes, jrettype, graph, is_static)
+        jrettype = self.lltype_to_cts(rettype)
+        funcobj = node.Function(
+            self, classobj, funcnm, jargtypes, jrettype, graph, is_static)
         return funcobj
 
     def pending_node(self, node):
-        self._pending.append(node)
+        self._pending_nodes.add(node)
+        
+    def pending_function(self, graph):
+        """
+        This is invoked when a standalone function is to be compiled.
+        It creates a class named after the function with a single
+        method, invoke().  This class is added to the worklist.
+        Returns a jvmgen.Method object that allows this function to be
+        invoked.
+        """
+        if graph in self._functions:
+            return self._functions[graph]
+        classnm = self._make_unique_name(graph.name)
+        classobj = node.Class(classnm)
+        funcobj = self._function_for_graph(classobj, "invoke", True, graph)
+        classobj.add_method(funcobj)
+        self.pending_node(classobj)
+        res = self._functions[graph] = funcobj.method()
+        return res
 
     def len_pending(self):
-        return len(self._pending)
+        return len(self._pending_nodes)
 
     def pop(self):
-        return self._pending.pop()
+        return self._pending_nodes.pop()
+
+    # Type translation functions
+
+    def escape_name(self, nm):
+        # invoked by oosupport/function.py; our names don't need escaping?
+        return nm
+
+    def llvar_to_cts(self, llv):
+        """ Returns a tuple (JvmType, str) with the translated type
+        and name of the given variable"""
+        return self.lltype_to_cts(llv.concretetype), llv.name
+
+    def lltype_to_cts(self, oot):
+        """ Returns an instance of JvmType corresponding to
+        the given OOType"""
+
+        # Check the easy cases
+        if oot in ootype_to_jvm:
+            return ootype_to_jvm[oot]
+
+        # Now handle the harder ones
+        if isinstance(oot, ootype.Ptr) and isinstance(t.TO, ootype.OpaqueType):
+            return jObject
+        if isinstance(oot, ootype.Instance):
+            return XXX
+        if isinstance(oot, ootype.Record):
+            return XXX
+        if isinstance(oot, ootype.StaticMethod):
+            return XXX
+
+        # Uh-oh
+        unhandled_case    
+
+    # Invoked by genoo:
+    #   I am not sure that we need them
+    
+    def record_function(self, graph, name):
+        self._function_names[graph] = name
+
+    def graph_name(self, graph):
+        # XXX: graph name are not guaranteed to be unique
+        return self._function_names.get(graph, None)

Modified: pypy/dist/pypy/translator/jvm/generator.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/generator.py	(original)
+++ pypy/dist/pypy/translator/jvm/generator.py	Tue Oct 31 10:59:57 2006
@@ -2,6 +2,7 @@
 from pypy.objspace.flow import model as flowmodel
 from pypy.translator.oosupport.metavm import Generator
 from pypy.translator.jvm.typesystem import JvmType
+from pypy.rpython.ootypesystem import ootype
 
 # ___________________________________________________________________________
 # JVM Opcode Flags:
@@ -10,10 +11,10 @@
 #   assertions
 
 NOFLAGS   = 0
-BRANCH    = 1   # Opcode is a branching opcode (implies a label argument)
-INTARG    = 2   # Opcode has an integer argument
-CONSTSPEC = 6   # Opcode has specialized variants (implies INTARG)
-INVOKE    = 8   # Opcode is some kind of method invocation
+BRANCH    = 1          # Opcode is a branching opcode (implies a label argument)
+INVOKE    = 2          # Opcode is some kind of method invocation
+CONST5    = 4          # Opcode is specialized for int arguments from -1 to 5
+CONST3    = 4          # Opcode is specialized for int arguments from 0 to 3
 
 # ___________________________________________________________________________
 # JVM Opcodes:
@@ -29,6 +30,25 @@
         self.flags = flags
         self.jvmstr = jvmstr
         
+    def specialize_opcode(self, args):
+        """ Process the argument list according to the various flags.
+        Returns a tuple (OPCODE, ARGS) where OPCODE is a string representing
+        the new opcode, and ARGS is a list of arguments or empty tuple """
+
+        if self.flags & CONST5: 
+            assert len(args) == 1
+            if args[0] == -1:
+                return self.jvmstr + "_m1", ()
+            elif args[0] >= 0 and args[0] <= 5:
+                return self.jvmstr + "_" + str(args[0]), ()
+
+        if self.flags & CONST3: 
+            assert len(args) == 1
+            if args[0] >= 0 and args[0] <= 3:
+                return self.jvmstr + "_" + str(args[0]), ()
+
+        return self.jvmstr, args
+        
 class OpcodeFamily(object):
     """
     Many opcodes in JVM have variants that depend on the type of the
@@ -59,17 +79,32 @@
         'argtype', a JvmType object. """
 
         # These are always true:
-        if self[0] == 'L': return self._o("A")   # Objects
-        if self[0] == '[': return self._o("A")   # Arrays
-        if self == 'I':    return self._o("I")   # Integers
-        if self == 'J':    return self._o("L")   # Integers
-        if self == 'D':    return self._o("D")   # Doubles
-        if self == 'C':    return self._o("C")   # Characters
-        if self == 'B':    return self._o("B")   # Bytes
-        if self == 'V':    return self._o("")    # Void [used by RETURN]
+        if argtype[0] == 'L': return self._o("a")   # Objects
+        if argtype[0] == '[': return self._o("a")   # Arrays
+        if argtype == 'I':    return self._o("i")   # Integers
+        if argtype == 'J':    return self._o("l")   # Integers
+        if argtype == 'D':    return self._o("d")   # Doubles
+        if argtype == 'V':    return self._o("")    # Void [used by RETURN]
+
+        # Chars/Bytes/Booleans are normally represented as ints
+        # in the JVM, but some opcodes are different.  They use a
+        # different OpcodeFamily (see ArrayOpcodeFamily for ex)
+        if argtype == 'C':    return self._o("i")   # Characters
+        if argtype == 'B':    return self._o("i")   # Bytes
+        if argtype == 'Z':    return self._o("i")   # Boolean
 
-        # TODO --- extend?  etc
-        unimplemented
+        assert False, "Unknown argtype=%s" % repr(argtype)
+        raise NotImplementedError
+
+class ArrayOpcodeFamily(OpcodeFamily):
+    """ Opcode family specialized for array access instr """
+    def for_type(self, argtype):
+        if argtype == 'J':    return self._o("l")   # Integers
+        if argtype == 'D':    return self._o("d")   # Doubles
+        if argtype == 'C':    return self._o("c")   # Characters
+        if argtype == 'B':    return self._o("b")   # Bytes
+        if argtype == 'Z':    return self._o("b")   # Boolean (access as bytes)
+        return OpcodeFamily.for_type(self, argtype)
 
 # Define the opcodes for IFNE, IFEQ, IFLT, IF_ICMPLT, etc.  The IFxx
 # variants compare a single integer arg against 0, and the IF_ICMPxx
@@ -91,10 +126,10 @@
 
 # Other opcodes
 GOTO =      Opcode(BRANCH, 'goto')
-ICONST =    Opcode(CONSTSPEC, 'iconst')
+ICONST =    Opcode(CONST5, 'iconst')
 DCONST_0 =  Opcode(NOFLAGS, 'dconst_0')
-DCONST_1 =  Opcode(NOFLAGS, 'dconst_1')
-LCONST_0 =  Opcode(NOFLAGS, 'lconst_0')
+DCONST_1 =  Opcode(NOFLAGS, 'dconst_0')
+LCONST_0 =  Opcode(NOFLAGS, 'lconst_1')
 LCONST_1 =  Opcode(NOFLAGS, 'lconst_1')
 GETFIELD =  Opcode(NOFLAGS, 'getfield')
 PUTFIELD =  Opcode(NOFLAGS, 'putfield')
@@ -139,10 +174,9 @@
 LSHL =      Opcode(NOFLAGS, 'lshl')
 LSHR =      Opcode(NOFLAGS, 'lshr')
 LUSHR =     Opcode(NOFLAGS, 'lushr')
-
 # Loading/storing local variables
-LOAD =      OpcodeFamily(INTARG, "load")
-STORE =     OpcodeFamily(INTARG, "store")
+LOAD =      OpcodeFamily(CONST3, "load")
+STORE =     OpcodeFamily(CONST3, "store")
 RETURN =    OpcodeFamily(NOFLAGS, "return")
 
 # Loading/storing from arrays
@@ -152,8 +186,8 @@
 #   Also: here I break from convention by naming the objects ARRLOAD
 #   rather than ALOAD, even though the suffix is 'aload'.  This is to
 #   avoid confusion with the ALOAD opcode.
-ARRLOAD =      OpcodeFamily(NOFLAGS, "aload")
-ARRSTORE =     OpcodeFamily(NOFLAGS, "astore")
+ARRLOAD =      ArrayOpcodeFamily(NOFLAGS, "aload")
+ARRSTORE =     ArrayOpcodeFamily(NOFLAGS, "astore")
 
 # ___________________________________________________________________________
 # Helper Method Information
@@ -181,19 +215,19 @@
 PYPYARRAYTOLIST =       Method('pypy.PyPy', 'array_to_list',
                                '([Ljava/lang/Object;)Ljava/util/List;')
 PYPYSTRTOINT =          Method('pypy.PyPy', 'str_to_int',
-                               '([Ljava/lang/String;)I')
+                               '(Ljava/lang/String;)I')
 PYPYSTRTOUINT =         Method('pypy.PyPy', 'str_to_uint',
-                               '([Ljava/lang/String;)I')
+                               '(Ljava/lang/String;)I')
 PYPYSTRTOLONG =         Method('pypy.PyPy', 'str_to_long',
-                               '([Ljava/lang/String;)J')
+                               '(Ljava/lang/String;)J')
 PYPYSTRTOULONG =        Method('pypy.PyPy', 'str_to_ulong',
-                               '([Ljava/lang/String;)J')
+                               '(Ljava/lang/String;)J')
 PYPYSTRTOBOOL =         Method('pypy.PyPy', 'str_to_bool',
-                               '([Ljava/lang/String;)B')
+                               '(Ljava/lang/String;)B')
 PYPYSTRTODOUBLE =       Method('pypy.PyPy', 'str_to_double',
-                               '([Ljava/lang/String;)D')
+                               '(Ljava/lang/String;)D')
 PYPYSTRTOCHAR =         Method('pypy.PyPy', 'str_to_char',
-                               '([Ljava/lang/String;)C')
+                               '(Ljava/lang/String;)C')
 
 class JVMGenerator(Generator):
 
@@ -204,8 +238,9 @@
     search for the string 'unimplemented' to find the methods that
     must be overloaded. """
 
-    def __init__(self, type_system):
-        self.type_system = type_system
+    def __init__(self, db):
+        self.db = db
+        self.label_counter = 0
 
     # __________________________________________________________________
     # JVM specific methods to be overloaded by a subclass
@@ -318,9 +353,9 @@
         identifier.
 
         'mark' --- if True, then also calls self.mark() with the new lbl """
-        labelnum = len(self._labels)
-        self._labels.append(desc)
-        res = ('Label', labelnum)
+        labelnum = self.label_counter
+        self.label_counter += 1
+        res = ('Label', labelnum, desc)
         if mark:
             self.mark(res)
         return res
@@ -394,7 +429,7 @@
 
     def _var_data(self, v):
         # Determine java type:
-        jty = self.type_system.ootype_to_jvm(v.concretetype)
+        jty = self.db.lltype_to_cts(v.concretetype)
         # Determine index in stack frame slots:
         #   note that arguments and locals can be treated the same here
         if v in self.local_vars:
@@ -404,29 +439,30 @@
             self.next_offset += jty.type_width()
         return jty, idx
         
-    def load(self, v):
-        if isinstance(v, flowmodel.Variable):
-            jty, idx = _var_data(v)
+    def load(self, value):
+        if isinstance(value, flowmodel.Variable):
+            jty, idx = self._var_data(value)
             return self.load_jvm_var(jty, idx)
 
-        if isinstance(v, flowmodel.Constant):
+        if isinstance(value, flowmodel.Constant):
             # TODO: Refactor and complete this code?  Maybe more like cli code?
+            # Knowledge of ootype SHOULD be constrainted to type system
+            TYPE = value.concretetype
             if TYPE is ootype.Void:
-                pass
+                return
             elif TYPE is ootype.Bool:
-                self._instr(ICONST, int(value))
+                return self._instr(ICONST, int(value.value))
             elif TYPE is ootype.Char or TYPE is ootype.UniChar:
-                self._instr(ICONST, ord(value))
-            elif isinstance(value, CDefinedIntSymbolic):
-                self._instr(ICONST, DEFINED_INT_SYMBOLICS[value.expr])
+                return self._instr(ICONST, ord(value.value))
             elif TYPE in (ootype.Signed, ootype.Unsigned):
-                self._instr(ICONST, value) # handle Unsigned better!
+                return self._instr(ICONST, value.value) # handle Unsigned better
             
-        raise Exception('Unexpected type for v in load(): '+v)
+        raise Exception('Unexpected type for v in load(): '+
+                        repr(value.concretetype) + " v=" + repr(value))
 
     def store(self, v):
         if isinstance(v, flowmodel.Variable):
-            jty, idx = _var_data(v)
+            jty, idx = self._var_data(v)
             return self.store_jvm_var(jty, idx)
         raise Exception('Unexpected type for v in store(): '+v)
 
@@ -439,12 +475,25 @@
     def downcast(self, type):
         self._instr(CHECKCAST, type)
 
+    def branch_unconditionally(self, target_label):
+        self._instr(GOTO, target_label)
+
+    def branch_conditionally(self, cond, target_label):
+        if cond:
+            self._instr(IFNE, target_label)
+        else:
+            self._instr(IFEQ, target_label)
+
+    def call_graph(self, graph):
+        mthd = self.db.pending_function(graph)
+        mthd.invoke(self)
+
+    def call_primitive(self, graph):
+        raise NotImplementedError
+
     # __________________________________________________________________
     # Methods invoked directly by strings in jvm/opcode.py
 
-    def goto(self, lbl):
-        self._instr(GOTO, lbl)
-
     def throw(self):
         """ Throw the object from top of the stack as an exception """
         self._instr(ATHROW)
@@ -540,7 +589,8 @@
         
 class JasminGenerator(JVMGenerator):
 
-    def __init__(self, outdir, package):
+    def __init__(self, db, outdir, package):
+        JVMGenerator.__init__(self, db)
         self.outdir = outdir
 
     def begin_class(self, classnm):
@@ -587,20 +637,16 @@
 
     def mark(self, lbl):
         """ Marks the point that a label indicates. """
-        _, lblnm = lbl
+        _, lblnum, lbldesc = lbl
         assert _ == "Label"
-        self.out.write('  %s:\n' % lblnm)
+        self.out.write('  %s_%s:\n' % (lbldesc, lblnum))
 
     def _instr(self, opcode, *args):
-        jvmstr = opcode.jvmstr
-        
-        # Hack: this should be somewhere else, just not sure where yet
-        if opcode.flags & CONSTSPEC:
-            if args[0] == -1:
-                jvmstr += "_m1"
-            elif args[0] >= 0 and args[0] <= 5:
-                jvmstr += "_%d" % args[0]
-            
+        jvmstr, args = opcode.specialize_opcode(args)
+        if opcode.flags & BRANCH:
+            assert len(args) == 1
+            _, lblnum, lbldesc = args[0]
+            args = ('%s_%s' % (lbldesc, lblnum),)
         self.out.write('    %s %s\n' % (
             jvmstr, " ".join([str(s) for s in args])))
 

Modified: pypy/dist/pypy/translator/jvm/genjvm.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/genjvm.py	(original)
+++ pypy/dist/pypy/translator/jvm/genjvm.py	Tue Oct 31 10:59:57 2006
@@ -7,13 +7,14 @@
 import py
 from pypy.tool.udir import udir
 from pypy.translator.translator import TranslationContext
+from pypy.translator.oosupport.genoo import GenOO
 
 from pypy.translator.jvm.generator import JasminGenerator
 from pypy.translator.jvm.option import getoption
 from pypy.translator.jvm.database import Database
-from pypy.translator.jvm.typesystem import JvmTypeSystem
 from pypy.translator.jvm.log import log
-from pypy.translator.jvm.node import EntryPoint
+from pypy.translator.jvm.node import EntryPoint, Function
+from pypy.translator.jvm.opcodes import opcodes
 
 class JvmError(Exception):
     """ Indicates an error occurred in the JVM runtime """
@@ -96,54 +97,43 @@
     if getoption('view'): t.view()
     if getoption('wd'): tmpdir = py.path.local('.')
     else: tmpdir = udir
-    jvm = GenJvm(tmpdir, t, entrypoint=EntryPoint(main_graph, True))
+    jvm = GenJvm(tmpdir, t, EntryPoint(main_graph, True))
     return jvm.generate_source()
 
-class GenJvm(object):
+class GenJvm(GenOO):
 
     """ Master object which guides the JVM backend along.  To use,
     create with appropriate parameters and then invoke
     generate_source().  *You can not use one of these objects more than
     once.* """
+
+    TypeSystem = lambda X, db: db # TypeSystem and Database are the same object 
+    Function = Function
+    Database = Database
+    opcodes = opcodes
+    log = log
     
-    def __init__(self, tmpdir, translator, entrypoint=None):
+    def __init__(self, tmpdir, translator, entrypoint):
         """
         'tmpdir' --- where the generated files will go.  In fact, we will
         put our binaries into the directory pypy/jvm
         'translator' --- a TranslationContext object
         'entrypoint' --- if supplied, an object with a render method
         """
+        GenOO.__init__(self, tmpdir, translator, entrypoint)
         self.jvmsrc = JvmGeneratedSource(tmpdir, getoption('package'))
-        self.type_system = JvmTypeSystem()
-        self.db = Database(self.type_system)
-        if entrypoint:
-            self.db.pending_node(entrypoint)
-        else:
-            self.db.pending_node(EntryPoint(translator.graphs[0], False))
 
     def generate_source(self):
         """ Creates the sources, and returns a JvmGeneratedSource object
         for manipulating them """
-        generator = self._create_generator()
-
-        # Drain worklist
-        n = 0
-        while self.db.len_pending():
-            node = self.db.pop()
-            node.render(self.db, generator)
-            n+=1
-            if (n%100) == 0:
-                total = len(self.db.len_pending()) + n
-                log.graphs('Rendered %d/%d (approx. %.2f%%)' %\
-                           (n, total, n*100.0/total))
-
-        # Return the source object once we have finished
+        GenOO.generate_source(self)
         return self.jvmsrc
 
-    def _create_generator(self):
+    def create_assembler(self):
         """ Creates and returns a Generator object according to the
         configuration.  Right now, however, there is only one kind of
         generator: JasminGenerator """
-        return JasminGenerator(self.jvmsrc.javadir, self.jvmsrc.package)
+        return JasminGenerator(
+            self.db, self.jvmsrc.javadir, self.jvmsrc.package)
         
         

Modified: pypy/dist/pypy/translator/jvm/node.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/node.py	(original)
+++ pypy/dist/pypy/translator/jvm/node.py	Tue Oct 31 10:59:57 2006
@@ -7,13 +7,14 @@
 from pypy.rpython.lltypesystem import lltype
 from pypy.rpython.ootypesystem import ootype
 from pypy.translator.jvm.typesystem import jStringArray, jVoid, jThrowable
-from pypy.translator.jvm.typesystem import jvm_for_class
-import pypy.translator.jvm.generator as jvmgen
+from pypy.translator.jvm.typesystem import jvm_for_class, jvm_method_desc
 from pypy.translator.jvm.opcodes import opcodes
+from pypy.translator.oosupport.function import Function as OOFunction
+import pypy.translator.jvm.generator as jvmgen
 
 class Node(object):
-    def render(self, db, generator):
-        unimplemented
+    def set_db(self, db):
+        self.db = db
 
 class EntryPoint(Node):
 
@@ -57,9 +58,10 @@
         ootype.Char:jvmgen.PYPYSTRTOCHAR
         }
 
-    def render(self, db, gen):
+    def render(self, gen):
         gen.begin_class('pypy.Main')
-        gen.begin_function('main', (), [jStringArray], jVoid, static=True)
+        gen.begin_function(
+            'main', (), [jStringArray], jVoid, static=True)
 
         # Handle arguments:
         if self.expand_arguments:
@@ -78,20 +80,17 @@
             gen.emit(jvmgen.PYPYARRAYTOLIST)
 
         # Generate a call to this method
-        gen.emit(db.method_for_graph(self.graph, static=True))
+        gen.emit(self.db.pending_function(self.graph))
         
         gen.end_function()
         gen.end_class()
 
-class Function(object):
+class Function(OOFunction):
     
-    """ Represents a function to be emitted.  *Note* that it is not a
-    descendant of Node: it cannot be entered into the database
-    worklist.  This is because in Java, all functions occur w/in a
-    class: therefore classes as a whole must be placed on the
-    worklist. """
+    """ Represents a function to be emitted. """
     
-    def __init__(self, classobj, name, jargtypes, jrettype, graph, is_static):
+    def __init__(self, db, classobj, name, jargtypes,
+                 jrettype, graph, is_static):
         """
         classobj: the Class object this is a part of (even static
         functions have a class)
@@ -101,134 +100,84 @@
         graph: the graph representing the body of the function
         is_static: boolean flag indicate whether func is static (!)
         """
-        self.classnm = classnm
-        self.name = name
-        self.graph = graph
+        OOFunction.__init__(self, db, graph, name, not is_static)
+        self.classnm = classobj.name
         self.jargtypes = jargtypes
         self.jrettype = jrettype
-        self.is_static = is_static
+        self._block_labels = {}
 
     def method(self):
         """ Returns a jvmgen.Method that can invoke this function """
-        if self.is_static: opcode = jvmgen.INVOKESTATIC
+        if not self.is_method: opcode = jvmgen.INVOKESTATIC
         else: opcode = jvmgen.INVOKEVIRTUAL
         mdesc = jvm_method_desc(self.jargtypes, self.jrettype)
-        return jvmgen.Method(classnm, self.func_name, mdesc, opcode=opcode)
-
-    def render_func(self, db, gen):
-        if getattr(self.graph.func, 'suggested_primitive', False):
-            assert False, 'Cannot render a suggested_primitive'
+        return jvmgen.Method(self.classnm, self.name, mdesc, opcode=opcode)
 
+    def begin_render(self):
         # Prepare argument lists for begin_function call
+        lltype_to_cts = self.db.lltype_to_cts
         jargvars = []
         jargtypes = []
         for arg in self.graph.getargs():
             if arg.concretetype is ootype.Void: continue
             jargvars.append(arg)
-            jargtypes.append(db.type_system.ootype_to_jvm(arg.concretetype))
+            jargtypes.append(lltype_to_cts(arg.concretetype))
 
         # Determine return type
-        jrettype = db.type_system.ootype_to_jvm(
-            self.graph.getreturnvar().concretetype)
+        jrettype = lltype_to_cts(self.graph.getreturnvar().concretetype)
+        self.ilasm.begin_function(
+            self.name, jargvars, jargtypes, jrettype, static=not self.is_method)
+
+    def end_render(self):
+        self.ilasm.end_function()
+
+    def _create_generator(self, ilasm):
+        # JVM doesn't distinguish
+        return ilasm
+
+    def _get_block_name(self, block):
+        if block in self._block_labels:
+            return self._block_labels[block]
+        blocklbl = self.ilasm.unique_label('BasicBlock')
+        self._block_labels[block] = blocklbl
+        return blocklbl
+
+    def set_label(self, blocklbl):
+        self.ilasm.mark(blocklbl)
 
-        # Start the function definition
-        gen.begin_function(self.name, jargvars, jargtypes, jrettype,
-                           static=self.is_static)
-
-        # Go through each block and create a label for it; if the
-        # block will be used to catch an exception, add a second label
-        # to catch_labels
-        block_labels = {}
-        #catch_labels = {}
-        for ctr, block in enumerate(graph.iterblocks()):
-            blbl = gen.unique_label('Block_'+ctr)
-            block_labels[block] = blbl
-
-            ## Go through the blocks we may toss exceptions to
-            #if block.exitswitch == flowmodel.c_last_exception:
-            #    for link in block.exits:
-            #        if link.exitcase is None: continue # return
-            #        if link.target not in catch_labels:
-            #            catch_labels[link.target] = gen.unique_label('catch')
-
-        # Iterate through the blocks and generate code for them
-        return_blocks = []
-        for block in graph.iterblocks():
-            
-            # Mark the beginning of the block, render all the ops, and
-            # then mark the end.
-            gen.mark(block_labels[block][0])
-
-            # Determine whether the last oper in this block may raise an exc
-            handle_exc = (block.exitswitch == flowmodel.c_last_exception)
-
-            # Render the operations; create labels for a try/catch
-            # region around the last operation
-            if block.operations:
-                for op in block.operations[:-1]:
-                    self._render_op(op)
-                if handle_exc: trybeglbl = gen.unique_label('try', mark=True)
-                self._render_op(block.operations[-1])
-                if handle_exc: tryendlbl = gen.unique_label('try', mark=True)
-
-            # Handle 'return' blocks: in this case, we return the
-            # variable specified
-            if self._is_return_block(block):
-                return_var = block.inputargs[0]
-                return_ty = ootype_to_jvm(return_var.concretetype)
-                if return_var.concretetype is not Void:
-                    self.load(return_var)
-                gen.return_val(return_ty)
-
-            # Handle 'raise' blocks: in this case, we just throw the
-            # variable specified
-            if self._is_raise_block(block):
-                exc = block.inputargs[1]
-                self.load(exc)
-                gen.throw()
-
-            if handle_exc:
-                # search for the "default" block to be executed when
-                # no exception is raised
-                for link in block.exits:
-                    if link.exitcase is None:
-                        self._copy_link_vars(gen, link)
-                        gen.goto(block_labels[link.target])
-
-                # TODO: proper exception handling; we may not want to
-                # use the same model as CLR
-            else:
-                # no exception handling, determine correct link to follow
-                for link in block.exits:
-                    self._copy_link_vars(gen, link)
-                    target_label = block_labels[link.target]
-                    if link.exitcase is None or link is block.exits[-1]:
-                        gen.goto(target_label)
-                    else:
-                        assert type(link.exitcase is bool)
-                        assert block.exitswitch is not None
-                        gen.load(block.exitswitch)
-                        gen.goto_if_true(target_label)
+    def begin_try(self):
+        self.ilasm.begin_try()
 
-        gen.end_function()
+    def end_try(self):
+        self.ilasm.end_try()
+
+    def begin_catch(self, llexitcase):
+        unimplemented
 
-    def _render_op(self, op):
-        instr_list = opcodes.get(op.opname, None)
-        assert getoption('nostop') or instr_list is not None
-        if instr_list: instr_list.render(self, op)
+    def end_catch(self, llexitcase):
+        unimplemented
+
+    def store_exception_and_link(self, link):
+        unimplemented
+
+    def render_return_block(self, block):
+        return_var = block.inputargs[0]
+        return_ty = self.db.lltype_to_cts(return_var.concretetype)
+        if return_var.concretetype is not ootype.Void:
+            self.ilasm.load(return_var)
+        self.ilasm.return_val(return_ty)
+
+    def render_raise_block(self, block):
+        exc = block.inputargs[1]
+        self.ilasm.load(exc)
+        self.ilasm.throw()
 
-    def _copy_link_vars(self, gen, link):
+    def _setup_link(self, link):
         target = link.target
         for to_load, to_store in zip(link.args, target.inputargs):
-            if to_load.concretetype is not Void:
-                gen.load(to_load)
-                gen.store(to_store)
-                
-    def _is_return_block(self, block):
-        return (not block.exits) and len(block.inputargs) == 1
-
-    def _is_raise_block(self, block):
-        return (not block.exits) and len(block.inputargs) == 2        
+            if to_load.concretetype is not ootype.Void:
+                self.ilasm.load(to_load)
+                self.ilasm.store(to_store)
 
 class Class(Node):
 
@@ -240,9 +189,9 @@
         'name' should be a fully qualified Java class name like
         "java.lang.String"
         """
-        self.name = name
+        self.name = name        # public attribute
         self.fields = []
-        self.methods = {}      # Maps graph -> Function
+        self.methods = []
         self.rendered = False
 
     def jvm_type(self):
@@ -255,24 +204,21 @@
         assert not self.rendered
         self.fields.append((fieldty, fieldnm))
 
-    def has_method_for(self, graph):
-        return graph in self.methods
-        
     def add_method(self, func):
         """ Creates a new method in this class, represented by the
         Function object 'func'.  Must be called before render();
-        intended to be invoked by the database."""
-        assert not self.rendered
-        self.methods[func.graph] = func
+        intended to be invoked by the database.  Note that some of these
+        'methods' may actually represent static functions. """
+        self.methods.append(func)
 
-    def render(self, db, gen):
+    def render(self, gen):
         self.rendered = True
         gen.begin_class(self.name)
 
         for fieldty, fieldnm in self.fields:
             gen.add_field(fieldty, fieldnm)
 
-        for method in self.methods.values():
-            method.render_func(db, gen)
+        for method in self.methods:
+            method.render(gen)
         
-        gen.end_class(self.name)
+        gen.end_class()

Modified: pypy/dist/pypy/translator/jvm/opcodes.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/opcodes.py	(original)
+++ pypy/dist/pypy/translator/jvm/opcodes.py	Tue Oct 31 10:59:57 2006
@@ -6,7 +6,7 @@
 """
 
 from pypy.translator.oosupport.metavm import \
-     PushArg, PushAllArgs, StoreResult, InstructionList, New, DoNothing
+     PushArg, PushAllArgs, StoreResult, InstructionList, New, DoNothing, Call
 import pypy.translator.jvm.generator as jvmgen
 
 def _check_zer(op):
@@ -38,9 +38,9 @@
     #'ooparse_int':              [PushAllArgs, 'call int32 [pypylib]pypy.runtime.Utils::OOParseInt(string, int32)'],
     #'oonewcustomdict':          [NewCustomDict],
     #
-    #'same_as':                  DoNothing,
+    'same_as':                  DoNothing,
     #'hint':                     [PushArg(0), StoreResult],
-    #'direct_call':              [Call],
+    'direct_call':              [Call],
     #'indirect_call':            [IndirectCall],
     #
     #'cast_ptr_to_weakadr':      [PushAllArgs, 'newobj instance void class %s::.ctor(object)' % WEAKREF],
@@ -222,4 +222,7 @@
 for opc in opcodes:
     val = opcodes[opc]
     if not isinstance(val, list):
-        val = [PushAllArgs, val]
+        val = InstructionList((PushAllArgs, val))
+    else:
+        val = InstructionList(val)
+    opcodes[opc] = val

Modified: pypy/dist/pypy/translator/jvm/option.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/option.py	(original)
+++ pypy/dist/pypy/translator/jvm/option.py	Tue Oct 31 10:59:57 2006
@@ -1,4 +1,15 @@
 from pypy.translator.jvm.conftest import option
 
+# Not sure why this is needed.  Sure that it shouldn't be, even.
+_default_values = {
+    'javac':'javac',
+    'java':'java',
+    'noasm':False,
+    'package':'pypy',
+    'wd':False
+    }
+
 def getoption(name):
-    return getattr(option, name)
+    if hasattr(option, name):
+        return getattr(option, name)
+    return _default_values[name]

Modified: pypy/dist/pypy/translator/jvm/typesystem.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/typesystem.py	(original)
+++ pypy/dist/pypy/translator/jvm/typesystem.py	Tue Oct 31 10:59:57 2006
@@ -1,5 +1,6 @@
 """
-Translation between PyPy ootypesystem and JVM type system.
+Definition and some basic translations between PyPy ootypesystem and
+JVM type system.
 
 Here are some tentative non-obvious decisions:
 
@@ -94,12 +95,13 @@
 jStringBuilder = jvm_for_class('java.lang.StringBuilder')
 
 # Map from OOType to an internal JVM type descriptor
-_lltype_to_jvm = {
+#  only handles the simple cases
+ootype_to_jvm = {
     ootype.Void:             jVoid,
     ootype.Signed:           jInt,
     ootype.Unsigned:         jInt,
-    lltype.SignedLongLong:   jLong,
-    lltype.UnsignedLongLong: jLong,
+    ootype.SignedLongLong:   jLong,
+    ootype.UnsignedLongLong: jLong,
     ootype.Bool:             jBool,
     ootype.Float:            jDouble,
     ootype.Char:             jByte,
@@ -124,34 +126,3 @@
     into one of these descriptor strings. """
     return "(%s)%s" % ("".join(argtypes), rettype)
 
-class JvmTypeSystem(object):
-
-    """ This object translates between the OOTypeSystem and JVM type
-    descriptors. """
-
-    def enforce_jvm(self, typ):
-        if isinstance(typ, JvmType):
-            return typ
-        return self.ootype_to_jvm(typ)
-
-    def ootype_to_jvm(self, oot):
-        """ Returns an instance of JvmType corresponding to the given
-        OOType """
-
-        # Check the easy cases
-        if oot in _lltype_to_jvm:
-            return _lltype_to_jvm[oot]
-
-        # Now handle the harder ones
-        if isinstance(oot, lltype.Ptr) and isinstance(t.TO, lltype.OpaqueType):
-            return jObject
-        if isinstance(oot, ootype.Instance):
-            return XXX
-        if isinstance(oot, ootype.Record):
-            return XXX
-        if isinstance(oot, ootype.StaticMethod):
-            return XXX
-
-        # Uh-oh
-        unhandled_case
-

Modified: pypy/dist/pypy/translator/oosupport/function.py
==============================================================================
--- pypy/dist/pypy/translator/oosupport/function.py	(original)
+++ pypy/dist/pypy/translator/oosupport/function.py	Tue Oct 31 10:59:57 2006
@@ -10,10 +10,12 @@
         self.name = self.cts.escape_name(name or graph.name)
         self.is_method = is_method
         self.is_entrypoint = is_entrypoint
-        self.blocknum = {}
-        self._set_args()
-        self._set_locals()
-        self.generator = self._create_generator()
+        self.generator = None # set in render()
+        
+        # If you want to enumerate args/locals before processing, then
+        # add these functions into your __init__() [they are defined below]
+        #   self._set_args()
+        #   self._set_locals()
 
     def get_name(self):
         return self.name
@@ -49,17 +51,25 @@
         raise NotImplementedError
 
     def begin_try(self):
+        """ Begins a try block; end_try will be called exactly once, then
+        some number of begin_ and end_catch pairs """
         raise NotImplementedError
 
-    def end_try(self):
+    def end_try(self, target_label):
+        """ Ends the try block, and branchs to the given target_label if
+        no exception occurred """
         raise NotImplementedError
 
     def begin_catch(self, llexitcase):
+        """ Begins a catch block for the exception type specified in
+        llexitcase"""
         raise NotImplementedError
     
     def end_catch(self, target_label):
+        """ Ends the catch block, and branchs to the given target_label as the
+        last item in the catch block """
         raise NotImplementedError
-    
+
     def render(self, ilasm):
         if self.db.graph_name(self.graph) is not None and not self.is_method:
             return # already rendered
@@ -68,6 +78,7 @@
             assert False, 'Cannot render a suggested_primitive'
 
         self.ilasm = ilasm
+        self.generator = self._create_generator(self.ilasm)
         graph = self.graph
         self.begin_render()
 
@@ -104,15 +115,15 @@
             self.begin_try()
             self._render_op(block.operations[-1])
 
-        # search for the "default" block to be executed when no exception is raised
+        # search for the "default" block to be executed when no
+        # exception is raised
         for link in block.exits:
             if link.exitcase is None:
                 self._setup_link(link)
-                target_label = self._get_block_name(link.target)
-                self.ilasm.leave(target_label)
-
-        if block.operations:
-            self.end_try()
+                self.end_try(self._get_block_name(link.target))
+                break
+        else:
+            assert False, "No non-exceptional case from exc_handling block"
 
         # catch the exception and dispatch to the appropriate block
         for link in block.exits:
@@ -140,12 +151,12 @@
             self._setup_link(link)
             target_label = self._get_block_name(link.target)
             if link.exitcase is None or link is block.exits[-1]:
-                self.ilasm.branch(target_label)
+                self.generator.branch_unconditionally(target_label)
             else:
                 assert type(link.exitcase is bool)
                 assert block.exitswitch is not None
-                self.load(block.exitswitch)
-                self.ilasm.branch_if(link.exitcase, target_label)
+                self.generator.load(block.exitswitch)
+                self.generator.branch_conditionally(link.exitcase, target_label)
 
     def _setup_link(self, link):
         target = link.target
@@ -154,10 +165,33 @@
                 self.generator.load(to_load)
                 self.generator.store(to_store)
 
+    def _render_op(self, op):
+        instr_list = self.db.genoo.opcodes.get(op.opname, None)
+        assert instr_list is not None, 'Unknown opcode: %s ' % op
+        assert isinstance(instr_list, InstructionList)
+        instr_list.render(self.generator, op)
+
+    def field_name(self, obj, field):
+        raise NotImplementedError
+
+    # ---------------------------------------------------------#
+    # These methods are quite backend independent, but not     #
+    # used in all backends. Invoke them from your __init__ if  #
+    # desired.                                                 #
+    # ---------------------------------------------------------#
+
+    def _get_block_name(self, block):
+        # Note: this implementation requires that self._set_locals() be
+        # called to gather the blocknum's
+        return 'block%s' % self.blocknum[block]
+    
     def _set_locals(self):
-        # this code is partly borrowed from pypy.translator.c.funcgen.FunctionCodeGenerator
+        # this code is partly borrowed from
+        # pypy.translator.c.funcgen.FunctionCodeGenerator
         # TODO: refactoring to avoid code duplication
 
+        self.blocknum = {}
+
         graph = self.graph
         mix = [graph.getreturnvar()]
         for block in graph.iterblocks():
@@ -200,15 +234,3 @@
         args = [arg for arg in self.graph.getargs() if arg.concretetype is not Void]
         self.args = map(self.cts.llvar_to_cts, args)
         self.argset = set([argname for argtype, argname in self.args])
-
-    def _get_block_name(self, block):
-        return 'block%s' % self.blocknum[block]
-
-    def _render_op(self, op):
-        instr_list = self.db.genoo.opcodes.get(op.opname, None)
-        assert instr_list is not None, 'Unknown opcode: %s ' % op
-        assert isinstance(instr_list, InstructionList)
-        instr_list.render(self, op)
-
-    def field_name(self, obj, field):
-        raise NotImplementedError

Modified: pypy/dist/pypy/translator/oosupport/metavm.py
==============================================================================
--- pypy/dist/pypy/translator/oosupport/metavm.py	(original)
+++ pypy/dist/pypy/translator/oosupport/metavm.py	Tue Oct 31 10:59:57 2006
@@ -71,6 +71,35 @@
         """
         pass
 
+    def branch_unconditionally(self, target_label):
+        """ Branches to target_label unconditionally """
+        raise NotImplementedError
+
+    def branch_conditionally(self, iftrue, target_label):
+        """ Branches to target_label depending on the value on the top of
+        the stack.  If iftrue is True, then the branch occurs if the value
+        on top of the stack is true; if iftrue is false, then the branch
+        occurs if the value on the top of the stack is false
+
+        Stack: cond, ... -> ... """
+        raise NotImplementedError
+
+    def call_graph(self, graph):
+        """ Invokes the method corresponding to the given graph.  The
+        arguments to the graph have already been pushed in order
+        (i.e., first argument pushed first, etc).  Pushes the return
+        value.
+
+        Stack: argN...arg2, arg1, arg0, ... -> ret, ... """
+        raise NotImplementedError        
+
+    def call_primitive(self, graph):
+        """ Like call_graph, but it has been suggested that the method be
+        rendered as a primitive.
+
+        Stack: argN...arg2, arg1, arg0, ... -> ret, ... """
+        raise NotImplementedError        
+
 class InstructionList(list):
     def render(self, generator, op):
         for instr in self:
@@ -224,6 +253,71 @@
         except (KeyError, AttributeError):
             generator.new(op.args[0].value)
 
+class BranchUnconditionally(MicroInstruction):
+    def __init__(self, label):
+        self.label = label
+    def render(self, generator, op):
+        generator.branch_unconditionally(self.label)
+
+class BranchIfTrue(MicroInstruction):
+    def __init__(self, label):
+        self.label = label
+    def render(self, generator, op):
+        generator.branch_conditionally(True, self.label)
+
+class BranchIfFalse(MicroInstruction):
+    def __init__(self, label):
+        self.label = label
+    def render(self, generator, op):
+        generator.branch_conditionally(False, self.label)
+
+class _Call(MicroInstruction):
+    def render(self, generator, op):
+        callee = op.args[0].value
+        graph = callee.graph
+        method_name = None # XXX oopspec.get_method_name(graph, op)
+
+        for arg in op.args[1:]:
+            generator.load(arg)
+        
+        if method_name is None:
+            if getattr(graph.func, 'suggested_primitive', False):
+                generator.call_primitive(graph)
+            else:
+                generator.call_graph(graph)
+        else:
+            self._render_method(generator, method_name, op.args[1:])
+
+    def _render_method(self, generator, method_name, args):
+        this = args[0]
+        for arg in args: # push parameters
+            generator.load(arg)
+
+        # XXX: very hackish, need refactoring
+        if this.concretetype is ootype.String:
+            # special case for string: don't use methods, but plain functions
+            METH = this.concretetype._METHODS[method_name]
+            cts = generator.cts
+            ret_type = cts.lltype_to_cts(METH.RESULT)
+            arg_types = [cts.lltype_to_cts(arg) for arg in METH.ARGS if arg is not ootype.Void]
+            arg_types.insert(0, cts.lltype_to_cts(ootype.String))
+            arg_list = ', '.join(arg_types)
+            signature = '%s %s::%s(%s)' % (ret_type, STRING_HELPER_CLASS, method_name, arg_list)
+            generator.call_signature(signature)
+        else:
+            generator.call_method(this.concretetype, method_name)
+            
+            # special case: DictItemsIterator(XXX,
+            # Void).ll_current_value needs to return an int32 because
+            # we can't use 'void' as a parameter of a Generic. This
+            # means that after the call to ll_current_value there will
+            # be a value on the stack, and we need to explicitly pop
+            # it.
+            if isinstance(this.concretetype, ootype.DictItemsIterator) and \
+               this.concretetype._VALUETYPE is ootype.Void and \
+               method_name == 'll_current_value':
+                generator.ilasm.pop()
+
 New = _New()
 
 PushAllArgs = _PushAllArgs()
@@ -232,3 +326,4 @@
 GetField = _GetField()
 DownCast = _DownCast()
 DoNothing = _DoNothing()
+Call = _Call()



More information about the Pypy-commit mailing list