summaryrefslogtreecommitdiffstats
path: root/vm/compiler/codegen/armv5te/Codegen.c
diff options
context:
space:
mode:
Diffstat (limited to 'vm/compiler/codegen/armv5te/Codegen.c')
-rw-r--r--vm/compiler/codegen/armv5te/Codegen.c2892
1 files changed, 2892 insertions, 0 deletions
diff --git a/vm/compiler/codegen/armv5te/Codegen.c b/vm/compiler/codegen/armv5te/Codegen.c
new file mode 100644
index 000000000..178e5368b
--- /dev/null
+++ b/vm/compiler/codegen/armv5te/Codegen.c
@@ -0,0 +1,2892 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/OpCode.h"
+#include "dexdump/OpCodeNames.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "Armv5teLIR.h"
+#include "vm/mterp/common/FindInterface.h"
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+typedef enum {
+#include "../../template/armv5te/TemplateOpList.h"
+/*
+ * For example,
+ * TEMPLATE_CMP_LONG,
+ * TEMPLATE_RETURN,
+ * ...
+ */
+ TEMPLATE_LAST_MARK,
+} TemplateOpCode;
+#undef JIT_TEMPLATE
+
+/* Array holding the entry offset of each template relative to the first one */
+static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK];
+
+/* Track exercised opcodes */
+static int opcodeCoverage[256];
+
+/*****************************************************************************/
+
+/*
+ * The following are building blocks to construct low-level IRs with 0 - 3
+ * operands.
+ */
+static Armv5teLIR *newLIR0(CompilationUnit *cUnit, Armv5teOpCode opCode)
+{
+ Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true);
+ assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 0);
+ insn->opCode = opCode;
+ dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+ return insn;
+}
+
+static Armv5teLIR *newLIR1(CompilationUnit *cUnit, Armv5teOpCode opCode,
+ int dest)
+{
+ Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true);
+ assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 1);
+ insn->opCode = opCode;
+ insn->operands[0] = dest;
+ dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+ return insn;
+}
+
+static Armv5teLIR *newLIR2(CompilationUnit *cUnit, Armv5teOpCode opCode,
+ int dest, int src1)
+{
+ Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true);
+ assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 2);
+ insn->opCode = opCode;
+ insn->operands[0] = dest;
+ insn->operands[1] = src1;
+ dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+ return insn;
+}
+
+static Armv5teLIR *newLIR3(CompilationUnit *cUnit, Armv5teOpCode opCode,
+ int dest, int src1, int src2)
+{
+ Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true);
+ assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 3);
+ insn->opCode = opCode;
+ insn->operands[0] = dest;
+ insn->operands[1] = src1;
+ insn->operands[2] = src2;
+ dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+ return insn;
+}
+
+static Armv5teLIR *newLIR23(CompilationUnit *cUnit, Armv5teOpCode opCode,
+ int srcdest, int src2)
+{
+ assert(!isPseudoOpCode(opCode));
+ if (EncodingMap[opCode].operands==2)
+ return newLIR2(cUnit, opCode, srcdest, src2);
+ else
+ return newLIR3(cUnit, opCode, srcdest, srcdest, src2);
+}
+
+/*****************************************************************************/
+
+/*
+ * The following are building blocks to insert constants into the pool or
+ * instruction streams.
+ */
+
+/* Add a 32-bit constant either in the constant pool or mixed with code */
+static Armv5teLIR *addWordData(CompilationUnit *cUnit, int value, bool inPlace)
+{
+ /* Add the constant to the literal pool */
+ if (!inPlace) {
+ Armv5teLIR *newValue = dvmCompilerNew(sizeof(Armv5teLIR), true);
+ newValue->operands[0] = value;
+ newValue->generic.next = cUnit->wordList;
+ cUnit->wordList = (LIR *) newValue;
+ return newValue;
+ } else {
+ /* Add the constant in the middle of code stream */
+ newLIR1(cUnit, ARMV5TE_16BIT_DATA, (value & 0xffff));
+ newLIR1(cUnit, ARMV5TE_16BIT_DATA, (value >> 16));
+ }
+ return NULL;
+}
+
+/*
+ * Search the existing constants in the literal pool for an exact or close match
+ * within specified delta (greater or equal to 0).
+ */
+static Armv5teLIR *scanLiteralPool(CompilationUnit *cUnit, int value,
+ unsigned int delta)
+{
+ LIR *dataTarget = cUnit->wordList;
+ while (dataTarget) {
+ if (((unsigned) (value - ((Armv5teLIR *) dataTarget)->operands[0])) <=
+ delta)
+ return (Armv5teLIR *) dataTarget;
+ dataTarget = dataTarget->next;
+ }
+ return NULL;
+}
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool
+ */
+void loadConstant(CompilationUnit *cUnit, int rDest, int value)
+{
+ /* See if the value can be constructed cheaply */
+ if ((value >= 0) && (value <= 255)) {
+ newLIR2(cUnit, ARMV5TE_MOV_IMM, rDest, value);
+ return;
+ } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
+ newLIR2(cUnit, ARMV5TE_MOV_IMM, rDest, ~value);
+ newLIR2(cUnit, ARMV5TE_MVN, rDest, rDest);
+ return;
+ }
+ /* No shortcut - go ahead and use literal pool */
+ Armv5teLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
+ if (dataTarget == NULL) {
+ dataTarget = addWordData(cUnit, value, false);
+ }
+ Armv5teLIR *loadPcRel = dvmCompilerNew(sizeof(Armv5teLIR), true);
+ loadPcRel->opCode = ARMV5TE_LDR_PC_REL;
+ loadPcRel->generic.target = (LIR *) dataTarget;
+ loadPcRel->operands[0] = rDest;
+ dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+
+ /*
+ * To save space in the constant pool, we use the ADD_RRI8 instruction to
+ * add up to 255 to an existing constant value.
+ */
+ if (dataTarget->operands[0] != value) {
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, rDest, value - dataTarget->operands[0]);
+ }
+}
+
+/* Export the Dalvik PC assicated with an instruction to the StackSave area */
+static void genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC, int rAddr)
+{
+ int offset = offsetof(StackSaveArea, xtra.currentPc);
+ loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+ newLIR2(cUnit, ARMV5TE_MOV_RR, rAddr, rFP);
+ newLIR2(cUnit, ARMV5TE_SUB_RI8, rAddr, sizeof(StackSaveArea) - offset);
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, rDPC, rAddr, 0);
+}
+
+/* Generate conditional branch instructions */
+static void genConditionalBranch(CompilationUnit *cUnit,
+ Armv5teConditionCode cond,
+ Armv5teLIR *target)
+{
+ Armv5teLIR *branch = newLIR2(cUnit, ARMV5TE_B_COND, 0, cond);
+ branch->generic.target = (LIR *) target;
+}
+
+/* Generate unconditional branch instructions */
+static void genUnconditionalBranch(CompilationUnit *cUnit, Armv5teLIR *target)
+{
+ Armv5teLIR *branch = newLIR0(cUnit, ARMV5TE_B_UNCOND);
+ branch->generic.target = (LIR *) target;
+}
+
+#define USE_IN_CACHE_HANDLER 1
+
+/*
+ * Jump to the out-of-line handler in ARM mode to finish executing the
+ * remaining of more complex instructions.
+ */
+static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpCode opCode)
+{
+#if USE_IN_CACHE_HANDLER
+ /*
+ * NOTE - In practice BLX only needs one operand, but since the assembler
+ * may abort itself and retry due to other out-of-range conditions we
+ * cannot really use operand[0] to store the absolute target address since
+ * it may get clobbered by the final relative offset. Therefore,
+ * we fake BLX_1 is a two operand instruction and the absolute target
+ * address is stored in operand[1].
+ */
+ newLIR2(cUnit, ARMV5TE_BLX_1,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode]);
+ newLIR2(cUnit, ARMV5TE_BLX_2,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode]);
+#else
+ /*
+ * In case we want to access the statically compiled handlers for
+ * debugging purposes, define USE_IN_CACHE_HANDLER to 0
+ */
+ void *templatePtr;
+
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+ switch (opCode) {
+#define JIT_TEMPLATE(X) \
+ case TEMPLATE_##X: { templatePtr = dvmCompiler_TEMPLATE_##X; break; }
+#include "../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+ default: templatePtr = NULL;
+ }
+ loadConstant(cUnit, r7, (int) templatePtr);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r7);
+#endif
+}
+
+/* Perform the actual operation for OP_RETURN_* */
+static void genReturnCommon(CompilationUnit *cUnit, MIR *mir)
+{
+ genDispatchToHandler(cUnit, TEMPLATE_RETURN);
+#if defined(INVOKE_STATS)
+ gDvmJit.jitReturn++;
+#endif
+ int dPC = (int) (cUnit->method->insns + mir->offset);
+ Armv5teLIR *branch = newLIR0(cUnit, ARMV5TE_B_UNCOND);
+ /* Set up the place holder to reconstruct this Dalvik PC */
+ Armv5teLIR *pcrLabel = dvmCompilerNew(sizeof(Armv5teLIR), true);
+ pcrLabel->opCode = ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL;
+ pcrLabel->operands[0] = dPC;
+ pcrLabel->operands[1] = mir->offset;
+ /* Insert the place holder to the growable list */
+ dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ /* Branch to the PC reconstruction code */
+ branch->generic.target = (LIR *) pcrLabel;
+}
+
+/*
+ * Load a pair of values of rFP[src..src+1] and store them into rDestLo and
+ * rDestHi
+ */
+static void loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
+ int rDestHi)
+{
+ /* Use reg + imm5*4 to load the values if possible */
+ if (vSrc <= 30) {
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, rDestLo, rFP, vSrc);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, rDestHi, rFP, vSrc+1);
+ } else {
+ if (vSrc <= 64) {
+ /* Sneak 4 into the base address first */
+ newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDestLo, rFP, 4);
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, rDestHi, (vSrc-1)*4);
+ } else {
+ /* Offset too far from rFP */
+ loadConstant(cUnit, rDestLo, vSrc*4);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, rDestLo, rFP, rDestLo);
+ }
+ assert(rDestLo != rDestHi);
+ newLIR2(cUnit, ARMV5TE_LDMIA, rDestLo, (1<<rDestLo) | (1<<(rDestHi)));
+ }
+}
+
+/*
+ * Store a pair of values of rSrc and rSrc+1 and store them into vDest and
+ * vDest+1
+ */
+static void storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
+ int vDest, int rScratch)
+{
+ /* Use reg + imm5*4 to store the values if possible */
+ if (vDest <= 30) {
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, rSrcLo, rFP, vDest);
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, rSrcHi, rFP, vDest+1);
+ } else {
+ if (vDest <= 64) {
+ /* Sneak 4 into the base address first */
+ newLIR3(cUnit, ARMV5TE_ADD_RRI3, rScratch, rFP, 4);
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, rScratch, (vDest-1)*4);
+ } else {
+ /* Offset too far from rFP */
+ loadConstant(cUnit, rScratch, vDest*4);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, rScratch, rFP, rScratch);
+ }
+ assert(rSrcLo != rSrcHi);
+ newLIR2(cUnit, ARMV5TE_STMIA, rScratch, (1<<rSrcLo) | (1 << (rSrcHi)));
+ }
+}
+
+/* Load the address of a Dalvik register on the frame */
+static void loadValueAddress(CompilationUnit *cUnit, int vSrc, int rDest)
+{
+ /* RRI3 can add up to 7 */
+ if (vSrc <= 1) {
+ newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, vSrc*4);
+ } else if (vSrc <= 64) {
+ /* Sneak 4 into the base address first */
+ newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, 4);
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, rDest, (vSrc-1)*4);
+ } else {
+ loadConstant(cUnit, rDest, vSrc*4);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, rDest, rFP, rDest);
+ }
+}
+
+
+/* Load a single value from rFP[src] and store them into rDest */
+static void loadValue(CompilationUnit *cUnit, int vSrc, int rDest)
+{
+ /* Use reg + imm5*4 to load the value if possible */
+ if (vSrc <= 31) {
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, rDest, rFP, vSrc);
+ } else {
+ loadConstant(cUnit, rDest, vSrc*4);
+ newLIR3(cUnit, ARMV5TE_LDR_RRR, rDest, rFP, rDest);
+ }
+}
+
+/* Store a value from rSrc to vDest */
+static void storeValue(CompilationUnit *cUnit, int rSrc, int vDest,
+ int rScratch)
+{
+ /* Use reg + imm5*4 to store the value if possible */
+ if (vDest <= 31) {
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, rSrc, rFP, vDest);
+ } else {
+ loadConstant(cUnit, rScratch, vDest*4);
+ newLIR3(cUnit, ARMV5TE_STR_RRR, rSrc, rFP, rScratch);
+ }
+}
+
+/* Calculate the address of rFP+vSrc*4 */
+static void calculateValueAddress(CompilationUnit *cUnit, int vSrc, int rDest)
+{
+ /* Use add rd, rs, imm_3 */
+ if (vSrc <= 1) {
+ newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, vSrc*4);
+ } else if (vSrc <= 64) {
+ /* Use add rd, imm_8 */
+ /* Sneak in 4 above rFP to cover one more register offset (ie v64) */
+ newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, 4);
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, rDest, (vSrc-1)*4);
+ } else {
+ /* Load offset from the constant pool */
+ loadConstant(cUnit, rDest, vSrc*4);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, rDest, rFP, rDest);
+ }
+}
+
+/*
+ * Perform a binary operation on 64-bit operands and leave the results in the
+ * r0/r1 pair.
+ */
+static void genBinaryOpWide(CompilationUnit *cUnit, int vDest,
+ Armv5teOpCode preinst, Armv5teOpCode inst)
+{
+ newLIR23(cUnit, preinst, r0, r2);
+ newLIR23(cUnit, inst, r1, r3);
+ storeValuePair(cUnit, r0, r1, vDest, r2);
+}
+
+/* Perform a binary operation on 32-bit operands and leave the results in r0. */
+static void genBinaryOp(CompilationUnit *cUnit, int vDest, Armv5teOpCode inst)
+{
+ newLIR23(cUnit, inst, r0, r1);
+ storeValue(cUnit, r0, vDest, r1);
+}
+
+/* Create the PC reconstruction slot if not already done */
+static inline Armv5teLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset,
+ Armv5teLIR *branch,
+ Armv5teLIR *pcrLabel)
+{
+ /* Set up the place holder to reconstruct this Dalvik PC */
+ if (pcrLabel == NULL) {
+ int dPC = (int) (cUnit->method->insns + dOffset);
+ pcrLabel = dvmCompilerNew(sizeof(Armv5teLIR), true);
+ pcrLabel->opCode = ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL;
+ pcrLabel->operands[0] = dPC;
+ pcrLabel->operands[1] = dOffset;
+ /* Insert the place holder to the growable list */
+ dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ }
+ /* Branch to the PC reconstruction code */
+ branch->generic.target = (LIR *) pcrLabel;
+ return pcrLabel;
+}
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static inline Armv5teLIR *genRegImmCheck(CompilationUnit *cUnit,
+ Armv5teConditionCode cond, int reg,
+ int checkValue, int dOffset,
+ Armv5teLIR *pcrLabel)
+{
+ newLIR2(cUnit, ARMV5TE_CMP_RI8, reg, checkValue);
+ Armv5teLIR *branch = newLIR2(cUnit, ARMV5TE_B_COND, 0, cond);
+ return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+/*
+ * Perform a "reg cmp reg" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static inline Armv5teLIR *inertRegRegCheck(CompilationUnit *cUnit,
+ Armv5teConditionCode cond,
+ int reg1, int reg2, int dOffset,
+ Armv5teLIR *pcrLabel)
+{
+ newLIR2(cUnit, ARMV5TE_CMP_RR, reg1, reg2);
+ Armv5teLIR *branch = newLIR2(cUnit, ARMV5TE_B_COND, 0, cond);
+ return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+/* Perform null-check on a register */
+static Armv5teLIR *genNullCheck(CompilationUnit *cUnit, int reg, int dOffset,
+ Armv5teLIR *pcrLabel)
+{
+ return genRegImmCheck(cUnit, ARM_COND_EQ, reg, 0, dOffset, pcrLabel);
+}
+
+/* Perform bound check on two registers */
+static Armv5teLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
+ int rBound, int dOffset, Armv5teLIR *pcrLabel)
+{
+ return inertRegRegCheck(cUnit, ARM_COND_CS, rIndex, rBound, dOffset,
+ pcrLabel);
+}
+
+/* Generate a unconditional branch to go to the interpreter */
+static inline Armv5teLIR *genTrap(CompilationUnit *cUnit, int dOffset,
+ Armv5teLIR *pcrLabel)
+{
+ Armv5teLIR *branch = newLIR0(cUnit, ARMV5TE_B_UNCOND);
+ return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+/* Load a wide field from an object instance */
+static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+ loadValue(cUnit, dInsn->vB, r2);
+ loadConstant(cUnit, r3, fieldOffset);
+ genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, r2, r3);
+ newLIR2(cUnit, ARMV5TE_LDMIA, r2, (1<<r0 | 1<<r1));
+ storeValuePair(cUnit, r0, r1, dInsn->vA, r3);
+}
+
+/* Store a wide field to an object instance */
+static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+ loadValue(cUnit, dInsn->vB, r2);
+ loadValuePair(cUnit, dInsn->vA, r0, r1);
+ loadConstant(cUnit, r3, fieldOffset);
+ genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, r2, r3);
+ newLIR2(cUnit, ARMV5TE_STMIA, r2, (1<<r0 | 1<<r1));
+}
+
+/*
+ * Load a field from an object instance
+ *
+ * Inst should be one of:
+ * ARMV5TE_LDR_RRR
+ * ARMV5TE_LDRB_RRR
+ * ARMV5TE_LDRH_RRR
+ * ARMV5TE_LDRSB_RRR
+ * ARMV5TE_LDRSH_RRR
+ */
+static void genIGet(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst,
+ int fieldOffset)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+ /* TUNING: write a utility routine to load via base + constant offset */
+ loadValue(cUnit, dInsn->vB, r0);
+ loadConstant(cUnit, r1, fieldOffset);
+ genNullCheck(cUnit, r0, mir->offset, NULL); /* null object? */
+ newLIR3(cUnit, inst, r0, r0, r1);
+ storeValue(cUnit, r0, dInsn->vA, r1);
+}
+
+/*
+ * Store a field to an object instance
+ *
+ * Inst should be one of:
+ * ARMV5TE_STR_RRR
+ * ARMV5TE_STRB_RRR
+ * ARMV5TE_STRH_RRR
+ */
+static void genIPut(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst,
+ int fieldOffset)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+ /* TUNING: write a utility routine to load via base + constant offset */
+ loadValue(cUnit, dInsn->vB, r2);
+ loadConstant(cUnit, r1, fieldOffset);
+ loadValue(cUnit, dInsn->vA, r0);
+ genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */
+ newLIR3(cUnit, inst, r0, r2, r1);
+}
+
+
+/* TODO: This should probably be done as an out-of-line instruction handler. */
+
+/*
+ * Generate array load
+ *
+ * Inst should be one of:
+ * ARMV5TE_LDR_RRR
+ * ARMV5TE_LDRB_RRR
+ * ARMV5TE_LDRH_RRR
+ * ARMV5TE_LDRSB_RRR
+ * ARMV5TE_LDRSH_RRR
+ */
+static void genArrayGet(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst,
+ int vArray, int vIndex, int vDest, int scale)
+{
+ int lenOffset = offsetof(ArrayObject, length);
+ int dataOffset = offsetof(ArrayObject, contents);
+
+ loadValue(cUnit, vArray, r2);
+ loadValue(cUnit, vIndex, r3);
+
+ /* null object? */
+ Armv5teLIR * pcrLabel = genNullCheck(cUnit, r2, mir->offset, NULL);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r2, lenOffset >> 2); /* Get len */
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, dataOffset); /* r2 -> array data */
+ genBoundsCheck(cUnit, r3, r0, mir->offset, pcrLabel);
+ if (scale) {
+ newLIR3(cUnit, ARMV5TE_LSL, r3, r3, scale);
+ }
+ if (scale==3) {
+ newLIR3(cUnit, inst, r0, r2, r3);
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, 4);
+ newLIR3(cUnit, inst, r1, r2, r3);
+ storeValuePair(cUnit, r0, r1, vDest, r3);
+ } else {
+ newLIR3(cUnit, inst, r0, r2, r3);
+ storeValue(cUnit, r0, vDest, r3);
+ }
+}
+
+/* TODO: This should probably be done as an out-of-line instruction handler. */
+
+/*
+ * Generate array store
+ *
+ * Inst should be one of:
+ * ARMV5TE_STR_RRR
+ * ARMV5TE_STRB_RRR
+ * ARMV5TE_STRH_RRR
+ */
+static void genArrayPut(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst,
+ int vArray, int vIndex, int vSrc, int scale)
+{
+ int lenOffset = offsetof(ArrayObject, length);
+ int dataOffset = offsetof(ArrayObject, contents);
+
+ loadValue(cUnit, vArray, r2);
+ loadValue(cUnit, vIndex, r3);
+ genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r2, lenOffset >> 2); /* Get len */
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, dataOffset); /* r2 -> array data */
+ genBoundsCheck(cUnit, r3, r0, mir->offset, NULL);
+ /* at this point, r2 points to array, r3 is unscaled index */
+ if (scale==3) {
+ loadValuePair(cUnit, vSrc, r0, r1);
+ } else {
+ loadValue(cUnit, vSrc, r0);
+ }
+ if (scale) {
+ newLIR3(cUnit, ARMV5TE_LSL, r3, r3, scale);
+ }
+ /*
+ * at this point, r2 points to array, r3 is scaled index, and r0[r1] is
+ * data
+ */
+ if (scale==3) {
+ newLIR3(cUnit, inst, r0, r2, r3);
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, 4);
+ newLIR3(cUnit, inst, r1, r2, r3);
+ } else {
+ newLIR3(cUnit, inst, r0, r2, r3);
+ }
+}
+
+static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir, int vDest,
+ int vSrc1, int vShift)
+{
+ loadValuePair(cUnit, vSrc1, r0, r1);
+ loadValue(cUnit, vShift, r2);
+ switch( mir->dalvikInsn.opCode) {
+ case OP_SHL_LONG:
+ case OP_SHL_LONG_2ADDR:
+ genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG);
+ break;
+ case OP_SHR_LONG:
+ case OP_SHR_LONG_2ADDR:
+ genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG);
+ break;
+ case OP_USHR_LONG:
+ case OP_USHR_LONG_2ADDR:
+ genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG);
+ break;
+ default:
+ return true;
+ }
+ storeValuePair(cUnit, r0, r1, vDest, r2);
+ return false;
+}
+
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir, int vDest,
+ int vSrc1, int vSrc2)
+{
+ void* funct;
+ /* TODO: use a proper include file to define these */
+ float __aeabi_fadd(float a, float b);
+ float __aeabi_fsub(float a, float b);
+ float __aeabi_fdiv(float a, float b);
+ float __aeabi_fmul(float a, float b);
+ float fmodf(float a, float b);
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_ADD_FLOAT:
+ funct = (void*) __aeabi_fadd;
+ break;
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_SUB_FLOAT:
+ funct = (void*) __aeabi_fsub;
+ break;
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_DIV_FLOAT:
+ funct = (void*) __aeabi_fdiv;
+ break;
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_MUL_FLOAT:
+ funct = (void*) __aeabi_fmul;
+ break;
+ case OP_REM_FLOAT_2ADDR:
+ case OP_REM_FLOAT:
+ funct = (void*) fmodf;
+ break;
+ case OP_NEG_FLOAT: {
+ loadValue(cUnit, vSrc2, r0);
+ loadConstant(cUnit, r1, 0x80000000);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, r0, r0, r1);
+ storeValue(cUnit, r0, vDest, r1);
+ return false;
+ }
+ default:
+ return true;
+ }
+ loadConstant(cUnit, r2, (int)funct);
+ loadValue(cUnit, vSrc1, r0);
+ loadValue(cUnit, vSrc2, r1);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r2);
+ storeValue(cUnit, r0, vDest, r1);
+ return false;
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir, int vDest,
+ int vSrc1, int vSrc2)
+{
+ void* funct;
+ /* TODO: use a proper include file to define these */
+ double __aeabi_dadd(double a, double b);
+ double __aeabi_dsub(double a, double b);
+ double __aeabi_ddiv(double a, double b);
+ double __aeabi_dmul(double a, double b);
+ double fmod(double a, double b);
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_ADD_DOUBLE:
+ funct = (void*) __aeabi_dadd;
+ break;
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE:
+ funct = (void*) __aeabi_dsub;
+ break;
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE:
+ funct = (void*) __aeabi_ddiv;
+ break;
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE:
+ funct = (void*) __aeabi_dmul;
+ break;
+ case OP_REM_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE:
+ funct = (void*) fmod;
+ break;
+ case OP_NEG_DOUBLE: {
+ loadValuePair(cUnit, vSrc2, r0, r1);
+ loadConstant(cUnit, r2, 0x80000000);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, r1, r1, r2);
+ storeValuePair(cUnit, r0, r1, vDest, r2);
+ return false;
+ }
+ default:
+ return true;
+ }
+ loadConstant(cUnit, r4PC, (int)funct);
+ loadValuePair(cUnit, vSrc1, r0, r1);
+ loadValuePair(cUnit, vSrc2, r2, r3);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r4PC);
+ storeValuePair(cUnit, r0, r1, vDest, r2);
+ return false;
+}
+
+static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir, int vDest,
+ int vSrc1, int vSrc2)
+{
+ int firstOp = ARMV5TE_BKPT;
+ int secondOp = ARMV5TE_BKPT;
+ bool callOut = false;
+ void *callTgt;
+ int retReg = r0;
+ /* TODO - find proper .h file to declare these */
+ long long __aeabi_ldivmod(long long op1, long long op2);
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_NOT_LONG:
+ firstOp = ARMV5TE_MVN;
+ secondOp = ARMV5TE_MVN;
+ break;
+ case OP_ADD_LONG:
+ case OP_ADD_LONG_2ADDR:
+ firstOp = ARMV5TE_ADD_RRR;
+ secondOp = ARMV5TE_ADC;
+ break;
+ case OP_SUB_LONG:
+ case OP_SUB_LONG_2ADDR:
+ firstOp = ARMV5TE_SUB_RRR;
+ secondOp = ARMV5TE_SBC;
+ break;
+ case OP_MUL_LONG:
+ case OP_MUL_LONG_2ADDR:
+ loadValuePair(cUnit, vSrc1, r0, r1);
+ loadValuePair(cUnit, vSrc2, r2, r3);
+ genDispatchToHandler(cUnit, TEMPLATE_MUL_LONG);
+ storeValuePair(cUnit, r0, r1, vDest, r2);
+ return false;
+ break;
+ case OP_DIV_LONG:
+ case OP_DIV_LONG_2ADDR:
+ callOut = true;
+ retReg = r0;
+ callTgt = (void*)__aeabi_ldivmod;
+ break;
+ /* NOTE - result is in r2/r3 instead of r0/r1 */
+ case OP_REM_LONG:
+ case OP_REM_LONG_2ADDR:
+ callOut = true;
+ callTgt = (void*)__aeabi_ldivmod;
+ retReg = r2;
+ break;
+ case OP_AND_LONG:
+ case OP_AND_LONG_2ADDR:
+ firstOp = ARMV5TE_AND_RR;
+ secondOp = ARMV5TE_AND_RR;
+ break;
+ case OP_OR_LONG:
+ case OP_OR_LONG_2ADDR:
+ firstOp = ARMV5TE_ORR;
+ secondOp = ARMV5TE_ORR;
+ break;
+ case OP_XOR_LONG:
+ case OP_XOR_LONG_2ADDR:
+ firstOp = ARMV5TE_EOR;
+ secondOp = ARMV5TE_EOR;
+ break;
+ case OP_NEG_LONG:
+ loadValuePair(cUnit, vSrc2, r2, r3);
+ loadConstant(cUnit, r1, 0);
+ newLIR3(cUnit, ARMV5TE_SUB_RRR, r0, r1, r2);
+ newLIR2(cUnit, ARMV5TE_SBC, r1, r3);
+ storeValuePair(cUnit, r0, r1, vDest, r2);
+ return false;
+ default:
+ LOGE("Invalid long arith op");
+ dvmAbort();
+ }
+ if (!callOut) {
+ loadValuePair(cUnit, vSrc1, r0, r1);
+ loadValuePair(cUnit, vSrc2, r2, r3);
+ genBinaryOpWide(cUnit, vDest, firstOp, secondOp);
+ } else {
+ loadValuePair(cUnit, vSrc2, r2, r3);
+ loadConstant(cUnit, r4PC, (int) callTgt);
+ loadValuePair(cUnit, vSrc1, r0, r1);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r4PC);
+ storeValuePair(cUnit, retReg, retReg+1, vDest, r4PC);
+ }
+ return false;
+}
+
+static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir, int vDest,
+ int vSrc1, int vSrc2)
+{
+ int armOp = ARMV5TE_BKPT;
+ bool callOut = false;
+ bool checkZero = false;
+ int retReg = r0;
+ void *callTgt;
+
+ /* TODO - find proper .h file to declare these */
+ int __aeabi_idivmod(int op1, int op2);
+ int __aeabi_idiv(int op1, int op2);
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_NEG_INT:
+ armOp = ARMV5TE_NEG;
+ break;
+ case OP_NOT_INT:
+ armOp = ARMV5TE_MVN;
+ break;
+ case OP_ADD_INT:
+ case OP_ADD_INT_2ADDR:
+ armOp = ARMV5TE_ADD_RRR;
+ break;
+ case OP_SUB_INT:
+ case OP_SUB_INT_2ADDR:
+ armOp = ARMV5TE_SUB_RRR;
+ break;
+ case OP_MUL_INT:
+ case OP_MUL_INT_2ADDR:
+ armOp = ARMV5TE_MUL;
+ break;
+ case OP_DIV_INT:
+ case OP_DIV_INT_2ADDR:
+ callOut = true;
+ checkZero = true;
+ callTgt = __aeabi_idiv;
+ retReg = r0;
+ break;
+ /* NOTE: returns in r1 */
+ case OP_REM_INT:
+ case OP_REM_INT_2ADDR:
+ callOut = true;
+ checkZero = true;
+ callTgt = __aeabi_idivmod;
+ retReg = r1;
+ break;
+ case OP_AND_INT:
+ case OP_AND_INT_2ADDR:
+ armOp = ARMV5TE_AND_RR;
+ break;
+ case OP_OR_INT:
+ case OP_OR_INT_2ADDR:
+ armOp = ARMV5TE_ORR;
+ break;
+ case OP_XOR_INT:
+ case OP_XOR_INT_2ADDR:
+ armOp = ARMV5TE_EOR;
+ break;
+ case OP_SHL_INT:
+ case OP_SHL_INT_2ADDR:
+ armOp = ARMV5TE_LSLV;
+ break;
+ case OP_SHR_INT:
+ case OP_SHR_INT_2ADDR:
+ armOp = ARMV5TE_ASRV;
+ break;
+ case OP_USHR_INT:
+ case OP_USHR_INT_2ADDR:
+ armOp = ARMV5TE_LSRV;
+ break;
+ default:
+ LOGE("Invalid word arith op: 0x%x(%d)",
+ mir->dalvikInsn.opCode, mir->dalvikInsn.opCode);
+ dvmAbort();
+ }
+ if (!callOut) {
+ loadValue(cUnit, vSrc1, r0);
+ loadValue(cUnit, vSrc2, r1);
+ genBinaryOp(cUnit, vDest, armOp);
+ } else {
+ loadValue(cUnit, vSrc2, r1);
+ loadConstant(cUnit, r2, (int) callTgt);
+ loadValue(cUnit, vSrc1, r0);
+ if (checkZero) {
+ genNullCheck(cUnit, r1, mir->offset, NULL);
+ }
+ newLIR1(cUnit, ARMV5TE_BLX_R, r2);
+ storeValue(cUnit, retReg, vDest, r2);
+ }
+ return false;
+}
+
+static bool genArithOp(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+ int vA = mir->dalvikInsn.vA;
+ int vB = mir->dalvikInsn.vB;
+ int vC = mir->dalvikInsn.vC;
+
+ if ((opCode >= OP_ADD_LONG_2ADDR) && (opCode <= OP_XOR_LONG_2ADDR)) {
+ return genArithOpLong(cUnit,mir, vA, vA, vB);
+ }
+ if ((opCode >= OP_ADD_LONG) && (opCode <= OP_XOR_LONG)) {
+ return genArithOpLong(cUnit,mir, vA, vB, vC);
+ }
+ if ((opCode >= OP_SHL_LONG_2ADDR) && (opCode <= OP_USHR_LONG_2ADDR)) {
+ return genShiftOpLong(cUnit,mir, vA, vA, vB);
+ }
+ if ((opCode >= OP_SHL_LONG) && (opCode <= OP_USHR_LONG)) {
+ return genShiftOpLong(cUnit,mir, vA, vB, vC);
+ }
+ if ((opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_USHR_INT_2ADDR)) {
+ return genArithOpInt(cUnit,mir, vA, vA, vB);
+ }
+ if ((opCode >= OP_ADD_INT) && (opCode <= OP_USHR_INT)) {
+ return genArithOpInt(cUnit,mir, vA, vB, vC);
+ }
+ if ((opCode >= OP_ADD_FLOAT_2ADDR) && (opCode <= OP_REM_FLOAT_2ADDR)) {
+ return genArithOpFloat(cUnit,mir, vA, vA, vB);
+ }
+ if ((opCode >= OP_ADD_FLOAT) && (opCode <= OP_REM_FLOAT)) {
+ return genArithOpFloat(cUnit,mir, vA, vB, vC);
+ }
+ if ((opCode >= OP_ADD_DOUBLE_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) {
+ return genArithOpDouble(cUnit,mir, vA, vA, vB);
+ }
+ if ((opCode >= OP_ADD_DOUBLE) && (opCode <= OP_REM_DOUBLE)) {
+ return genArithOpDouble(cUnit,mir, vA, vB, vC);
+ }
+ return true;
+}
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir, void *funct,
+ int srcSize, int tgtSize)
+{
+ loadConstant(cUnit, r2, (int)funct);
+ if (srcSize == 1) {
+ loadValue(cUnit, mir->dalvikInsn.vB, r0);
+ } else {
+ loadValuePair(cUnit, mir->dalvikInsn.vB, r0, r1);
+ }
+ newLIR1(cUnit, ARMV5TE_BLX_R, r2);
+ if (tgtSize == 1) {
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r1);
+ } else {
+ storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2);
+ }
+ return false;
+}
+
+/* Experimental example of completely inlining a native replacement */
+static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
+{
+ int offset = (int) &((InterpState *) NULL)->retval;
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ assert(dInsn->vA == 1);
+ loadValue(cUnit, dInsn->arg[0], r0);
+ loadConstant(cUnit, r1, gDvm.offJavaLangString_count);
+ genNullCheck(cUnit, r0, mir->offset, NULL);
+ newLIR3(cUnit, ARMV5TE_LDR_RRR, r0, r0, r1);
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rGLUE, offset >> 2);
+ return false;
+}
+
+static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir,
+ DecodedInstruction *dInsn,
+ Armv5teLIR **pcrLabel)
+{
+ unsigned int i;
+ unsigned int regMask = 0;
+
+ /* Load arguments to r0..r4 */
+ for (i = 0; i < dInsn->vA; i++) {
+ regMask |= 1 << i;
+ loadValue(cUnit, dInsn->arg[i], i);
+ }
+ if (regMask) {
+ /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+ newLIR2(cUnit, ARMV5TE_MOV_RR, r7, rFP);
+ newLIR2(cUnit, ARMV5TE_SUB_RI8, r7,
+ sizeof(StackSaveArea) + (dInsn->vA << 2));
+ /* generate null check */
+ if (pcrLabel) {
+ *pcrLabel = genNullCheck(cUnit, r0, mir->offset, NULL);
+ }
+ newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask);
+ }
+}
+
+static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir,
+ DecodedInstruction *dInsn,
+ Armv5teLIR **pcrLabel)
+{
+ int srcOffset = dInsn->vC << 2;
+ int numArgs = dInsn->vA;
+ int regMask;
+ /*
+ * r4PC : &rFP[vC]
+ * r7: &newFP[0]
+ */
+ if (srcOffset < 8) {
+ newLIR3(cUnit, ARMV5TE_ADD_RRI3, r4PC, rFP, srcOffset);
+ } else {
+ loadConstant(cUnit, r4PC, srcOffset);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, r4PC, rFP, r4PC);
+ }
+ /* load [r0 .. min(numArgs,4)] */
+ regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1;
+ newLIR2(cUnit, ARMV5TE_LDMIA, r4PC, regMask);
+
+ if (sizeof(StackSaveArea) + (numArgs << 2) < 256) {
+ newLIR2(cUnit, ARMV5TE_MOV_RR, r7, rFP);
+ newLIR2(cUnit, ARMV5TE_SUB_RI8, r7,
+ sizeof(StackSaveArea) + (numArgs << 2));
+ } else {
+ loadConstant(cUnit, r7, sizeof(StackSaveArea) + (numArgs << 2));
+ newLIR3(cUnit, ARMV5TE_SUB_RRR, r7, rFP, r7);
+ }
+
+ /* generate null check */
+ if (pcrLabel) {
+ *pcrLabel = genNullCheck(cUnit, r0, mir->offset, NULL);
+ }
+
+ /*
+ * Handle remaining 4n arguments:
+ * store previously loaded 4 values and load the next 4 values
+ */
+ if (numArgs >= 8) {
+ Armv5teLIR *loopLabel = NULL;
+ /*
+ * r0 contains "this" and it will be used later, so push it to the stack
+ * first. Pushing r5 is just for stack alignment purposes.
+ */
+ newLIR1(cUnit, ARMV5TE_PUSH, 1 << r0 | 1 << 5);
+ /* No need to generate the loop structure if numArgs <= 11 */
+ if (numArgs > 11) {
+ loadConstant(cUnit, 5, ((numArgs - 4) >> 2) << 2);
+ loopLabel = newLIR0(cUnit, ARMV5TE_PSEUDO_TARGET_LABEL);
+ }
+ newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask);
+ newLIR2(cUnit, ARMV5TE_LDMIA, r4PC, regMask);
+ /* No need to generate the loop structure if numArgs <= 11 */
+ if (numArgs > 11) {
+ newLIR2(cUnit, ARMV5TE_SUB_RI8, 5, 4);
+ genConditionalBranch(cUnit, ARM_COND_NE, loopLabel);
+ }
+ }
+
+ /* Save the last batch of loaded values */
+ newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask);
+
+ /* Generate the loop epilogue - don't use r0 */
+ if ((numArgs > 4) && (numArgs % 4)) {
+ regMask = ((1 << (numArgs & 0x3)) - 1) << 1;
+ newLIR2(cUnit, ARMV5TE_LDMIA, r4PC, regMask);
+ }
+ if (numArgs >= 8)
+ newLIR1(cUnit, ARMV5TE_POP, 1 << r0 | 1 << 5);
+
+ /* Save the modulo 4 arguments */
+ if ((numArgs > 4) && (numArgs % 4)) {
+ newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask);
+ }
+}
+
+static void genInvokeCommon(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+ Armv5teLIR *labelList, Armv5teLIR *pcrLabel,
+ const Method *calleeMethod)
+{
+ Armv5teLIR *retChainingCell = &labelList[bb->fallThrough->id];
+
+ /* r1 = &retChainingCell */
+ Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL,
+ r1, 0);
+ /* r4PC = dalvikCallsite */
+ loadConstant(cUnit, r4PC,
+ (int) (cUnit->method->insns + mir->offset));
+ addrRetChain->generic.target = (LIR *) retChainingCell;
+ /*
+ * r0 = calleeMethod (loaded upon calling genInvokeCommon)
+ * r1 = &ChainingCell
+ * r4PC = callsiteDPC
+ */
+ if (dvmIsNativeMethod(calleeMethod)) {
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(INVOKE_STATS)
+ gDvmJit.invokeNoOpt++;
+#endif
+ } else {
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_CHAIN);
+#if defined(INVOKE_STATS)
+ gDvmJit.invokeChain++;
+#endif
+ genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+ }
+ /* Handle exceptions using the interpreter */
+ genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/* Geneate a branch to go back to the interpreter */
+static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset)
+{
+ /* r0 = dalvik pc */
+ loadConstant(cUnit, r0, (int) (cUnit->method->insns + offset));
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, rGLUE,
+ offsetof(InterpState, jitToInterpEntries.dvmJitToInterpPunt) >> 2);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r1);
+}
+
+/*
+ * Attempt to single step one instruction using the interpreter and return
+ * to the compiled code for the next Dalvik instruction
+ */
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir)
+{
+ int flags = dexGetInstrFlags(gDvm.instrFlags, mir->dalvikInsn.opCode);
+ int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
+ kInstrCanThrow;
+ if ((mir->next == NULL) || (flags & flagsToCheck)) {
+ genPuntToInterp(cUnit, mir->offset);
+ return;
+ }
+ int entryAddr = offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpSingleStep);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r2, rGLUE, entryAddr >> 2);
+ /* r0 = dalvik pc */
+ loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+ /* r1 = dalvik pc of following instruction */
+ loadConstant(cUnit, r1, (int) (cUnit->method->insns + mir->next->offset));
+ newLIR1(cUnit, ARMV5TE_BLX_R, r2);
+}
+
+
+/*****************************************************************************/
+/*
+ * The following are the first-level codegen routines that analyze the format
+ * of each bytecode then either dispatch special purpose codegen routines
+ * or produce corresponding Thumb instructions directly.
+ */
+
+static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir,
+ BasicBlock *bb, Armv5teLIR *labelList)
+{
+ /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */
+ genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+ return false;
+}
+
+static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ if (((dalvikOpCode >= OP_UNUSED_3E) && (dalvikOpCode <= OP_UNUSED_43)) ||
+ ((dalvikOpCode >= OP_UNUSED_E3) && (dalvikOpCode <= OP_UNUSED_EC))) {
+ LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode);
+ return true;
+ }
+ switch (dalvikOpCode) {
+ case OP_RETURN_VOID:
+ genReturnCommon(cUnit,mir);
+ break;
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode);
+ return true;
+ case OP_NOP:
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir)
+{
+ switch (mir->dalvikInsn.opCode) {
+ case OP_CONST:
+ case OP_CONST_4:
+ loadConstant(cUnit, r0, mir->dalvikInsn.vB);
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r1);
+ break;
+ case OP_CONST_WIDE_32:
+ loadConstant(cUnit, r0, mir->dalvikInsn.vB);
+ newLIR3(cUnit, ARMV5TE_ASR, r1, r0, 31);
+ storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir)
+{
+ switch (mir->dalvikInsn.opCode) {
+ case OP_CONST_HIGH16:
+ loadConstant(cUnit, r0, mir->dalvikInsn.vB << 16);
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r1);
+ break;
+ case OP_CONST_WIDE_HIGH16:
+ loadConstant(cUnit, r1, mir->dalvikInsn.vB << 16);
+ loadConstant(cUnit, r0, 0);
+ storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir)
+{
+ /* For OP_THROW_VERIFICATION_ERROR */
+ genInterpSingleStep(cUnit, mir);
+ return false;
+}
+
+static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir)
+{
+ switch (mir->dalvikInsn.opCode) {
+ /*
+ * TODO: Verify that we can ignore the resolution check here because
+ * it will have already successfully been interpreted once
+ */
+ case OP_CONST_STRING_JUMBO:
+ case OP_CONST_STRING: {
+ void *strPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]);
+ assert(strPtr != NULL);
+ loadConstant(cUnit, r0, (int) strPtr );
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r1);
+ break;
+ }
+ /*
+ * TODO: Verify that we can ignore the resolution check here because
+ * it will have already successfully been interpreted once
+ */
+ case OP_CONST_CLASS: {
+ void *classPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+ assert(classPtr != NULL);
+ loadConstant(cUnit, r0, (int) classPtr );
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r1);
+ break;
+ }
+ case OP_SGET_OBJECT:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_CHAR:
+ case OP_SGET_BYTE:
+ case OP_SGET_SHORT:
+ case OP_SGET: {
+ int valOffset = (int)&((struct StaticField*)NULL)->value;
+ void *fieldPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+ assert(fieldPtr != NULL);
+ loadConstant(cUnit, r0, (int) fieldPtr + valOffset);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, 0);
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r2);
+ break;
+ }
+ case OP_SGET_WIDE: {
+ int valOffset = (int)&((struct StaticField*)NULL)->value;
+ void *fieldPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+ assert(fieldPtr != NULL);
+ loadConstant(cUnit, r2, (int) fieldPtr + valOffset);
+ newLIR2(cUnit, ARMV5TE_LDMIA, r2, (1<<r0 | 1<<r1));
+ storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2);
+ break;
+ }
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_SHORT:
+ case OP_SPUT: {
+ int valOffset = (int)&((struct StaticField*)NULL)->value;
+ void *fieldPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+ assert(fieldPtr != NULL);
+ loadValue(cUnit, mir->dalvikInsn.vA, r0);
+ loadConstant(cUnit, r1, (int) fieldPtr + valOffset);
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, r1, 0);
+ break;
+ }
+ case OP_SPUT_WIDE: {
+ int valOffset = (int)&((struct StaticField*)NULL)->value;
+ void *fieldPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+ assert(fieldPtr != NULL);
+ loadValuePair(cUnit, mir->dalvikInsn.vA, r0, r1);
+ loadConstant(cUnit, r2, (int) fieldPtr + valOffset);
+ newLIR2(cUnit, ARMV5TE_STMIA, r2, (1<<r0 | 1<<r1));
+ break;
+ }
+ case OP_NEW_INSTANCE: {
+ ClassObject *classPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+ assert(classPtr != NULL);
+ assert(classPtr->status & CLASS_INITIALIZED);
+ if ((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) != 0) {
+ /* It's going to throw, just let the interp. deal with it. */
+ genInterpSingleStep(cUnit, mir);
+ return false;
+ }
+ loadConstant(cUnit, r0, (int) classPtr);
+ loadConstant(cUnit, r4PC, (int)dvmAllocObject);
+ genExportPC(cUnit, mir, r2, r3 );
+ loadConstant(cUnit, r1, ALLOC_DONT_TRACK);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r4PC);
+ /*
+ * TODO: As coded, we'll bail and reinterpret on alloc failure.
+ * Need a general mechanism to bail to thrown exception code.
+ */
+ genNullCheck(cUnit, r0, mir->offset, NULL);
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r1);
+ break;
+ }
+ case OP_CHECK_CAST: {
+ ClassObject *classPtr =
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+ loadConstant(cUnit, r1, (int) classPtr );
+ loadValue(cUnit, mir->dalvikInsn.vA, r0); /* Ref */
+ /*
+ * TODO - in theory classPtr should be resoved by the time this
+ * instruction made into a trace, but we are seeing NULL at runtime
+ * so this check is temporarily used as a workaround.
+ */
+ Armv5teLIR * pcrLabel = genNullCheck(cUnit, r1, mir->offset, NULL);
+ newLIR2(cUnit, ARMV5TE_CMP_RI8, r0, 0); /* Null? */
+ Armv5teLIR *branch1 =
+ newLIR2(cUnit, ARMV5TE_B_COND, 4, ARM_COND_EQ);
+ /* r0 now contains object->clazz */
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0,
+ offsetof(Object, clazz) >> 2);
+ loadConstant(cUnit, r4PC, (int)dvmInstanceofNonTrivial);
+ newLIR2(cUnit, ARMV5TE_CMP_RR, r0, r1);
+ Armv5teLIR *branch2 =
+ newLIR2(cUnit, ARMV5TE_B_COND, 2, ARM_COND_EQ);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r4PC);
+ /* check cast failed - punt to the interpreter */
+ genNullCheck(cUnit, r0, mir->offset, pcrLabel);
+ /* check cast passed - branch target here */
+ Armv5teLIR *target = newLIR0(cUnit, ARMV5TE_PSEUDO_TARGET_LABEL);
+ branch1->generic.target = (LIR *)target;
+ branch2->generic.target = (LIR *)target;
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ switch (dalvikOpCode) {
+ case OP_MOVE_EXCEPTION: {
+ int offset = offsetof(InterpState, self);
+ int exOffset = offsetof(Thread, exception);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, r0, exOffset >> 2);
+ storeValue(cUnit, r1, mir->dalvikInsn.vA, r0);
+ break;
+ }
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_OBJECT: {
+ int offset = offsetof(InterpState, retval);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2);
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r1);
+ break;
+ }
+ case OP_MOVE_RESULT_WIDE: {
+ int offset = offsetof(InterpState, retval);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, rGLUE, (offset >> 2)+1);
+ storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2);
+ break;
+ }
+ case OP_RETURN_WIDE: {
+ loadValuePair(cUnit, mir->dalvikInsn.vA, r0, r1);
+ int offset = offsetof(InterpState, retval);
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rGLUE, offset >> 2);
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, r1, rGLUE, (offset >> 2)+1);
+ genReturnCommon(cUnit,mir);
+ break;
+ }
+ case OP_RETURN:
+ case OP_RETURN_OBJECT: {
+ loadValue(cUnit, mir->dalvikInsn.vA, r0);
+ int offset = offsetof(InterpState, retval);
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rGLUE, offset >> 2);
+ genReturnCommon(cUnit,mir);
+ break;
+ }
+ /*
+ * TODO-VERIFY: May be playing a bit fast and loose here. As coded,
+ * a failure on lock/unlock will cause us to revert to the interpeter
+ * to try again. This means we essentially ignore the first failure on
+ * the assumption that the interpreter will correctly handle the 2nd.
+ */
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT: {
+ int offset = offsetof(InterpState, self);
+ loadValue(cUnit, mir->dalvikInsn.vA, r1);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2);
+ if (dalvikOpCode == OP_MONITOR_ENTER) {
+ loadConstant(cUnit, r2, (int)dvmLockObject);
+ } else {
+ loadConstant(cUnit, r2, (int)dvmUnlockObject);
+ }
+ /*
+ * TODO-VERIFY: Note that we're not doing an EXPORT_PC, as
+ * Lock/unlock won't throw, and this code does not support
+ * DEADLOCK_PREDICTION or MONITOR_TRACKING. Should it?
+ */
+ genNullCheck(cUnit, r1, mir->offset, NULL);
+ /* Do the call */
+ newLIR1(cUnit, ARMV5TE_BLX_R, r2);
+ break;
+ }
+ case OP_THROW: {
+ genInterpSingleStep(cUnit, mir);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+ int vSrc1Dest = mir->dalvikInsn.vA;
+ int vSrc2 = mir->dalvikInsn.vB;
+
+ /* TODO - find the proper include file to declare these */
+ float __aeabi_i2f( int op1 );
+ int __aeabi_f2iz( float op1 );
+ float __aeabi_d2f( double op1 );
+ double __aeabi_f2d( float op1 );
+ double __aeabi_i2d( int op1 );
+ int __aeabi_d2iz( double op1 );
+ long __aeabi_f2lz( float op1 );
+ float __aeabi_l2f( long op1 );
+ long __aeabi_d2lz( double op1 );
+ double __aeabi_l2d( long op1 );
+
+ if ( (opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) {
+ return genArithOp( cUnit, mir );
+ }
+
+ switch (opCode) {
+ case OP_INT_TO_FLOAT:
+ return genConversion(cUnit, mir, (void*)__aeabi_i2f, 1, 1);
+ case OP_FLOAT_TO_INT:
+ return genConversion(cUnit, mir, (void*)__aeabi_f2iz, 1, 1);
+ case OP_DOUBLE_TO_FLOAT:
+ return genConversion(cUnit, mir, (void*)__aeabi_d2f, 2, 1);
+ case OP_FLOAT_TO_DOUBLE:
+ return genConversion(cUnit, mir, (void*)__aeabi_f2d, 1, 2);
+ case OP_INT_TO_DOUBLE:
+ return genConversion(cUnit, mir, (void*)__aeabi_i2d, 1, 2);
+ case OP_DOUBLE_TO_INT:
+ return genConversion(cUnit, mir, (void*)__aeabi_d2iz, 2, 1);
+ case OP_FLOAT_TO_LONG:
+ return genConversion(cUnit, mir, (void*)__aeabi_f2lz, 1, 2);
+ case OP_LONG_TO_FLOAT:
+ return genConversion(cUnit, mir, (void*)__aeabi_l2f, 2, 1);
+ case OP_DOUBLE_TO_LONG:
+ return genConversion(cUnit, mir, (void*)__aeabi_d2lz, 2, 2);
+ case OP_LONG_TO_DOUBLE:
+ return genConversion(cUnit, mir, (void*)__aeabi_l2d, 2, 2);
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ return genArithOpInt(cUnit, mir, vSrc1Dest, vSrc1Dest, vSrc2);
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ return genArithOpLong(cUnit,mir, vSrc1Dest, vSrc1Dest, vSrc2);
+ case OP_NEG_FLOAT:
+ return genArithOpFloat(cUnit,mir,vSrc1Dest,vSrc1Dest,vSrc2);
+ case OP_NEG_DOUBLE:
+ return genArithOpDouble(cUnit,mir,vSrc1Dest,vSrc1Dest,vSrc2);
+ case OP_MOVE_WIDE:
+ loadValuePair(cUnit, mir->dalvikInsn.vB, r0, r1);
+ storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2);
+ break;
+ case OP_INT_TO_LONG:
+ loadValue(cUnit, mir->dalvikInsn.vB, r0);
+ newLIR3(cUnit, ARMV5TE_ASR, r1, r0, 31);
+ storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2);
+ break;
+ case OP_MOVE:
+ case OP_MOVE_OBJECT:
+ case OP_LONG_TO_INT:
+ loadValue(cUnit, vSrc2, r0);
+ storeValue(cUnit, r0, vSrc1Dest, r1);
+ break;
+ case OP_INT_TO_BYTE:
+ loadValue(cUnit, vSrc2, r0);
+ newLIR3(cUnit, ARMV5TE_LSL, r0, r0, 24);
+ newLIR3(cUnit, ARMV5TE_ASR, r0, r0, 24);
+ storeValue(cUnit, r0, vSrc1Dest, r1);
+ break;
+ case OP_INT_TO_SHORT:
+ loadValue(cUnit, vSrc2, r0);
+ newLIR3(cUnit, ARMV5TE_LSL, r0, r0, 16);
+ newLIR3(cUnit, ARMV5TE_ASR, r0, r0, 16);
+ storeValue(cUnit, r0, vSrc1Dest, r1);
+ break;
+ case OP_INT_TO_CHAR:
+ loadValue(cUnit, vSrc2, r0);
+ newLIR3(cUnit, ARMV5TE_LSL, r0, r0, 16);
+ newLIR3(cUnit, ARMV5TE_LSR, r0, r0, 16);
+ storeValue(cUnit, r0, vSrc1Dest, r1);
+ break;
+ case OP_ARRAY_LENGTH: {
+ int lenOffset = offsetof(ArrayObject, length);
+ loadValue(cUnit, vSrc2, r0);
+ genNullCheck(cUnit, r0, mir->offset, NULL);
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, lenOffset >> 2);
+ storeValue(cUnit, r0, vSrc1Dest, r1);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ /* It takes few instructions to handle OP_CONST_WIDE_16 inline */
+ if (dalvikOpCode == OP_CONST_WIDE_16) {
+ int rDest = mir->dalvikInsn.vA;
+ int BBBB = mir->dalvikInsn.vB;
+ int rLow = r0, rHigh = r1;
+ if (BBBB == 0) {
+ newLIR2(cUnit, ARMV5TE_MOV_IMM, rLow, 0);
+ rHigh = rLow;
+ } else if (BBBB > 0 && BBBB <= 255) {
+ /* rLow = ssssBBBB */
+ newLIR2(cUnit, ARMV5TE_MOV_IMM, rLow, BBBB);
+ /* rHigh = 0 */
+ newLIR2(cUnit, ARMV5TE_MOV_IMM, rHigh, 0);
+ } else {
+ loadConstant(cUnit, rLow, BBBB);
+ /*
+ * arithmetic-shift-right 32 bits to get the high half of long
+ * [63..32]
+ */
+ newLIR3(cUnit, ARMV5TE_ASR, rHigh, rLow, 0);
+ }
+
+ /* Save the long values to the specified Dalvik register pair */
+ /*
+ * If rDest is no greater than 30, use two "str rd, [rFP + immed_5]"
+ * instructions to store the results. Effective address is
+ * rFP + immed_5 << 2.
+ */
+ if (rDest < 31) {
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, rLow, rFP, rDest);
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, rHigh, rFP, rDest+1);
+ } else {
+ /*
+ * Otherwise just load the frame offset from the constant pool and add
+ * it to rFP. Then use stmia to store the results to the specified
+ * register pair.
+ */
+ /* Need to replicate the content in r0 to r1 */
+ if (rLow == rHigh) {
+ newLIR3(cUnit, ARMV5TE_ADD_RRI3, rLow+1, rLow, 0);
+ }
+ /* load the rFP offset into r2 */
+ loadConstant(cUnit, r2, rDest*4);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, rFP, r2);
+ newLIR2(cUnit, ARMV5TE_STMIA, r2, (1<<r0 | 1 << r1));
+ }
+ } else if (dalvikOpCode == OP_CONST_16) {
+ int rDest = mir->dalvikInsn.vA;
+ int BBBB = mir->dalvikInsn.vB;
+ if (BBBB >= 0 && BBBB <= 255) {
+ /* r0 = BBBB */
+ newLIR2(cUnit, ARMV5TE_MOV_IMM, r0, BBBB);
+ } else {
+ loadConstant(cUnit, r0, BBBB);
+ }
+
+ /* Save the constant to the specified Dalvik register */
+ /*
+ * If rDest is no greater than 31, effective address is
+ * rFP + immed_5 << 2.
+ */
+ if (rDest < 32) {
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rFP, rDest);
+ } else {
+ /*
+ * Otherwise just load the frame offset from the constant pool and add
+ * it to rFP. Then use stmia to store the results to the specified
+ * register pair.
+ */
+ /* load the rFP offset into r2 */
+ loadConstant(cUnit, r2, rDest*4);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, rFP, r2);
+ newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, r2, 0);
+ }
+ } else {
+ return true;
+ }
+ return false;
+}
+
+/* Compare agaist zero */
+static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+ Armv5teLIR *labelList)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ Armv5teConditionCode cond;
+
+ loadValue(cUnit, mir->dalvikInsn.vA, r0);
+ newLIR2(cUnit, ARMV5TE_CMP_RI8, r0, 0);
+
+ switch (dalvikOpCode) {
+ case OP_IF_EQZ:
+ cond = ARM_COND_EQ;
+ break;
+ case OP_IF_NEZ:
+ cond = ARM_COND_NE;
+ break;
+ case OP_IF_LTZ:
+ cond = ARM_COND_LT;
+ break;
+ case OP_IF_GEZ:
+ cond = ARM_COND_GE;
+ break;
+ case OP_IF_GTZ:
+ cond = ARM_COND_GT;
+ break;
+ case OP_IF_LEZ:
+ cond = ARM_COND_LE;
+ break;
+ default:
+ cond = 0;
+ LOGE("Unexpected opcode (%d) for Fmt21t\n", dalvikOpCode);
+ dvmAbort();
+ }
+ genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
+ /* This mostly likely will be optimized away in a later phase */
+ genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+ return false;
+}
+
+static bool handleFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ int vSrc = mir->dalvikInsn.vB;
+ int vDest = mir->dalvikInsn.vA;
+ int lit = mir->dalvikInsn.vC;
+ int armOp;
+
+ /* TODO: find the proper .h file to declare these */
+ int __aeabi_idivmod(int op1, int op2);
+ int __aeabi_idiv(int op1, int op2);
+
+ switch (dalvikOpCode) {
+ case OP_ADD_INT_LIT8:
+ case OP_ADD_INT_LIT16:
+ loadValue(cUnit, vSrc, r0);
+ if (lit <= 255 && lit >= 0) {
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, r0, lit);
+ storeValue(cUnit, r0, vDest, r1);
+ } else if (lit >= -255 && lit <= 0) {
+ /* Convert to a small constant subtraction */
+ newLIR2(cUnit, ARMV5TE_SUB_RI8, r0, -lit);
+ storeValue(cUnit, r0, vDest, r1);
+ } else {
+ loadConstant(cUnit, r1, lit);
+ genBinaryOp(cUnit, vDest, ARMV5TE_ADD_RRR);
+ }
+ break;
+
+ case OP_RSUB_INT_LIT8:
+ case OP_RSUB_INT:
+ loadValue(cUnit, vSrc, r1);
+ loadConstant(cUnit, r0, lit);
+ genBinaryOp(cUnit, vDest, ARMV5TE_SUB_RRR);
+ break;
+
+ case OP_MUL_INT_LIT8:
+ case OP_MUL_INT_LIT16:
+ case OP_AND_INT_LIT8:
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT8:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT8:
+ case OP_XOR_INT_LIT16:
+ loadValue(cUnit, vSrc, r0);
+ loadConstant(cUnit, r1, lit);
+ switch (dalvikOpCode) {
+ case OP_MUL_INT_LIT8:
+ case OP_MUL_INT_LIT16:
+ armOp = ARMV5TE_MUL;
+ break;
+ case OP_AND_INT_LIT8:
+ case OP_AND_INT_LIT16:
+ armOp = ARMV5TE_AND_RR;
+ break;
+ case OP_OR_INT_LIT8:
+ case OP_OR_INT_LIT16:
+ armOp = ARMV5TE_ORR;
+ break;
+ case OP_XOR_INT_LIT8:
+ case OP_XOR_INT_LIT16:
+ armOp = ARMV5TE_EOR;
+ break;
+ default:
+ dvmAbort();
+ }
+ genBinaryOp(cUnit, vDest, armOp);
+ break;
+
+ case OP_SHL_INT_LIT8:
+ case OP_SHR_INT_LIT8:
+ case OP_USHR_INT_LIT8:
+ loadValue(cUnit, vSrc, r0);
+ switch (dalvikOpCode) {
+ case OP_SHL_INT_LIT8:
+ armOp = ARMV5TE_LSL;
+ break;
+ case OP_SHR_INT_LIT8:
+ armOp = ARMV5TE_ASR;
+ break;
+ case OP_USHR_INT_LIT8:
+ armOp = ARMV5TE_LSR;
+ break;
+ default: dvmAbort();
+ }
+ newLIR3(cUnit, armOp, r0, r0, lit);
+ storeValue(cUnit, r0, vDest, r1);
+ break;
+
+ case OP_DIV_INT_LIT8:
+ case OP_DIV_INT_LIT16:
+ if (lit == 0) {
+ /* Let the interpreter deal with div by 0 */
+ genInterpSingleStep(cUnit, mir);
+ return false;
+ }
+ loadConstant(cUnit, r2, (int)__aeabi_idiv);
+ loadConstant(cUnit, r1, lit);
+ loadValue(cUnit, vSrc, r0);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r2);
+ storeValue(cUnit, r0, vDest, r2);
+ break;
+
+ case OP_REM_INT_LIT8:
+ case OP_REM_INT_LIT16:
+ if (lit == 0) {
+ /* Let the interpreter deal with div by 0 */
+ genInterpSingleStep(cUnit, mir);
+ return false;
+ }
+ loadConstant(cUnit, r2, (int)__aeabi_idivmod);
+ loadConstant(cUnit, r1, lit);
+ loadValue(cUnit, vSrc, r0);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r2);
+ storeValue(cUnit, r1, vDest, r2);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ int fieldOffset;
+
+ if (dalvikOpCode >= OP_IGET && dalvikOpCode <= OP_IPUT_SHORT) {
+ InstField *pInstField = (InstField *)
+ cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC];
+ int fieldOffset;
+
+ assert(pInstField != NULL);
+ fieldOffset = pInstField->byteOffset;
+ } else {
+ /* To make the compiler happy */
+ fieldOffset = 0;
+ }
+ switch (dalvikOpCode) {
+ /*
+ * TODO: I may be assuming too much here.
+ * Verify what is known at JIT time.
+ */
+ case OP_NEW_ARRAY: {
+ void *classPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+ assert(classPtr != NULL);
+ loadValue(cUnit, mir->dalvikInsn.vB, r1); /* Len */
+ loadConstant(cUnit, r0, (int) classPtr );
+ loadConstant(cUnit, r4PC, (int)dvmAllocArrayByClass);
+ Armv5teLIR *pcrLabel =
+ genRegImmCheck(cUnit, ARM_COND_MI, r1, 0, mir->offset, NULL);
+ genExportPC(cUnit, mir, r2, r3 );
+ newLIR2(cUnit, ARMV5TE_MOV_IMM,r2,ALLOC_DONT_TRACK);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r4PC);
+ /*
+ * TODO: As coded, we'll bail and reinterpret on alloc failure.
+ * Need a general mechanism to bail to thrown exception code.
+ */
+ genNullCheck(cUnit, r0, mir->offset, pcrLabel);
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r1);
+ break;
+ }
+ /*
+ * TODO: I may be assuming too much here.
+ * Verify what is known at JIT time.
+ */
+ case OP_INSTANCE_OF: {
+ ClassObject *classPtr =
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+ assert(classPtr != NULL);
+ loadValue(cUnit, mir->dalvikInsn.vB, r1); /* Ref */
+ loadConstant(cUnit, r2, (int) classPtr );
+ loadConstant(cUnit, r0, 1); /* Assume true */
+ newLIR2(cUnit, ARMV5TE_CMP_RI8, r1, 0); /* Null? */
+ Armv5teLIR *branch1 = newLIR2(cUnit, ARMV5TE_B_COND, 4,
+ ARM_COND_EQ);
+ /* r1 now contains object->clazz */
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, r1,
+ offsetof(Object, clazz) >> 2);
+ loadConstant(cUnit, r4PC, (int)dvmInstanceofNonTrivial);
+ newLIR2(cUnit, ARMV5TE_CMP_RR, r1, r2);
+ Armv5teLIR *branch2 = newLIR2(cUnit, ARMV5TE_B_COND, 2,
+ ARM_COND_EQ);
+ newLIR2(cUnit, ARMV5TE_MOV_RR, r0, r1);
+ newLIR2(cUnit, ARMV5TE_MOV_RR, r1, r2);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r4PC);
+ /* branch target here */
+ Armv5teLIR *target = newLIR0(cUnit, ARMV5TE_PSEUDO_TARGET_LABEL);
+ storeValue(cUnit, r0, mir->dalvikInsn.vA, r1);
+ branch1->generic.target = (LIR *)target;
+ branch2->generic.target = (LIR *)target;
+ break;
+ }
+ case OP_IGET_WIDE:
+ genIGetWide(cUnit, mir, fieldOffset);
+ break;
+ case OP_IGET:
+ case OP_IGET_OBJECT:
+ genIGet(cUnit, mir, ARMV5TE_LDR_RRR, fieldOffset);
+ break;
+ case OP_IGET_BOOLEAN:
+ genIGet(cUnit, mir, ARMV5TE_LDRB_RRR, fieldOffset);
+ break;
+ case OP_IGET_BYTE:
+ genIGet(cUnit, mir, ARMV5TE_LDRSB_RRR, fieldOffset);
+ break;
+ case OP_IGET_CHAR:
+ genIGet(cUnit, mir, ARMV5TE_LDRH_RRR, fieldOffset);
+ break;
+ case OP_IGET_SHORT:
+ genIGet(cUnit, mir, ARMV5TE_LDRSH_RRR, fieldOffset);
+ break;
+ case OP_IPUT_WIDE:
+ genIPutWide(cUnit, mir, fieldOffset);
+ break;
+ case OP_IPUT:
+ case OP_IPUT_OBJECT:
+ genIPut(cUnit, mir, ARMV5TE_STR_RRR, fieldOffset);
+ break;
+ case OP_IPUT_SHORT:
+ case OP_IPUT_CHAR:
+ genIPut(cUnit, mir, ARMV5TE_STRH_RRR, fieldOffset);
+ break;
+ case OP_IPUT_BYTE:
+ case OP_IPUT_BOOLEAN:
+ genIPut(cUnit, mir, ARMV5TE_STRB_RRR, fieldOffset);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ int fieldOffset = mir->dalvikInsn.vC;
+ switch (dalvikOpCode) {
+ case OP_IGET_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ genIGet(cUnit, mir, ARMV5TE_LDR_RRR, fieldOffset);
+ break;
+ case OP_IPUT_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ genIPut(cUnit, mir, ARMV5TE_STR_RRR, fieldOffset);
+ break;
+ case OP_IGET_WIDE_QUICK:
+ genIGetWide(cUnit, mir, fieldOffset);
+ break;
+ case OP_IPUT_WIDE_QUICK:
+ genIPutWide(cUnit, mir, fieldOffset);
+ break;
+ default:
+ return true;
+ }
+ return false;
+
+}
+
+/* Compare agaist zero */
+static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+ Armv5teLIR *labelList)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ Armv5teConditionCode cond;
+
+ loadValue(cUnit, mir->dalvikInsn.vA, r0);
+ loadValue(cUnit, mir->dalvikInsn.vB, r1);
+ newLIR2(cUnit, ARMV5TE_CMP_RR, r0, r1);
+
+ switch (dalvikOpCode) {
+ case OP_IF_EQ:
+ cond = ARM_COND_EQ;
+ break;
+ case OP_IF_NE:
+ cond = ARM_COND_NE;
+ break;
+ case OP_IF_LT:
+ cond = ARM_COND_LT;
+ break;
+ case OP_IF_GE:
+ cond = ARM_COND_GE;
+ break;
+ case OP_IF_GT:
+ cond = ARM_COND_GT;
+ break;
+ case OP_IF_LE:
+ cond = ARM_COND_LE;
+ break;
+ default:
+ cond = 0;
+ LOGE("Unexpected opcode (%d) for Fmt22t\n", dalvikOpCode);
+ dvmAbort();
+ }
+ genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
+ /* This mostly likely will be optimized away in a later phase */
+ genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+ return false;
+}
+
+static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+ int vSrc1Dest = mir->dalvikInsn.vA;
+ int vSrc2 = mir->dalvikInsn.vB;
+
+ switch (opCode) {
+ case OP_MOVE_16:
+ case OP_MOVE_OBJECT_16:
+ case OP_MOVE_FROM16:
+ case OP_MOVE_OBJECT_FROM16:
+ loadValue(cUnit, vSrc2, r0);
+ storeValue(cUnit, r0, vSrc1Dest, r1);
+ break;
+ case OP_MOVE_WIDE_16:
+ case OP_MOVE_WIDE_FROM16:
+ loadValuePair(cUnit, vSrc2, r0, r1);
+ storeValuePair(cUnit, r0, r1, vSrc1Dest, r2);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+ int vA = mir->dalvikInsn.vA;
+ int vB = mir->dalvikInsn.vB;
+ int vC = mir->dalvikInsn.vC;
+
+ if ( (opCode >= OP_ADD_INT) && (opCode <= OP_REM_DOUBLE)) {
+ return genArithOp( cUnit, mir );
+ }
+
+ switch (opCode) {
+ case OP_CMP_LONG:
+ loadValuePair(cUnit,vB, r0, r1);
+ loadValuePair(cUnit, vC, r2, r3);
+ genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
+ storeValue(cUnit, r0, vA, r1);
+ break;
+ case OP_CMPL_FLOAT:
+ loadValue(cUnit, vB, r0);
+ loadValue(cUnit, vC, r1);
+ genDispatchToHandler(cUnit, TEMPLATE_CMPL_FLOAT);
+ storeValue(cUnit, r0, vA, r1);
+ break;
+ case OP_CMPG_FLOAT:
+ loadValue(cUnit, vB, r0);
+ loadValue(cUnit, vC, r1);
+ genDispatchToHandler(cUnit, TEMPLATE_CMPG_FLOAT);
+ storeValue(cUnit, r0, vA, r1);
+ break;
+ case OP_CMPL_DOUBLE:
+ loadValueAddress(cUnit, vB, r0);
+ loadValueAddress(cUnit, vC, r1);
+ genDispatchToHandler(cUnit, TEMPLATE_CMPL_DOUBLE);
+ storeValue(cUnit, r0, vA, r1);
+ break;
+ case OP_CMPG_DOUBLE:
+ loadValueAddress(cUnit, vB, r0);
+ loadValueAddress(cUnit, vC, r1);
+ genDispatchToHandler(cUnit, TEMPLATE_CMPG_DOUBLE);
+ storeValue(cUnit, r0, vA, r1);
+ break;
+ case OP_AGET_WIDE:
+ genArrayGet(cUnit, mir, ARMV5TE_LDR_RRR, vB, vC, vA, 3);
+ break;
+ case OP_AGET:
+ case OP_AGET_OBJECT:
+ genArrayGet(cUnit, mir, ARMV5TE_LDR_RRR, vB, vC, vA, 2);
+ break;
+ case OP_AGET_BOOLEAN:
+ genArrayGet(cUnit, mir, ARMV5TE_LDRB_RRR, vB, vC, vA, 0);
+ break;
+ case OP_AGET_BYTE:
+ genArrayGet(cUnit, mir, ARMV5TE_LDRSB_RRR, vB, vC, vA, 0);
+ break;
+ case OP_AGET_CHAR:
+ genArrayGet(cUnit, mir, ARMV5TE_LDRH_RRR, vB, vC, vA, 1);
+ break;
+ case OP_AGET_SHORT:
+ genArrayGet(cUnit, mir, ARMV5TE_LDRSH_RRR, vB, vC, vA, 1);
+ break;
+ case OP_APUT_WIDE:
+ genArrayPut(cUnit, mir, ARMV5TE_STR_RRR, vB, vC, vA, 3);
+ break;
+ case OP_APUT:
+ case OP_APUT_OBJECT:
+ genArrayPut(cUnit, mir, ARMV5TE_STR_RRR, vB, vC, vA, 2);
+ break;
+ case OP_APUT_SHORT:
+ case OP_APUT_CHAR:
+ genArrayPut(cUnit, mir, ARMV5TE_STRH_RRR, vB, vC, vA, 1);
+ break;
+ case OP_APUT_BYTE:
+ case OP_APUT_BOOLEAN:
+ genArrayPut(cUnit, mir, ARMV5TE_STRB_RRR, vB, vC, vA, 0);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ switch (dalvikOpCode) {
+ case OP_FILL_ARRAY_DATA: {
+ loadConstant(cUnit, r4PC, (int)dvmInterpHandleFillArrayData);
+ loadValue(cUnit, mir->dalvikInsn.vA, r0);
+ loadConstant(cUnit, r1, (mir->dalvikInsn.vB << 1) +
+ (int) (cUnit->method->insns + mir->offset));
+ genExportPC(cUnit, mir, r2, r3 );
+ newLIR1(cUnit, ARMV5TE_BLX_R, r4PC);
+ genNullCheck(cUnit, r0, mir->offset, NULL);
+ break;
+ }
+ /*
+ * TODO
+ * - Add a 1 to 3-entry per-location cache here to completely
+ * bypass the dvmInterpHandle[Packed/Sparse]Switch call w/ chaining
+ * - Use out-of-line handlers for both of these
+ */
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH: {
+ if (dalvikOpCode == OP_PACKED_SWITCH) {
+ loadConstant(cUnit, r4PC, (int)dvmInterpHandlePackedSwitch);
+ } else {
+ loadConstant(cUnit, r4PC, (int)dvmInterpHandleSparseSwitch);
+ }
+ loadValue(cUnit, mir->dalvikInsn.vA, r1);
+ loadConstant(cUnit, r0, (mir->dalvikInsn.vB << 1) +
+ (int) (cUnit->method->insns + mir->offset));
+ newLIR1(cUnit, ARMV5TE_BLX_R, r4PC);
+ loadConstant(cUnit, r1, (int)(cUnit->method->insns + mir->offset));
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r2, rGLUE,
+ offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNoChain)
+ >> 2);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, r0, r0, r0);
+ newLIR3(cUnit, ARMV5TE_ADD_RRR, r4PC, r0, r1);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r2);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+ Armv5teLIR *labelList)
+{
+ Armv5teLIR *retChainingCell = &labelList[bb->fallThrough->id];
+ Armv5teLIR *pcrLabel = NULL;
+
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ switch (mir->dalvikInsn.opCode) {
+ /*
+ * calleeMethod = this->clazz->vtable[
+ * method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex
+ * ]
+ */
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_VIRTUAL_RANGE: {
+ int methodIndex =
+ cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]->
+ methodIndex;
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* r0 now contains this->clazz */
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0,
+ offsetof(Object, clazz) >> 2);
+ /* r1 = &retChainingCell */
+ Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL,
+ r1, 0);
+ /* r4PC = dalvikCallsite */
+ loadConstant(cUnit, r4PC,
+ (int) (cUnit->method->insns + mir->offset));
+
+ /* r0 now contains this->clazz->vtable */
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0,
+ offsetof(ClassObject, vtable) >> 2);
+ addrRetChain->generic.target = (LIR *) retChainingCell;
+
+ if (methodIndex < 32) {
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, methodIndex);
+ } else {
+ loadConstant(cUnit, r7, methodIndex<<2);
+ newLIR3(cUnit, ARMV5TE_LDR_RRR, r0, r0, r7);
+ }
+
+ /*
+ * r0 = calleeMethod,
+ * r1 = &ChainingCell,
+ * r4PC = callsiteDPC,
+ */
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(INVOKE_STATS)
+ gDvmJit.invokeNoOpt++;
+#endif
+ /* Handle exceptions using the interpreter */
+ genTrap(cUnit, mir->offset, pcrLabel);
+ break;
+ }
+ /*
+ * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex
+ * ->pResMethods[BBBB]->methodIndex]
+ */
+ /* TODO - not excersized in RunPerf.jar */
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE: {
+ int mIndex = cUnit->method->clazz->pDvmDex->
+ pResMethods[dInsn->vB]->methodIndex;
+ const Method *calleeMethod =
+ cUnit->method->clazz->super->vtable[mIndex];
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* r0 = calleeMethod */
+ loadConstant(cUnit, r0, (int) calleeMethod);
+
+ genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel,
+ calleeMethod);
+ break;
+ }
+ /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE: {
+ const Method *calleeMethod =
+ cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB];
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_DIRECT)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* r0 = calleeMethod */
+ loadConstant(cUnit, r0, (int) calleeMethod);
+
+ genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel,
+ calleeMethod);
+ break;
+ }
+ /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE: {
+ const Method *calleeMethod =
+ cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB];
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_STATIC)
+ genProcessArgsNoRange(cUnit, mir, dInsn,
+ NULL /* no null check */);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn,
+ NULL /* no null check */);
+
+ /* r0 = calleeMethod */
+ loadConstant(cUnit, r0, (int) calleeMethod);
+
+ genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel,
+ calleeMethod);
+ break;
+ }
+ /*
+ * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz,
+ * BBBB, method, method->clazz->pDvmDex)
+ */
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_INTERFACE_RANGE: {
+ int methodIndex = dInsn->vB;
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_INTERFACE)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* r0 now contains this->clazz */
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0,
+ offsetof(Object, clazz) >> 2);
+
+ /* r1 = BBBB */
+ loadConstant(cUnit, r1, dInsn->vB);
+
+ /* r2 = method (caller) */
+ loadConstant(cUnit, r2, (int) cUnit->method);
+
+ /* r3 = pDvmDex */
+ loadConstant(cUnit, r3, (int) cUnit->method->clazz->pDvmDex);
+
+ loadConstant(cUnit, r7,
+ (intptr_t) dvmFindInterfaceMethodInCache);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r7);
+
+ /* r0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */
+
+ /* r1 = &retChainingCell */
+ Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL,
+ r1, 0);
+ /* r4PC = dalvikCallsite */
+ loadConstant(cUnit, r4PC,
+ (int) (cUnit->method->insns + mir->offset));
+
+ addrRetChain->generic.target = (LIR *) retChainingCell;
+ /*
+ * r0 = this, r1 = calleeMethod,
+ * r1 = &ChainingCell,
+ * r4PC = callsiteDPC,
+ */
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(INVOKE_STATS)
+ gDvmJit.invokeNoOpt++;
+#endif
+ /* Handle exceptions using the interpreter */
+ genTrap(cUnit, mir->offset, pcrLabel);
+ break;
+ }
+ /* NOP */
+ case OP_INVOKE_DIRECT_EMPTY: {
+ return false;
+ }
+ case OP_FILLED_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY_RANGE: {
+ /* Just let the interpreter deal with these */
+ genInterpSingleStep(cUnit, mir);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir,
+ BasicBlock *bb, Armv5teLIR *labelList)
+{
+ Armv5teLIR *retChainingCell = &labelList[bb->fallThrough->id];
+ Armv5teLIR *pcrLabel = NULL;
+
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ switch (mir->dalvikInsn.opCode) {
+ /* calleeMethod = this->clazz->vtable[BBBB] */
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_VIRTUAL_QUICK: {
+ int methodIndex = dInsn->vB;
+ if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL_QUICK)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* r0 now contains this->clazz */
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0,
+ offsetof(Object, clazz) >> 2);
+ /* r1 = &retChainingCell */
+ Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL,
+ r1, 0);
+ /* r4PC = dalvikCallsite */
+ loadConstant(cUnit, r4PC,
+ (int) (cUnit->method->insns + mir->offset));
+
+ /* r0 now contains this->clazz->vtable */
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0,
+ offsetof(ClassObject, vtable) >> 2);
+ addrRetChain->generic.target = (LIR *) retChainingCell;
+
+ if (methodIndex < 32) {
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, methodIndex);
+ } else {
+ loadConstant(cUnit, r7, methodIndex<<2);
+ newLIR3(cUnit, ARMV5TE_LDR_RRR, r0, r0, r7);
+ }
+
+ /*
+ * r0 = calleeMethod,
+ * r1 = &ChainingCell,
+ * r4PC = callsiteDPC,
+ */
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(INVOKE_STATS)
+ gDvmJit.invokeNoOpt++;
+#endif
+ break;
+ }
+ /* calleeMethod = method->clazz->super->vtable[BBBB] */
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE: {
+ const Method *calleeMethod =
+ cUnit->method->clazz->super->vtable[dInsn->vB];
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER_QUICK)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* r0 = calleeMethod */
+ loadConstant(cUnit, r0, (int) calleeMethod);
+
+ genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel,
+ calleeMethod);
+ break;
+ }
+ /* calleeMethod = method->clazz->super->vtable[BBBB] */
+ default:
+ return true;
+ }
+ /* Handle exceptions using the interpreter */
+ genTrap(cUnit, mir->offset, pcrLabel);
+ return false;
+}
+
+/*
+ * NOTE: We assume here that the special native inline routines
+ * are side-effect free. By making this assumption, we can safely
+ * re-execute the routine from the interpreter if it decides it
+ * wants to throw an exception. We still need to EXPORT_PC(), though.
+ */
+static bool handleFmt3inline(CompilationUnit *cUnit, MIR *mir)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ switch( mir->dalvikInsn.opCode) {
+ case OP_EXECUTE_INLINE: {
+ unsigned int i;
+ const InlineOperation* inLineTable = dvmGetInlineOpsTable();
+ int offset = (int) &((InterpState *) NULL)->retval;
+ int operation = dInsn->vB;
+
+ if (!strcmp(inLineTable[operation].classDescriptor,
+ "Ljava/lang/String;") &&
+ !strcmp(inLineTable[operation].methodName,
+ "length") &&
+ !strcmp(inLineTable[operation].methodSignature,
+ "()I")) {
+ return genInlinedStringLength(cUnit,mir);
+ }
+
+ /* Materialize pointer to retval & push */
+ newLIR2(cUnit, ARMV5TE_MOV_RR, r4PC, rGLUE);
+ newLIR2(cUnit, ARMV5TE_ADD_RI8, r4PC, offset);
+ /* Push r4 and (just to take up space) r5) */
+ newLIR1(cUnit, ARMV5TE_PUSH, (1<<r4PC | 1<<rFP));
+
+ /* Get code pointer to inline routine */
+ loadConstant(cUnit, r4PC, (int)inLineTable[operation].func);
+
+ /* Export PC */
+ genExportPC(cUnit, mir, r0, r1 );
+
+ /* Load arguments to r0 through r3 as applicable */
+ for (i=0; i < dInsn->vA; i++) {
+ loadValue(cUnit, dInsn->arg[i], i);
+ }
+ /* Call inline routine */
+ newLIR1(cUnit, ARMV5TE_BLX_R, r4PC);
+
+ /* Strip frame */
+ newLIR1(cUnit, ARMV5TE_ADD_SPI7, 2);
+
+ /* Did we throw? If so, redo under interpreter*/
+ genNullCheck(cUnit, r0, mir->offset, NULL);
+
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt51l(CompilationUnit *cUnit, MIR *mir)
+{
+ loadConstant(cUnit, r0, mir->dalvikInsn.vB_wide & 0xFFFFFFFFUL);
+ loadConstant(cUnit, r1, (mir->dalvikInsn.vB_wide>>32) & 0xFFFFFFFFUL);
+ storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2);
+ return false;
+}
+
+/*****************************************************************************/
+/*
+ * The following are special processing routines that handle transfer of
+ * controls between compiled code and the interpreter. Certain VM states like
+ * Dalvik PC and special-purpose registers are reconstructed here.
+ */
+
+/* Chaining cell for normal-ending compiles (eg branches) */
+static void handleGenericChainingCell(CompilationUnit *cUnit,
+ unsigned int offset)
+{
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE,
+ offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNormal) >> 2);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r0);
+ addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
+}
+
+/*
+ * Chaining cell for instructions that immediately following a method
+ * invocation.
+ */
+static void handlePostInvokeChainingCell(CompilationUnit *cUnit,
+ unsigned int offset)
+{
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE,
+ offsetof(InterpState, jitToInterpEntries.dvmJitToTraceSelect) >> 2);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r0);
+ addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokeChainingCell(CompilationUnit *cUnit,
+ const Method *callee)
+{
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE,
+ offsetof(InterpState, jitToInterpEntries.dvmJitToTraceSelect) >> 2);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r0);
+ addWordData(cUnit, (int) (callee->insns), true);
+}
+
+/* Load the Dalvik PC into r0 and jump to the specified target */
+static void handlePCReconstruction(CompilationUnit *cUnit,
+ Armv5teLIR *targetLabel)
+{
+ Armv5teLIR **pcrLabel =
+ (Armv5teLIR **) cUnit->pcReconstructionList.elemList;
+ int numElems = cUnit->pcReconstructionList.numUsed;
+ int i;
+ for (i = 0; i < numElems; i++) {
+ dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]);
+ /* r0 = dalvik PC */
+ loadConstant(cUnit, r0, pcrLabel[i]->operands[0]);
+ genUnconditionalBranch(cUnit, targetLabel);
+ }
+}
+
+/* Entry function to invoke the backend of the JIT compiler */
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit)
+{
+ /* Used to hold the labels of each block */
+ Armv5teLIR *labelList =
+ dvmCompilerNew(sizeof(Armv5teLIR) * cUnit->numBlocks, true);
+ GrowableList chainingListByType[CHAINING_CELL_LAST];
+ int i;
+
+ /*
+ * Initialize the three chaining lists for generic, post-invoke, and invoke
+ * chains.
+ */
+ for (i = 0; i < CHAINING_CELL_LAST; i++) {
+ dvmInitGrowableList(&chainingListByType[i], 2);
+ }
+
+ BasicBlock **blockList = cUnit->blockList;
+
+ /* Handle the content in each basic block */
+ for (i = 0; i < cUnit->numBlocks; i++) {
+ blockList[i]->visited = true;
+ MIR *mir;
+
+ labelList[i].operands[0] = blockList[i]->startOffset;
+
+ if (blockList[i]->blockType >= CHAINING_CELL_LAST) {
+ /*
+ * Append the label pseudo LIR first. Chaining cells will be handled
+ * separately afterwards.
+ */
+ dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
+ }
+
+ if (blockList[i]->blockType == DALVIK_BYTECODE) {
+ labelList[i].opCode = ARMV5TE_PSEUDO_NORMAL_BLOCK_LABEL;
+ } else {
+ switch (blockList[i]->blockType) {
+ case CHAINING_CELL_GENERIC:
+ labelList[i].opCode = ARMV5TE_PSEUDO_CHAINING_CELL_GENERIC;
+ /* handle the codegen later */
+ dvmInsertGrowableList(
+ &chainingListByType[CHAINING_CELL_GENERIC], (void *) i);
+ break;
+ case CHAINING_CELL_INVOKE:
+ labelList[i].opCode = ARMV5TE_PSEUDO_CHAINING_CELL_INVOKE;
+ labelList[i].operands[0] =
+ (int) blockList[i]->containingMethod;
+ /* handle the codegen later */
+ dvmInsertGrowableList(
+ &chainingListByType[CHAINING_CELL_INVOKE], (void *) i);
+ break;
+ case CHAINING_CELL_POST_INVOKE:
+ labelList[i].opCode =
+ ARMV5TE_PSEUDO_CHAINING_CELL_POST_INVOKE;
+ /* handle the codegen later */
+ dvmInsertGrowableList(
+ &chainingListByType[CHAINING_CELL_POST_INVOKE],
+ (void *) i);
+ break;
+ case PC_RECONSTRUCTION:
+ /* Make sure exception handling block is next */
+ labelList[i].opCode =
+ ARMV5TE_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL;
+ assert (i == cUnit->numBlocks - 2);
+ handlePCReconstruction(cUnit, &labelList[i+1]);
+ break;
+ case EXCEPTION_HANDLING:
+ labelList[i].opCode = ARMV5TE_PSEUDO_EH_BLOCK_LABEL;
+ if (cUnit->pcReconstructionList.numUsed) {
+ newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, rGLUE,
+ offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpPunt)
+ >> 2);
+ newLIR1(cUnit, ARMV5TE_BLX_R, r1);
+ }
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+ for (mir = blockList[i]->firstMIRInsn; mir; mir = mir->next) {
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ InstructionFormat dalvikFormat =
+ dexGetInstrFormat(gDvm.instrFormat, dalvikOpCode);
+ newLIR2(cUnit, ARMV5TE_PSEUDO_DALVIK_BYTECODE_BOUNDARY,
+ mir->offset,dalvikOpCode);
+ bool notHandled;
+ /*
+ * Debugging: screen the opcode first to see if it is in the
+ * do[-not]-compile list
+ */
+ bool singleStepMe =
+ gDvmJit.includeSelectedOp !=
+ ((gDvmJit.opList[dalvikOpCode >> 3] &
+ (1 << (dalvikOpCode & 0x7))) !=
+ 0);
+ if (singleStepMe || cUnit->allSingleStep) {
+ notHandled = false;
+ genInterpSingleStep(cUnit, mir);
+ } else {
+ opcodeCoverage[dalvikOpCode]++;
+ switch (dalvikFormat) {
+ case kFmt10t:
+ case kFmt20t:
+ case kFmt30t:
+ notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit,
+ mir, blockList[i], labelList);
+ break;
+ case kFmt10x:
+ notHandled = handleFmt10x(cUnit, mir);
+ break;
+ case kFmt11n:
+ case kFmt31i:
+ notHandled = handleFmt11n_Fmt31i(cUnit, mir);
+ break;
+ case kFmt11x:
+ notHandled = handleFmt11x(cUnit, mir);
+ break;
+ case kFmt12x:
+ notHandled = handleFmt12x(cUnit, mir);
+ break;
+ case kFmt20bc:
+ notHandled = handleFmt20bc(cUnit, mir);
+ break;
+ case kFmt21c:
+ case kFmt31c:
+ notHandled = handleFmt21c_Fmt31c(cUnit, mir);
+ break;
+ case kFmt21h:
+ notHandled = handleFmt21h(cUnit, mir);
+ break;
+ case kFmt21s:
+ notHandled = handleFmt21s(cUnit, mir);
+ break;
+ case kFmt21t:
+ notHandled = handleFmt21t(cUnit, mir, blockList[i],
+ labelList);
+ break;
+ case kFmt22b:
+ case kFmt22s:
+ notHandled = handleFmt22b_Fmt22s(cUnit, mir);
+ break;
+ case kFmt22c:
+ notHandled = handleFmt22c(cUnit, mir);
+ break;
+ case kFmt22cs:
+ notHandled = handleFmt22cs(cUnit, mir);
+ break;
+ case kFmt22t:
+ notHandled = handleFmt22t(cUnit, mir, blockList[i],
+ labelList);
+ break;
+ case kFmt22x:
+ case kFmt32x:
+ notHandled = handleFmt22x_Fmt32x(cUnit, mir);
+ break;
+ case kFmt23x:
+ notHandled = handleFmt23x(cUnit, mir);
+ break;
+ case kFmt31t:
+ notHandled = handleFmt31t(cUnit, mir);
+ break;
+ case kFmt3rc:
+ case kFmt35c:
+ notHandled = handleFmt35c_3rc(cUnit, mir, blockList[i],
+ labelList);
+ break;
+ case kFmt3rms:
+ case kFmt35ms:
+ notHandled = handleFmt35ms_3rms(cUnit, mir,blockList[i],
+ labelList);
+ break;
+ case kFmt3inline:
+ notHandled = handleFmt3inline(cUnit, mir);
+ break;
+ case kFmt51l:
+ notHandled = handleFmt51l(cUnit, mir);
+ break;
+ default:
+ notHandled = true;
+ break;
+ }
+ }
+ if (notHandled) {
+ LOGE("%#06x: Opcode 0x%x (%s) / Fmt %d not handled\n",
+ mir->offset,
+ dalvikOpCode, getOpcodeName(dalvikOpCode),
+ dalvikFormat);
+ dvmAbort();
+ break;
+ } else {
+ gDvmJit.opHistogram[dalvikOpCode]++;
+ }
+ }
+ }
+
+ /* Handle the codegen in predefined order */
+ for (i = 0; i < CHAINING_CELL_LAST; i++) {
+ size_t j;
+ int *blockIdList = (int *) chainingListByType[i].elemList;
+
+ cUnit->numChainingCells[i] = chainingListByType[i].numUsed;
+
+ /* No chaining cells of this type */
+ if (cUnit->numChainingCells[i] == 0)
+ continue;
+
+ /* Record the first LIR for a new type of chaining cell */
+ cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]];
+
+ for (j = 0; j < chainingListByType[i].numUsed; j++) {
+ int blockId = blockIdList[j];
+
+ /* Align this chaining cell first */
+ newLIR0(cUnit, ARMV5TE_PSEUDO_ALIGN4);
+
+ /* Insert the pseudo chaining instruction */
+ dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+
+ switch (blockList[blockId]->blockType) {
+ case CHAINING_CELL_GENERIC:
+ handleGenericChainingCell(cUnit,
+ blockList[blockId]->startOffset);
+ break;
+ case CHAINING_CELL_INVOKE:
+ handleInvokeChainingCell(cUnit,
+ blockList[blockId]->containingMethod);
+ break;
+ case CHAINING_CELL_POST_INVOKE:
+ handlePostInvokeChainingCell(cUnit,
+ blockList[blockId]->startOffset);
+ break;
+ default:
+ dvmAbort();
+ break;
+ }
+ }
+ }
+}
+
+/* Accept the work and start compiling */
+void *dvmCompilerDoWork(CompilerWorkOrder *work)
+{
+ void *res;
+
+ if (gDvmJit.codeCacheFull) {
+ return NULL;
+ }
+
+ switch (work->kind) {
+ case kWorkOrderMethod:
+ res = dvmCompileMethod(work->info);
+ break;
+ case kWorkOrderTrace:
+ res = dvmCompileTrace(work->info);
+ break;
+ default:
+ res = NULL;
+ dvmAbort();
+ }
+ return res;
+}
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchInit(void)
+{
+ /* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ int i = 0;
+ extern void dvmCompilerTemplateStart(void);
+
+ /*
+ * Then, populate the templateEntryOffsets array with the offsets from the
+ * the dvmCompilerTemplateStart symbol for each template.
+ */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+ (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ /* Codegen-specific assumptions */
+ assert(offsetof(ClassObject, vtable) < 128 &&
+ (offsetof(ClassObject, vtable) & 0x3) == 0);
+ assert(offsetof(ArrayObject, length) < 128 &&
+ (offsetof(ArrayObject, length) & 0x3) == 0);
+ assert(offsetof(ArrayObject, contents) < 256);
+
+ /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+ assert(sizeof(StackSaveArea) < 236);
+
+ /*
+ * EA is calculated by doing "Rn + imm5 << 2", and there are 5 entry points
+ * that codegen may access, make sure that the offset from the top of the
+ * struct is less than 108.
+ */
+ assert(offsetof(InterpState, jitToInterpEntries) < 108);
+ return true;
+}
+
+/* Architectural-specific debugging helpers go here */
+void dvmCompilerArchDump(void)
+{
+ /* Print compiled opcode in this VM instance */
+ int i, start, streak;
+ char buf[1024];
+
+ streak = i = 0;
+ buf[0] = 0;
+ while (opcodeCoverage[i] == 0 && i < 256) {
+ i++;
+ }
+ if (i == 256) {
+ return;
+ }
+ for (start = i++, streak = 1; i < 256; i++) {
+ if (opcodeCoverage[i]) {
+ streak++;
+ } else {
+ if (streak == 1) {
+ sprintf(buf+strlen(buf), "%x,", start);
+ } else {
+ sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
+ }
+ streak = 0;
+ while (opcodeCoverage[i] == 0 && i < 256) {
+ i++;
+ }
+ if (i < 256) {
+ streak = 1;
+ start = i;
+ }
+ }
+ }
+ if (streak) {
+ if (streak == 1) {
+ sprintf(buf+strlen(buf), "%x", start);
+ } else {
+ sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
+ }
+ }
+ if (strlen(buf)) {
+ LOGD("dalvik.vm.jitop = %s", buf);
+ }
+}