from string import Template
interp_defs_file = "../../libdex/DexOpcodes.h" # need opcode list
-kNumPackedOpcodes = 256 # TODO: Derive this from DexOpcodes.h.
+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):
#
# 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:
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
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)
#
#
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:
#
# 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):
#
# 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):
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.
#
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);
#
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.
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):
# 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 })
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))
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
# 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"
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