OSDN Git Service

Dalvik fast interpreter support and JIT implementation
[android-x86/dalvik.git] / vm / mterp / gen-mterp.py
index 7d3d58b..ec7acaf 100755 (executable)
@@ -25,14 +25,21 @@ from string import Template
 interp_defs_file = "../../libdex/DexOpcodes.h" # need opcode list
 kNumPackedOpcodes = 512 # TODO: Derive this from DexOpcodes.h.
 
+splitops = False
 verbose = False
 handler_size_bits = -1000
 handler_size_bytes = -1000
 in_op_start = 0             # 0=not started, 1=started, 2=ended
+in_alt_op_start = 0         # 0=not started, 1=started, 2=ended
 default_op_dir = None
+default_alt_stub = None
 opcode_locations = {}
+alt_opcode_locations = {}
 asm_stub_text = []
 label_prefix = ".L"         # use ".L" to hide labels from gdb
+alt_label_prefix = ".L_ALT" # use ".L" to hide labels from gdb
+style = None                # interpreter style
+generate_alt_table = False
 
 # Exception class.
 class DataParseError(SyntaxError):
@@ -47,18 +54,32 @@ def getGlobalSubDict():
 
 #
 # Parse arch config file --
+# Set interpreter style.
+#
+def setHandlerStyle(tokens):
+    global style
+    if len(tokens) != 2:
+        raise DataParseError("handler-style requires one argument")
+    style = tokens[1]
+    if style != "computed-goto" and style != "jump-table" and style != "all-c":
+        raise DataParseError("handler-style (%s) invalid" % style)
+
+#
+# Parse arch config file --
 # Set handler_size_bytes to the value of tokens[1], and handler_size_bits to
-# log2(handler_size_bytes).  Throws an exception if "bytes" is not a power
-# of two.
+# log2(handler_size_bytes).  Throws an exception if "bytes" is not 0 or
+# a power of two.
 #
 def setHandlerSize(tokens):
     global handler_size_bits, handler_size_bytes
+    if style != "computed-goto":
+        print "Warning: handler-size valid only for computed-goto interpreters"
     if len(tokens) != 2:
         raise DataParseError("handler-size requires one argument")
     if handler_size_bits != -1000:
         raise DataParseError("handler-size may only be set once")
 
-    # compute log2(n), and make sure n is a power of 2
+    # compute log2(n), and make sure n is 0 or a power of 2
     handler_size_bytes = bytes = int(tokens[1])
     bits = -1
     while bytes > 0:
@@ -66,7 +87,7 @@ def setHandlerSize(tokens):
         bits += 1
 
     if handler_size_bytes == 0 or handler_size_bytes != (1 << bits):
-        raise DataParseError("handler-size (%d) must be power of 2 and > 0" \
+        raise DataParseError("handler-size (%d) must be power of 2" \
                 % orig_bytes)
     handler_size_bits = bits
 
@@ -78,12 +99,12 @@ def importFile(tokens):
     if len(tokens) != 2:
         raise DataParseError("import requires one argument")
     source = tokens[1]
-    if source.endswith(".c"):
+    if source.endswith(".cpp"):
         appendSourceFile(tokens[1], getGlobalSubDict(), c_fp, None)
     elif source.endswith(".S"):
         appendSourceFile(tokens[1], getGlobalSubDict(), asm_fp, None)
     else:
-        raise DataParseError("don't know how to import %s (expecting .c/.S)"
+        raise DataParseError("don't know how to import %s (expecting .cpp/.S)"
                 % source)
 
 #
@@ -92,6 +113,8 @@ def importFile(tokens):
 #
 def setAsmStub(tokens):
     global asm_stub_text
+    if style == "all-c":
+        print "Warning: asm-stub ignored for all-c interpreter"
     if len(tokens) != 2:
         raise DataParseError("import requires one argument")
     try:
@@ -104,6 +127,19 @@ def setAsmStub(tokens):
 
 #
 # Parse arch config file --
+# Record location of default alt stub
+#
+def setAsmAltStub(tokens):
+    global default_alt_stub, generate_alt_table
+    if style == "all-c":
+        print "Warning: asm-alt-stub ingored for all-c interpreter"
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    default_alt_stub = tokens[1]
+    generate_alt_table = True
+
+#
+# Parse arch config file --
 # Start of opcode list.
 #
 def opStart(tokens):
@@ -118,6 +154,26 @@ def opStart(tokens):
 
 #
 # Parse arch config file --
+# Set location of a single alt opcode's source file.
+#
+def altEntry(tokens):
+    global generate_alt_table
+    if len(tokens) != 3:
+        raise DataParseError("alt requires exactly two arguments")
+    if in_op_start != 1:
+        raise DataParseError("alt statements must be between opStart/opEnd")
+    try:
+        index = opcodes.index(tokens[1])
+    except ValueError:
+        raise DataParseError("unknown opcode %s" % tokens[1])
+    if alt_opcode_locations.has_key(tokens[1]):
+        print "Note: alt overrides earlier %s (%s -> %s)" \
+                % (tokens[1], alt_opcode_locations[tokens[1]], tokens[2])
+    alt_opcode_locations[tokens[1]] = tokens[2]
+    generate_alt_table = True
+
+#
+# Parse arch config file --
 # Set location of a single opcode's source file.
 #
 def opEntry(tokens):
@@ -131,11 +187,25 @@ def opEntry(tokens):
     except ValueError:
         raise DataParseError("unknown opcode %s" % tokens[1])
     if opcode_locations.has_key(tokens[1]):
-        print "Warning: op overrides earlier %s (%s -> %s)" \
+        print "Note: op overrides earlier %s (%s -> %s)" \
                 % (tokens[1], opcode_locations[tokens[1]], tokens[2])
     opcode_locations[tokens[1]] = tokens[2]
 
 #
+# Emit jump table
+#
+def emitJmpTable(start_label, prefix):
+    asm_fp.write("\n    .global %s\n" % start_label)
+    asm_fp.write("    .text\n")
+    asm_fp.write("%s:\n" % start_label)
+    for i in xrange(kNumPackedOpcodes):
+        op = opcodes[i]
+        dict = getGlobalSubDict()
+        dict.update({ "opcode":op, "opnum":i })
+        asm_fp.write("    .long " + prefix + \
+                     "_%(opcode)s /* 0x%(opnum)02x */\n" % dict)
+
+#
 # Parse arch config file --
 # End of opcode list; emit instruction blocks.
 #
@@ -148,6 +218,23 @@ def opEnd(tokens):
     in_op_start = 2
 
     loadAndEmitOpcodes()
+    if splitops == False:
+        if generate_alt_table:
+            loadAndEmitAltOpcodes()
+            if style == "jump-table":
+                emitJmpTable("dvmAsmInstructionStart", label_prefix);
+                emitJmpTable("dvmAsmAltInstructionStart", alt_label_prefix);
+
+def genaltop(tokens):
+    if in_op_start != 2:
+       raise DataParseError("alt-op can be specified only after op-end")
+    if len(tokens) != 1:
+        raise DataParseError("opEnd takes no arguments")
+    if generate_alt_table:
+        loadAndEmitAltOpcodes()
+        if style == "jump-table":
+            emitJmpTable("dvmAsmInstructionStart", label_prefix);
+            emitJmpTable("dvmAsmAltInstructionStart", alt_label_prefix);
 
 
 #
@@ -172,6 +259,9 @@ def getOpcodeList():
         raise SyntaxError, "bad opcode count"
     return opcodes
 
+def emitAlign():
+    if style == "computed-goto":
+        asm_fp.write("    .balign %d\n" % handler_size_bytes)
 
 #
 # Load and emit opcodes for all kNumPackedOpcodes instructions.
@@ -180,11 +270,17 @@ def loadAndEmitOpcodes():
     sister_list = []
     assert len(opcodes) == kNumPackedOpcodes
     need_dummy_start = False
+    if style == "jump-table":
+        start_label = "dvmAsmInstructionStartCode"
+        end_label = "dvmAsmInstructionEndCode"
+    else:
+        start_label = "dvmAsmInstructionStart"
+        end_label = "dvmAsmInstructionEnd"
 
     # point dvmAsmInstructionStart at the first handler or stub
-    asm_fp.write("\n    .global dvmAsmInstructionStart\n")
-    asm_fp.write("    .type   dvmAsmInstructionStart, %function\n")
-    asm_fp.write("dvmAsmInstructionStart = " + label_prefix + "_OP_NOP\n")
+    asm_fp.write("\n    .global %s\n" % start_label)
+    asm_fp.write("    .type   %s, %%function\n" % start_label)
+    asm_fp.write("%s = " % start_label + label_prefix + "_OP_NOP\n")
     asm_fp.write("    .text\n\n")
 
     for i in xrange(kNumPackedOpcodes):
@@ -207,34 +303,78 @@ def loadAndEmitOpcodes():
     # too annoying to try to slide it in after the alignment psuedo-op, so
     # we take the low road and just emit a dummy OP_NOP here.
     if need_dummy_start:
-        asm_fp.write("    .balign %d\n" % handler_size_bytes)
+        emitAlign()
         asm_fp.write(label_prefix + "_OP_NOP:   /* dummy */\n");
 
-    asm_fp.write("\n    .balign %d\n" % handler_size_bytes)
-    asm_fp.write("    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart\n")
-    asm_fp.write("    .global dvmAsmInstructionEnd\n")
-    asm_fp.write("dvmAsmInstructionEnd:\n")
+    emitAlign()
+    asm_fp.write("    .size   %s, .-%s\n" % (start_label, start_label))
+    asm_fp.write("    .global %s\n" % end_label)
+    asm_fp.write("%s:\n" % end_label)
 
-    emitSectionComment("Sister implementations", asm_fp)
-    asm_fp.write("    .global dvmAsmSisterStart\n")
-    asm_fp.write("    .type   dvmAsmSisterStart, %function\n")
-    asm_fp.write("    .text\n")
-    asm_fp.write("    .balign 4\n")
-    asm_fp.write("dvmAsmSisterStart:\n")
-    asm_fp.writelines(sister_list)
+    if style == "computed-goto":
+        emitSectionComment("Sister implementations", asm_fp)
+        asm_fp.write("    .global dvmAsmSisterStart\n")
+        asm_fp.write("    .type   dvmAsmSisterStart, %function\n")
+        asm_fp.write("    .text\n")
+        asm_fp.write("    .balign 4\n")
+        asm_fp.write("dvmAsmSisterStart:\n")
+        asm_fp.writelines(sister_list)
+        asm_fp.write("\n    .size   dvmAsmSisterStart, .-dvmAsmSisterStart\n")
+        asm_fp.write("    .global dvmAsmSisterEnd\n")
+        asm_fp.write("dvmAsmSisterEnd:\n\n")
+
+#
+# Load an alternate entry stub
+#
+def loadAndEmitAltStub(source, opindex):
+    op = opcodes[opindex]
+    if verbose:
+        print " alt emit %s --> stub" % source
+    dict = getGlobalSubDict()
+    dict.update({ "opcode":op, "opnum":opindex })
+
+    emitAsmHeader(asm_fp, dict, alt_label_prefix)
+    appendSourceFile(source, dict, asm_fp, None)
+
+#
+# Load and emit alternate opcodes for all kNumPackedOpcodes instructions.
+#
+def loadAndEmitAltOpcodes():
+    assert len(opcodes) == kNumPackedOpcodes
+    if style == "jump-table":
+        start_label = "dvmAsmAltInstructionStartCode"
+        end_label = "dvmAsmAltInstructionEndCode"
+    else:
+        start_label = "dvmAsmAltInstructionStart"
+        end_label = "dvmAsmAltInstructionEnd"
+
+    # point dvmAsmInstructionStart at the first handler or stub
+    asm_fp.write("\n    .global %s\n" % start_label)
+    asm_fp.write("    .type   %s, %%function\n" % start_label)
+    asm_fp.write("    .text\n\n")
+    asm_fp.write("%s = " % start_label + label_prefix + "_ALT_OP_NOP\n")
+
+    for i in xrange(kNumPackedOpcodes):
+        op = opcodes[i]
+        if alt_opcode_locations.has_key(op):
+            source = "%s/ALT_%s.S" % (alt_opcode_locations[op], op)
+        else:
+            source = default_alt_stub
+        loadAndEmitAltStub(source, i)
 
-    asm_fp.write("\n    .size   dvmAsmSisterStart, .-dvmAsmSisterStart\n")
-    asm_fp.write("    .global dvmAsmSisterEnd\n")
-    asm_fp.write("dvmAsmSisterEnd:\n\n")
+    emitAlign()
+    asm_fp.write("    .size   %s, .-%s\n" % (start_label, start_label))
+    asm_fp.write("    .global %s\n" % end_label)
+    asm_fp.write("%s:\n" % end_label)
 
 #
 # Load a C fragment and emit it, then output an assembly stub.
 #
 def loadAndEmitC(location, opindex):
     op = opcodes[opindex]
-    source = "%s/%s.c" % (location, op)
+    source = "%s/%s.cpp" % (location, op)
     if verbose:
-        print " emit %s --> C" % source
+        print " emit %s --> C++" % source
     dict = getGlobalSubDict()
     dict.update({ "opcode":op, "opnum":opindex })
 
@@ -254,28 +394,28 @@ def loadAndEmitAsm(location, opindex, sister_list):
     if verbose:
         print " emit %s --> asm" % source
 
-    emitAsmHeader(asm_fp, dict)
+    emitAsmHeader(asm_fp, dict, label_prefix)
     appendSourceFile(source, dict, asm_fp, sister_list)
 
 #
 # Output the alignment directive and label for an assembly piece.
 #
-def emitAsmHeader(outfp, dict):
+def emitAsmHeader(outfp, dict, prefix):
     outfp.write("/* ------------------------------ */\n")
     # The alignment directive ensures that the handler occupies
     # at least the correct amount of space.  We don't try to deal
     # with overflow here.
-    outfp.write("    .balign %d\n" % handler_size_bytes)
+    emitAlign()
     # Emit a label so that gdb will say the right thing.  We prepend an
     # underscore so the symbol name doesn't clash with the Opcode enum.
-    outfp.write(label_prefix + "_%(opcode)s: /* 0x%(opnum)02x */\n" % dict)
+    outfp.write(prefix + "_%(opcode)s: /* 0x%(opnum)02x */\n" % dict)
 
 #
 # Output a generic instruction stub that updates the "glue" struct and
 # calls the C implementation.
 #
 def emitAsmStub(outfp, dict):
-    emitAsmHeader(outfp, dict)
+    emitAsmHeader(outfp, dict, label_prefix)
     for line in asm_stub_text:
         templ = Template(line)
         outfp.write(templ.substitute(dict))
@@ -334,7 +474,7 @@ def appendSourceFile(source, dict, outfp, sister_list):
 
         elif line.startswith("%break") and sister_list != None:
             # allow more than one %break, ignoring all following the first
-            if not in_sister:
+            if style == "computed-goto" and not in_sister:
                 in_sister = True
                 sister_list.append("\n/* continuation for %(opcode)s */\n"%dict)
             continue
@@ -407,7 +547,7 @@ except:
 # Open and prepare output files.
 #
 try:
-    c_fp = open("%s/InterpC-%s.c" % (output_dir, target_arch), "w")
+    c_fp = open("%s/InterpC-%s.cpp" % (output_dir, target_arch), "w")
     asm_fp = open("%s/InterpAsm-%s.S" % (output_dir, target_arch), "w")
 except:
     print "Unable to open output files"
@@ -452,14 +592,27 @@ try:
                 importFile(tokens)
             elif tokens[0] == "asm-stub":
                 setAsmStub(tokens)
+            elif tokens[0] == "asm-alt-stub":
+                setAsmAltStub(tokens)
             elif tokens[0] == "op-start":
                 opStart(tokens)
             elif tokens[0] == "op-end":
                 opEnd(tokens)
+            elif tokens[0] == "alt":
+                altEntry(tokens)
             elif tokens[0] == "op":
                 opEntry(tokens)
+            elif tokens[0] == "handler-style":
+                setHandlerStyle(tokens)
+            elif tokens[0] == "alt-ops":
+                genaltop(tokens)
+            elif tokens[0] == "split-ops":
+                splitops = True
             else:
                 raise DataParseError, "unrecognized command '%s'" % tokens[0]
+            if style == None:
+                print "tokens[0] = %s" % tokens[0]
+                raise DataParseError, "handler-style must be first command"
 except DataParseError, err:
     print "Failed: " + str(err)
     # TODO: remove output files so "make" doesn't get confused