[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