* fmt: for pretty-prining
*/
#define ENCODING_MAP(opcode, skeleton, ds, de, s1s, s1e, s2s, s2e, operands, \
- name, fmt) \
+ name, fmt, size) \
{skeleton, {{ds, de}, {s1s, s1e}, {s2s, s2e}}, opcode, operands, name, \
- fmt}
+ fmt, size}
/* Instruction dump string format keys: !pf, where "!" is the start
* of the key, "p" is which numeric operand to use and "f" is the
Armv5teEncodingMap EncodingMap[ARMV5TE_LAST] = {
ENCODING_MAP(ARMV5TE_16BIT_DATA, 0x0000, 15, 0, -1, -1, -1, -1,
IS_UNARY_OP,
- "data", "0x!0h(!0d)"),
+ "data", "0x!0h(!0d)", 1),
ENCODING_MAP(ARMV5TE_ADC, 0x4140, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "adc", "r!0d, r!1d"),
+ "adc", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_ADD_RRI3, 0x1c00, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "add", "r!0d, r!1d, #!2d"),
+ "add", "r!0d, r!1d, #!2d", 1),
ENCODING_MAP(ARMV5TE_ADD_RI8, 0x3000, 10, 8, 7, 0, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "add", "r!0d, r!0d, #!1d"),
+ "add", "r!0d, r!0d, #!1d", 1),
ENCODING_MAP(ARMV5TE_ADD_RRR, 0x1800, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "add", "r!0d, r!1d, r!2d"),
+ "add", "r!0d, r!1d, r!2d", 1),
ENCODING_MAP(ARMV5TE_ADD_RR_LH, 0x4440, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
"add",
- "r!0d, r!1d"),
+ "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_ADD_RR_HL, 0x4480, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "add", "r!0d, r!1d"),
+ "add", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_ADD_RR_HH, 0x44c0, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "add", "r!0d, r!1d"),
+ "add", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_ADD_PC_REL, 0xa000, 10, 8, 7, 0, -1, -1,
IS_TERTIARY_OP | CLOBBER_DEST,
- "add", "r!0d, pc, #!1E"),
+ "add", "r!0d, pc, #!1E", 1),
ENCODING_MAP(ARMV5TE_ADD_SP_REL, 0xa800, 10, 8, 7, 0, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "add", "r!0d, sp, #!1E"),
+ "add", "r!0d, sp, #!1E", 1),
ENCODING_MAP(ARMV5TE_ADD_SPI7, 0xb000, 6, 0, -1, -1, -1, -1,
IS_UNARY_OP | CLOBBER_DEST,
- "add", "sp, #!0d*4"),
+ "add", "sp, #!0d*4", 1),
ENCODING_MAP(ARMV5TE_AND_RR, 0x4000, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "and", "r!0d, r!1d"),
+ "and", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_ASR, 0x1000, 2, 0, 5, 3, 10, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "asr", "r!0d, r!1d, #!2d"),
+ "asr", "r!0d, r!1d, #!2d", 1),
ENCODING_MAP(ARMV5TE_ASRV, 0x4100, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "asr", "r!0d, r!1d"),
+ "asr", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_B_COND, 0xd000, 7, 0, 11, 8, -1, -1,
IS_BINARY_OP | IS_BRANCH,
- "!1c", "!0t"),
+ "!1c", "!0t", 1),
ENCODING_MAP(ARMV5TE_B_UNCOND, 0xe000, 10, 0, -1, -1, -1, -1,
NO_OPERAND | IS_BRANCH,
- "b", "!0t"),
+ "b", "!0t", 1),
ENCODING_MAP(ARMV5TE_BIC, 0x4380, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "bic", "r!0d, r!1d"),
+ "bic", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_BKPT, 0xbe00, 7, 0, -1, -1, -1, -1,
IS_UNARY_OP | IS_BRANCH,
- "bkpt", "!0d"),
+ "bkpt", "!0d", 1),
ENCODING_MAP(ARMV5TE_BLX_1, 0xf000, 10, 0, -1, -1, -1, -1,
IS_BINARY_OP | IS_BRANCH,
- "blx_1", "!0u"),
+ "blx_1", "!0u", 1),
ENCODING_MAP(ARMV5TE_BLX_2, 0xe800, 10, 0, -1, -1, -1, -1,
IS_BINARY_OP | IS_BRANCH,
- "blx_2", "!0v"),
+ "blx_2", "!0v", 1),
ENCODING_MAP(ARMV5TE_BL_1, 0xf000, 10, 0, -1, -1, -1, -1,
IS_UNARY_OP | IS_BRANCH,
- "bl_1", "!0u"),
+ "bl_1", "!0u", 1),
ENCODING_MAP(ARMV5TE_BL_2, 0xf800, 10, 0, -1, -1, -1, -1,
IS_UNARY_OP | IS_BRANCH,
- "bl_2", "!0v"),
+ "bl_2", "!0v", 1),
ENCODING_MAP(ARMV5TE_BLX_R, 0x4780, 6, 3, -1, -1, -1, -1,
IS_UNARY_OP | IS_BRANCH,
- "blx", "r!0d"),
+ "blx", "r!0d", 1),
ENCODING_MAP(ARMV5TE_BX, 0x4700, 6, 3, -1, -1, -1, -1,
IS_UNARY_OP | IS_BRANCH,
- "bx", "r!0d"),
+ "bx", "r!0d", 1),
ENCODING_MAP(ARMV5TE_CMN, 0x42c0, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP,
- "cmn", "r!0d, r!1d"),
+ "cmn", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_CMP_RI8, 0x2800, 10, 8, 7, 0, -1, -1,
IS_BINARY_OP,
- "cmp", "r!0d, #!1d"),
+ "cmp", "r!0d, #!1d", 1),
ENCODING_MAP(ARMV5TE_CMP_RR, 0x4280, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP,
- "cmp", "r!0d, r!1d"),
+ "cmp", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_CMP_LH, 0x4540, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP,
- "cmp", "r!0d, r!1D"),
+ "cmp", "r!0d, r!1D", 1),
ENCODING_MAP(ARMV5TE_CMP_HL, 0x4580, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP,
- "cmp", "r!0D, r!1d"),
+ "cmp", "r!0D, r!1d", 1),
ENCODING_MAP(ARMV5TE_CMP_HH, 0x45c0, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP,
- "cmp", "r!0D, r!1D"),
+ "cmp", "r!0D, r!1D", 1),
ENCODING_MAP(ARMV5TE_EOR, 0x4040, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "eor", "r!0d, r!1d"),
+ "eor", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_LDMIA, 0xc800, 10, 8, 7, 0, -1, -1,
IS_BINARY_OP | CLOBBER_DEST | CLOBBER_SRC1,
- "ldmia", "r!0d!!, <!1R>"),
+ "ldmia", "r!0d!!, <!1R>", 1),
ENCODING_MAP(ARMV5TE_LDR_RRI5, 0x6800, 2, 0, 5, 3, 10, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "ldr", "r!0d, [r!1d, #!2E]"),
+ "ldr", "r!0d, [r!1d, #!2E]", 1),
ENCODING_MAP(ARMV5TE_LDR_RRR, 0x5800, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "ldr", "r!0d, [r!1d, r!2d]"),
+ "ldr", "r!0d, [r!1d, r!2d]", 1),
ENCODING_MAP(ARMV5TE_LDR_PC_REL, 0x4800, 10, 8, 7, 0, -1, -1,
IS_TERTIARY_OP | CLOBBER_DEST,
- "ldr", "r!0d, [pc, #!1E]"),
+ "ldr", "r!0d, [pc, #!1E]", 1),
ENCODING_MAP(ARMV5TE_LDR_SP_REL, 0x9800, 10, 8, 7, 0, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "ldr", "r!0d, [sp, #!1E]"),
+ "ldr", "r!0d, [sp, #!1E]", 1),
ENCODING_MAP(ARMV5TE_LDRB_RRI5, 0x7800, 2, 0, 5, 3, 10, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "ldrb", "r!0d, [r!1d, #2d]"),
+ "ldrb", "r!0d, [r!1d, #2d]", 1),
ENCODING_MAP(ARMV5TE_LDRB_RRR, 0x5c00, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "ldrb", "r!0d, [r!1d, r!2d]"),
+ "ldrb", "r!0d, [r!1d, r!2d]", 1),
ENCODING_MAP(ARMV5TE_LDRH_RRI5, 0x8800, 2, 0, 5, 3, 10, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "ldrh", "r!0d, [r!1d, #!2F]"),
+ "ldrh", "r!0d, [r!1d, #!2F]", 1),
ENCODING_MAP(ARMV5TE_LDRH_RRR, 0x5a00, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "ldrh", "r!0d, [r!1d, r!2d]"),
+ "ldrh", "r!0d, [r!1d, r!2d]", 1),
ENCODING_MAP(ARMV5TE_LDRSB_RRR, 0x5600, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "ldrsb", "r!0d, [r!1d, r!2d]"),
+ "ldrsb", "r!0d, [r!1d, r!2d]", 1),
ENCODING_MAP(ARMV5TE_LDRSH_RRR, 0x5e00, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "ldrsh", "r!0d, [r!1d, r!2d]"),
+ "ldrsh", "r!0d, [r!1d, r!2d]", 1),
ENCODING_MAP(ARMV5TE_LSL, 0x0000, 2, 0, 5, 3, 10, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "lsl", "r!0d, r!1d, #!2d"),
+ "lsl", "r!0d, r!1d, #!2d", 1),
ENCODING_MAP(ARMV5TE_LSLV, 0x4080, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "lsl", "r!0d, r!1d"),
+ "lsl", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_LSR, 0x0800, 2, 0, 5, 3, 10, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "lsr", "r!0d, r!1d, #!2d"),
+ "lsr", "r!0d, r!1d, #!2d", 1),
ENCODING_MAP(ARMV5TE_LSRV, 0x40c0, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "lsr", "r!0d, r!1d"),
+ "lsr", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_MOV_IMM, 0x2000, 10, 8, 7, 0, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "mov", "r!0d, #!1d"),
+ "mov", "r!0d, #!1d", 1),
ENCODING_MAP(ARMV5TE_MOV_RR, 0x1c00, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "mov", "r!0d, r!1d"),
+ "mov", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_MOV_RR_H2H, 0x46c0, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "mov", "r!0D, r!1D"),
+ "mov", "r!0D, r!1D", 1),
ENCODING_MAP(ARMV5TE_MOV_RR_H2L, 0x4640, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "mov", "r!0d, r!1D"),
+ "mov", "r!0d, r!1D", 1),
ENCODING_MAP(ARMV5TE_MOV_RR_L2H, 0x4680, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "mov", "r!0D, r!1d"),
+ "mov", "r!0D, r!1d", 1),
ENCODING_MAP(ARMV5TE_MUL, 0x4340, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "mul", "r!0d, r!1d"),
+ "mul", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_MVN, 0x43c0, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "mvn", "r!0d, r!1d"),
+ "mvn", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_NEG, 0x4240, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "neg", "r!0d, r!1d"),
+ "neg", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_ORR, 0x4300, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "orr", "r!0d, r!1d"),
+ "orr", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_POP, 0xbc00, 8, 0, -1, -1, -1, -1,
IS_UNARY_OP,
- "pop", "<!0R>"),
+ "pop", "<!0R>", 1),
ENCODING_MAP(ARMV5TE_PUSH, 0xb400, 8, 0, -1, -1, -1, -1,
IS_UNARY_OP,
- "push", "<!0R>"),
+ "push", "<!0R>", 1),
ENCODING_MAP(ARMV5TE_ROR, 0x41c0, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "ror", "r!0d, r!1d"),
+ "ror", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_SBC, 0x4180, 2, 0, 5, 3, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "sbc", "r!0d, r!1d"),
+ "sbc", "r!0d, r!1d", 1),
ENCODING_MAP(ARMV5TE_STMIA, 0xc000, 10, 8, 7, 0, -1, -1,
IS_BINARY_OP | CLOBBER_SRC1,
- "stmia", "r!0d!!, <!1R>"),
+ "stmia", "r!0d!!, <!1R>", 1),
ENCODING_MAP(ARMV5TE_STR_RRI5, 0x6000, 2, 0, 5, 3, 10, 6,
IS_TERTIARY_OP,
- "str", "r!0d, [r!1d, #!2E]"),
+ "str", "r!0d, [r!1d, #!2E]", 1),
ENCODING_MAP(ARMV5TE_STR_RRR, 0x5000, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP,
- "str", "r!0d, [r!1d, r!2d]"),
+ "str", "r!0d, [r!1d, r!2d]", 1),
ENCODING_MAP(ARMV5TE_STR_SP_REL, 0x9000, 10, 8, 7, 0, -1, -1,
IS_BINARY_OP,
- "str", "r!0d, [sp, #!1E]"),
+ "str", "r!0d, [sp, #!1E]", 1),
ENCODING_MAP(ARMV5TE_STRB_RRI5, 0x7000, 2, 0, 5, 3, 10, 6,
IS_TERTIARY_OP,
- "strb", "r!0d, [r!1d, #!2d]"),
+ "strb", "r!0d, [r!1d, #!2d]", 1),
ENCODING_MAP(ARMV5TE_STRB_RRR, 0x5400, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP,
- "strb", "r!0d, [r!1d, r!2d]"),
+ "strb", "r!0d, [r!1d, r!2d]", 1),
ENCODING_MAP(ARMV5TE_STRH_RRI5, 0x8000, 2, 0, 5, 3, 10, 6,
IS_TERTIARY_OP,
- "strh", "r!0d, [r!1d, #!2F]"),
+ "strh", "r!0d, [r!1d, #!2F]", 1),
ENCODING_MAP(ARMV5TE_STRH_RRR, 0x5200, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP,
- "strh", "r!0d, [r!1d, r!2d]"),
+ "strh", "r!0d, [r!1d, r!2d]", 1),
ENCODING_MAP(ARMV5TE_SUB_RRI3, 0x1e00, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "sub", "r!0d, r!1d, #!2d]"),
+ "sub", "r!0d, r!1d, #!2d]", 1),
ENCODING_MAP(ARMV5TE_SUB_RI8, 0x3800, 10, 8, 7, 0, -1, -1,
IS_BINARY_OP | CLOBBER_DEST,
- "sub", "r!0d, #!1d"),
+ "sub", "r!0d, #!1d", 1),
ENCODING_MAP(ARMV5TE_SUB_RRR, 0x1a00, 2, 0, 5, 3, 8, 6,
IS_TERTIARY_OP | CLOBBER_DEST,
- "sub", "r!0d, r!1d, r!2d"),
+ "sub", "r!0d, r!1d, r!2d", 1),
ENCODING_MAP(ARMV5TE_SUB_SPI7, 0xb080, 6, 0, -1, -1, -1, -1,
IS_UNARY_OP | CLOBBER_DEST,
- "sub", "sp, #!0d"),
+ "sub", "sp, #!0d", 1),
ENCODING_MAP(ARMV5TE_SWI, 0xdf00, 7, 0, -1, -1, -1, -1,
IS_UNARY_OP | IS_BRANCH,
- "swi", "!0d"),
+ "swi", "!0d", 1),
ENCODING_MAP(ARMV5TE_TST, 0x4200, 2, 0, 5, 3, -1, -1,
IS_UNARY_OP,
- "tst", "r!0d, r!1d"),
+ "tst", "r!0d, r!1d", 1),
};
#define PADDING_MOV_R0_R0 0x1C00
* before sending them off to the assembler. If out-of-range branch distance is
* seen rearrange the instructions a bit to correct it.
*/
-void dvmCompilerAssembleLIR(CompilationUnit *cUnit)
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info)
{
LIR *lir;
Armv5teLIR *armLIR;
ChainCellCounts chainCellCounts;
int descSize = jitTraceDescriptionSize(cUnit->traceDesc);
+ info->codeAddress = NULL;
+ info->instructionSet = cUnit->instructionSet;
+
/* Beginning offset needs to allow space for chain cell offset */
for (armLIR = (Armv5teLIR *) cUnit->firstLIRInsn;
armLIR;
/* Flush dcache and invalidate the icache to maintain coherence */
cacheflush((long)cUnit->baseAddr,
(long)((char *) cUnit->baseAddr + offset), 0);
+
+ /* Record code entry point and instruction set */
+ info->codeAddress = (char*)cUnit->baseAddr + cUnit->headerSize;
+ info->instructionSet = cUnit->instructionSet;
+ /* If applicable, mark low bit to denote thumb */
+ if (info->instructionSet != DALVIK_JIT_ARM)
+ info->codeAddress = (char*)info->codeAddress + 1;
}
static u4 assembleBXPair(int branchOffset)
dvmUnlockMutex(&gDvmJit.tableLock);
}
}
+
+typedef struct jitProfileAddrToLine {
+ u4 lineNum;
+ u4 bytecodeOffset;
+} jitProfileAddrToLine;
+
+
+/* Callback function to track the bytecode offset/line number relationiship */
+static int addrToLineCb (void *cnxt, u4 bytecodeOffset, u4 lineNum)
+{
+ jitProfileAddrToLine *addrToLine = (jitProfileAddrToLine *) cnxt;
+
+ /* Best match so far for this offset */
+ if (addrToLine->bytecodeOffset >= bytecodeOffset) {
+ addrToLine->lineNum = lineNum;
+ }
+ return 0;
+}
+
+char *getTraceBase(const JitEntry *p)
+{
+ return (char*)p->codeAddress -
+ (6 + (p->u.info.instructionSet == DALVIK_JIT_ARM ? 0 : 1));
+}
+
+/* Dumps profile info for a single trace */
+static int dumpTraceProfile(JitEntry *p)
+{
+ ChainCellCounts* pCellCounts;
+ char* traceBase;
+ u4* pExecutionCount;
+ u2* pCellOffset;
+ JitTraceDescription *desc;
+ const Method* method;
+
+ traceBase = getTraceBase(p);
+
+ if (p->codeAddress == NULL) {
+ LOGD("TRACEPROFILE 0x%08x 0 NULL 0 0", (int)traceBase);
+ return 0;
+ }
+
+ pExecutionCount = (u4*) (traceBase);
+ pCellOffset = (u2*) (traceBase + 4);
+ pCellCounts = (ChainCellCounts*) ((char *)pCellOffset + *pCellOffset);
+ desc = (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
+ method = desc->method;
+ char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+ jitProfileAddrToLine addrToLine = {0, desc->trace[0].frag.startOffset};
+
+ /*
+ * We may end up decoding the debug information for the same method
+ * multiple times, but the tradeoff is we don't need to allocate extra
+ * space to store the addr/line mapping. Since this is a debugging feature
+ * and done infrequently so the slower but simpler mechanism should work
+ * just fine.
+ */
+ dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+ dvmGetMethodCode(method),
+ method->clazz->descriptor,
+ method->prototype.protoIdx,
+ method->accessFlags,
+ addrToLineCb, NULL, &addrToLine);
+
+ LOGD("TRACEPROFILE 0x%08x % 10d [%#x(+%d), %d] %s%s;%s",
+ (int)traceBase,
+ *pExecutionCount,
+ desc->trace[0].frag.startOffset,
+ desc->trace[0].frag.numInsts,
+ addrToLine.lineNum,
+ method->clazz->descriptor, method->name, methodDesc);
+ free(methodDesc);
+
+ return *pExecutionCount;
+}
+
+/* Handy function to retrieve the profile count */
+static inline int getProfileCount(const JitEntry *entry)
+{
+ if (entry->dPC == 0 || entry->codeAddress == 0)
+ return 0;
+ u4 *pExecutionCount = (u4 *) getTraceBase(entry);
+
+ return *pExecutionCount;
+}
+
+
+/* qsort callback function */
+static int sortTraceProfileCount(const void *entry1, const void *entry2)
+{
+ const JitEntry *jitEntry1 = entry1;
+ const JitEntry *jitEntry2 = entry2;
+
+ int count1 = getProfileCount(jitEntry1);
+ int count2 = getProfileCount(jitEntry2);
+ return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
+}
+
+/* Sort the trace profile counts and dump them */
+void dvmCompilerSortAndPrintTraceProfiles()
+{
+ JitEntry *sortedEntries;
+ int numTraces = 0;
+ unsigned long counts = 0;
+ unsigned int i;
+
+ /* Make sure that the table is not changing */
+ dvmLockMutex(&gDvmJit.tableLock);
+
+ /* Sort the entries by descending order */
+ sortedEntries = malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
+ if (sortedEntries == NULL)
+ goto done;
+ memcpy(sortedEntries, gDvmJit.pJitEntryTable,
+ sizeof(JitEntry) * gDvmJit.jitTableSize);
+ qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry),
+ sortTraceProfileCount);
+
+ /* Dump the sorted entries */
+ for (i=0; i < gDvmJit.jitTableSize; i++) {
+ if (sortedEntries[i].dPC != 0) {
+ counts += dumpTraceProfile(&sortedEntries[i]);
+ numTraces++;
+ }
+ }
+ if (numTraces == 0)
+ numTraces = 1;
+ LOGD("JIT: Average execution count -> %d",(int)(counts / numTraces));
+
+ free(sortedEntries);
+done:
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ return;
+}
dvmInitMutex(&gDvmJit.tableLock);
if (res && gDvm.executionMode == kExecutionModeJit) {
- struct JitEntry *pJitTable = NULL;
+ JitEntry *pJitTable = NULL;
unsigned char *pJitProfTable = NULL;
assert(gDvm.jitTableSize &&
!(gDvm.jitTableSize & (gDvmJit.jitTableSize - 1))); // Power of 2?
dvmLockMutex(&gDvmJit.tableLock);
- pJitTable = (struct JitEntry*)
+ pJitTable = (JitEntry*)
calloc(gDvmJit.jitTableSize, sizeof(*pJitTable));
if (!pJitTable) {
LOGE("jit table allocation failed\n");
}
memset(pJitProfTable,0,JIT_PROF_SIZE);
for (i=0; i < gDvmJit.jitTableSize; i++) {
- pJitTable[i].chain = gDvmJit.jitTableSize;
+ pJitTable[i].u.info.chain = gDvmJit.jitTableSize;
}
/* Is chain field wide enough for termination pattern? */
- assert(pJitTable[0].chain == gDvm.maxJitTableEntries);
+ assert(pJitTable[0].u.info.chain == gDvm.maxJitTableEntries);
done:
gDvmJit.pJitEntryTable = pJitTable;
}
#endif
-typedef struct jitProfileAddrToLine {
- u4 lineNum;
- u4 bytecodeOffset;
-} jitProfileAddrToLine;
-
-
-/* Callback function to track the bytecode offset/line number relationiship */
-static int addrToLineCb (void *cnxt, u4 bytecodeOffset, u4 lineNum)
-{
- jitProfileAddrToLine *addrToLine = (jitProfileAddrToLine *) cnxt;
-
- /* Best match so far for this offset */
- if (addrToLine->bytecodeOffset >= bytecodeOffset) {
- addrToLine->lineNum = lineNum;
- }
- return 0;
-}
-
-/* Dumps profile info for a single trace */
-int dvmCompilerDumpTraceProfile(struct JitEntry *p)
-{
- ChainCellCounts* pCellCounts;
- char* traceBase;
- u4* pExecutionCount;
- u2* pCellOffset;
- JitTraceDescription *desc;
- const Method* method;
-
- /*
- * The codeAddress field has the low bit set to mark thumb
- * mode. We need to strip that off before reconstructing the
- * trace data. See the diagram in Assemble.c for more info
- * on the trace layout in memory.
- */
- traceBase = (char*)p->codeAddress - 7;
-
- if (p->codeAddress == NULL) {
- LOGD("TRACEPROFILE 0x%08x 0 NULL 0 0", (int)traceBase);
- return 0;
- }
-
- pExecutionCount = (u4*) (traceBase);
- pCellOffset = (u2*) (traceBase + 4);
- pCellCounts = (ChainCellCounts*) ((char *)pCellOffset + *pCellOffset);
- desc = (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
- method = desc->method;
- char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
- jitProfileAddrToLine addrToLine = {0, desc->trace[0].frag.startOffset};
-
- /*
- * We may end up decoding the debug information for the same method
- * multiple times, but the tradeoff is we don't need to allocate extra
- * space to store the addr/line mapping. Since this is a debugging feature
- * and done infrequently so the slower but simpler mechanism should work
- * just fine.
- */
- dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
- dvmGetMethodCode(method),
- method->clazz->descriptor,
- method->prototype.protoIdx,
- method->accessFlags,
- addrToLineCb, NULL, &addrToLine);
-
- LOGD("TRACEPROFILE 0x%08x % 10d [%#x(+%d), %d] %s%s;%s",
- (int)traceBase,
- *pExecutionCount,
- desc->trace[0].frag.startOffset,
- desc->trace[0].frag.numInsts,
- addrToLine.lineNum,
- method->clazz->descriptor, method->name, methodDesc);
- free(methodDesc);
-
- return *pExecutionCount;
-}
-
-/* Handy function to retrieve the profile count */
-static inline int getProfileCount(const JitEntry *entry)
-{
- if (entry->dPC == 0 || entry->codeAddress == 0)
- return 0;
- /*
- * The codeAddress field has the low bit set to mark thumb
- * mode. We need to strip that off before reconstructing the
- * trace data. See the diagram in Assemble.c for more info
- * on the trace layout in memory.
- */
- u4 *pExecutionCount = (u4 *) ((char*)entry->codeAddress - 7);
-
- return *pExecutionCount;
-}
-
-/* qsort callback function */
-static int sortTraceProfileCount(const void *entry1, const void *entry2)
-{
- const JitEntry *jitEntry1 = entry1;
- const JitEntry *jitEntry2 = entry2;
-
- int count1 = getProfileCount(jitEntry1);
- int count2 = getProfileCount(jitEntry2);
- return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
-}
-
-/* Sort the trace profile counts and dump them */
-static void sortAndPrintTraceProfiles()
-{
- JitEntry *sortedEntries;
- int numTraces = 0;
- unsigned long counts = 0;
- unsigned int i;
-
- /* Make sure that the table is not changing */
- dvmLockMutex(&gDvmJit.tableLock);
-
- /* Sort the entries by descending order */
- sortedEntries = malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
- if (sortedEntries == NULL)
- goto done;
- memcpy(sortedEntries, gDvmJit.pJitEntryTable,
- sizeof(JitEntry) * gDvmJit.jitTableSize);
- qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry),
- sortTraceProfileCount);
-
- /* Dump the sorted entries */
- for (i=0; i < gDvmJit.jitTableSize; i++) {
- if (sortedEntries[i].dPC != 0) {
- counts += dvmCompilerDumpTraceProfile(&sortedEntries[i]);
- numTraces++;
- }
- }
- if (numTraces == 0)
- numTraces = 1;
- LOGD("JIT: Average execution count -> %d",(int)(counts / numTraces));
-
- free(sortedEntries);
-done:
- dvmUnlockMutex(&gDvmJit.tableLock);
- return;
-}
-
/* Dumps debugging & tuning stats to the log */
void dvmJitStats()
{
hit++;
else
not_hit++;
- if (gDvmJit.pJitEntryTable[i].chain != gDvmJit.jitTableSize)
+ if (gDvmJit.pJitEntryTable[i].u.info.chain != gDvmJit.jitTableSize)
chains++;
}
LOGD(
gDvmJit.invokeNative, gDvmJit.returnOp);
#endif
if (gDvmJit.profile) {
- sortAndPrintTraceProfiles();
+ dvmCompilerSortAndPrintTraceProfiles();
}
}
}
+
/*
* Final JIT shutdown. Only do this once, and do not attempt to restart
* the JIT later.
return switchInterp;
}
-static inline struct JitEntry *findJitEntry(const u2* pc)
+static inline JitEntry *findJitEntry(const u2* pc)
{
int idx = dvmJitHash(pc);
return &gDvmJit.pJitEntryTable[idx];
else {
int chainEndMarker = gDvmJit.jitTableSize;
- while (gDvmJit.pJitEntryTable[idx].chain != chainEndMarker) {
- idx = gDvmJit.pJitEntryTable[idx].chain;
+ while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+ idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
if (gDvmJit.pJitEntryTable[idx].dPC == pc)
return &gDvmJit.pJitEntryTable[idx];
}
return NULL;
}
-struct JitEntry *dvmFindJitEntry(const u2* pc)
+JitEntry *dvmFindJitEntry(const u2* pc)
{
return findJitEntry(pc);
}
/*
- * Allocate an entry in a JitTable. Assumes caller holds lock, if
- * applicable. Normally used for table resizing. Will complain (die)
- * if entry already exists in the table or if table is full.
- */
-static struct JitEntry *allocateJitEntry(const u2* pc, struct JitEntry *table,
- u4 size)
-{
- struct JitEntry *p;
- unsigned int idx;
- unsigned int prev;
- idx = dvmJitHashMask(pc, size-1);
- while ((table[idx].chain != size) && (table[idx].dPC != pc)) {
- idx = table[idx].chain;
- }
- assert(table[idx].dPC != pc); /* Already there */
- if (table[idx].dPC == NULL) {
- /* use this slot */
- return &table[idx];
- }
- /* Find a free entry and chain it in */
- prev = idx;
- while (true) {
- idx++;
- if (idx == size)
- idx = 0; /* Wraparound */
- if ((table[idx].dPC == NULL) || (idx == prev))
- break;
- }
- assert(idx != prev);
- table[prev].chain = idx;
- assert(table[idx].dPC == NULL);
- return &table[idx];
-}
-
-/*
* If a translated code address exists for the davik byte code
* pointer return it. This routine needs to be fast.
*/
return gDvmJit.pJitEntryTable[idx].codeAddress;
} else {
int chainEndMarker = gDvmJit.jitTableSize;
- while (gDvmJit.pJitEntryTable[idx].chain != chainEndMarker) {
- idx = gDvmJit.pJitEntryTable[idx].chain;
+ while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+ idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
if (gDvmJit.pJitEntryTable[idx].dPC == dPC) {
#if defined(EXIT_STATS)
gDvmJit.addrLookupsFound++;
}
/*
+ * Find an entry in the JitTable, creating if necessary.
+ * Returns null if table is full.
+ */
+JitEntry *dvmJitLookupAndAdd(const u2* dPC)
+{
+ u4 chainEndMarker = gDvmJit.jitTableSize;
+ u4 idx = dvmJitHash(dPC);
+
+ /* Walk the bucket chain to find an exact match for our PC */
+ while ((gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) &&
+ (gDvmJit.pJitEntryTable[idx].dPC != dPC)) {
+ idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+ }
+
+ if (gDvmJit.pJitEntryTable[idx].dPC != dPC) {
+ /*
+ * No match. Aquire jitTableLock and find the last
+ * slot in the chain. Possibly continue the chain walk in case
+ * some other thread allocated the slot we were looking
+ * at previuosly (perhaps even the dPC we're trying to enter).
+ */
+ dvmLockMutex(&gDvmJit.tableLock);
+ /*
+ * At this point, if .dPC is NULL, then the slot we're
+ * looking at is the target slot from the primary hash
+ * (the simple, and common case). Otherwise we're going
+ * to have to find a free slot and chain it.
+ */
+ MEM_BARRIER(); /* Make sure we reload [].dPC after lock */
+ if (gDvmJit.pJitEntryTable[idx].dPC != NULL) {
+ u4 prev;
+ while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+ if (gDvmJit.pJitEntryTable[idx].dPC == dPC) {
+ /* Another thread got there first for this dPC */
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ return &gDvmJit.pJitEntryTable[idx];
+ }
+ idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+ }
+ /* Here, idx should be pointing to the last cell of an
+ * active chain whose last member contains a valid dPC */
+ assert(gDvmJit.pJitEntryTable[idx].dPC != NULL);
+ /* Linear walk to find a free cell and add it to the end */
+ prev = idx;
+ while (true) {
+ idx++;
+ if (idx == chainEndMarker)
+ idx = 0; /* Wraparound */
+ if ((gDvmJit.pJitEntryTable[idx].dPC == NULL) ||
+ (idx == prev))
+ break;
+ }
+ if (idx != prev) {
+ JitEntryInfoUnion oldValue;
+ JitEntryInfoUnion newValue;
+ /*
+ * Although we hold the lock so that noone else will
+ * be trying to update a chain field, the other fields
+ * packed into the word may be in use by other threads.
+ */
+ do {
+ oldValue = gDvmJit.pJitEntryTable[prev].u;
+ newValue = oldValue;
+ newValue.info.chain = idx;
+ } while (!ATOMIC_CMP_SWAP(
+ &gDvmJit.pJitEntryTable[prev].u.infoWord,
+ oldValue.infoWord, newValue.infoWord));
+ }
+ }
+ if (gDvmJit.pJitEntryTable[idx].dPC == NULL) {
+ /* Allocate the slot */
+ gDvmJit.pJitEntryTable[idx].dPC = dPC;
+ gDvmJit.jitTableEntriesUsed++;
+ } else {
+ /* Table is full */
+ idx = chainEndMarker;
+ }
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ }
+ return (idx == chainEndMarker) ? NULL : &gDvmJit.pJitEntryTable[idx];
+}
+/*
* Register the translated code pointer into the JitTable.
* NOTE: Once a codeAddress field transitions from NULL to
* JIT'd code, it must not be altered without first halting all
- * threads.
+ * threads. This routine should only be called by the compiler
+ * thread.
*/
-void dvmJitSetCodeAddr(const u2* dPC, void *nPC) {
- struct JitEntry *jitEntry = findJitEntry(dPC);
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set) {
+ JitEntryInfoUnion oldValue;
+ JitEntryInfoUnion newValue;
+ JitEntry *jitEntry = dvmJitLookupAndAdd(dPC);
assert(jitEntry);
- /* Thumb code has odd PC */
- jitEntry->codeAddress = (void *) ((intptr_t) nPC |1);
+ /* Note: order of update is important */
+ do {
+ oldValue = jitEntry->u;
+ newValue = oldValue;
+ newValue.info.instructionSet = set;
+ } while (!ATOMIC_CMP_SWAP(
+ &jitEntry->u.infoWord,
+ oldValue.infoWord, newValue.infoWord));
+ jitEntry->codeAddress = nPC;
}
/*
* requested
*/
-#define PROFILE_STALENESS_THRESHOLD 100000LL
bool dvmJitCheckTraceRequest(Thread* self, InterpState* interpState)
{
bool res = false; /* Assume success */
interpState->jitState = kJitNormal;
}
} else if (interpState->jitState == kJitTSelectRequest) {
- u4 chainEndMarker = gDvmJit.jitTableSize;
- u4 idx = dvmJitHash(interpState->pc);
-
- /* Walk the bucket chain to find an exact match for our PC */
- while ((gDvmJit.pJitEntryTable[idx].chain != chainEndMarker) &&
- (gDvmJit.pJitEntryTable[idx].dPC != interpState->pc)) {
- idx = gDvmJit.pJitEntryTable[idx].chain;
- }
-
- if (gDvmJit.pJitEntryTable[idx].dPC == interpState->pc) {
+ JitEntry *slot = dvmJitLookupAndAdd(interpState->pc);
+ if (slot == NULL) {
/*
- * Got a match. This means a trace has already
- * been requested for this address. Bail back to
- * mterp, which will check if the translation is ready
- * for execution
+ * Table is full. This should have been
+ * detected by the compiler thread and the table
+ * resized before we run into it here. Assume bad things
+ * are afoot and disable profiling.
*/
interpState->jitState = kJitTSelectAbort;
+ LOGD("JIT: JitTable full, disabling profiling");
+ dvmJitStopTranslationRequests();
+ } else if (slot->u.info.traceRequested) {
+ /* Trace already requested - revert to interpreter */
+ interpState->jitState = kJitTSelectAbort;
} else {
- /*
- * No match. Aquire jitTableLock and find the last
- * slot in the chain. Possibly continue the chain walk in case
- * some other thread allocated the slot we were looking
- * at previuosly
- */
- dvmLockMutex(&gDvmJit.tableLock);
- /*
- * At this point, if .dPC is NULL, then the slot we're
- * looking at is the target slot from the primary hash
- * (the simple, and expected case). Otherwise we're going
- * to have to find a free slot and chain it.
- */
- MEM_BARRIER();
- if (gDvmJit.pJitEntryTable[idx].dPC != NULL) {
- u4 prev;
- while (gDvmJit.pJitEntryTable[idx].chain != chainEndMarker) {
- idx = gDvmJit.pJitEntryTable[idx].chain;
- }
- /* Here, idx should be pointing to the last cell of an
- * active chain whose last member contains a valid dPC */
- assert(gDvmJit.pJitEntryTable[idx].dPC != NULL);
- /* Now, do a linear walk to find a free cell and add it to
- * end of this chain */
- prev = idx;
- while (true) {
- idx++;
- if (idx == chainEndMarker)
- idx = 0; /* Wraparound */
- if ((gDvmJit.pJitEntryTable[idx].dPC == NULL) ||
- (idx == prev))
- break;
- }
- if (idx != prev) {
- /* Got it - chain */
- gDvmJit.pJitEntryTable[prev].chain = idx;
- }
- }
- if (gDvmJit.pJitEntryTable[idx].dPC == NULL) {
- /* Allocate the slot */
- gDvmJit.pJitEntryTable[idx].dPC = interpState->pc;
- gDvmJit.jitTableEntriesUsed++;
- } else {
- /*
- * Table is full. We could resize it, but that would
- * be better handled by the translator thread. It
- * will be aware of how full the table is getting.
- * Disable further profiling and continue.
- */
- interpState->jitState = kJitTSelectAbort;
- LOGD("JIT: JitTable full, disabling profiling");
- dvmJitStopTranslationRequests();
- }
- dvmUnlockMutex(&gDvmJit.tableLock);
+ /* Mark request */
+ JitEntryInfoUnion oldValue;
+ JitEntryInfoUnion newValue;
+ do {
+ oldValue = slot->u;
+ newValue = oldValue;
+ newValue.info.traceRequested = true;
+ } while (!ATOMIC_CMP_SWAP( &slot->u.infoWord,
+ oldValue.infoWord, newValue.infoWord));
}
}
switch (interpState->jitState) {
*/
bool dvmJitResizeJitTable( unsigned int size )
{
- struct JitEntry *pNewTable;
+ JitEntry *pNewTable;
+ JitEntry *pOldTable;
u4 newMask;
+ unsigned int oldSize;
unsigned int i;
assert(gDvm.pJitEntryTable != NULL);
return true;
}
- pNewTable = (struct JitEntry*)calloc(size, sizeof(*pNewTable));
+ pNewTable = (JitEntry*)calloc(size, sizeof(*pNewTable));
if (pNewTable == NULL) {
return true;
}
for (i=0; i< size; i++) {
- pNewTable[i].chain = size; /* Initialize chain termination */
+ pNewTable[i].u.info.chain = size; /* Initialize chain termination */
}
/* Stop all other interpreting/jit'ng threads */
dvmSuspendAllThreads(SUSPEND_FOR_JIT);
- /*
- * At this point, only the compiler thread may be in contention
- * for the jitEntryTable (it is not affected by the thread suspension).
- * Aquire the lock.
- */
+ pOldTable = gDvmJit.pJitEntryTable;
+ oldSize = gDvmJit.jitTableSize;
dvmLockMutex(&gDvmJit.tableLock);
-
- for (i=0; i < gDvmJit.jitTableSize; i++) {
- if (gDvmJit.pJitEntryTable[i].dPC) {
- struct JitEntry *p;
- p = allocateJitEntry(gDvmJit.pJitEntryTable[i].dPC,
- pNewTable, size);
- p->dPC = gDvmJit.pJitEntryTable[i].dPC;
- p->codeAddress = gDvmJit.pJitEntryTable[i].codeAddress;
- }
- }
-
- free(gDvmJit.pJitEntryTable);
gDvmJit.pJitEntryTable = pNewTable;
gDvmJit.jitTableSize = size;
gDvmJit.jitTableMask = size - 1;
-
+ gDvmJit.jitTableEntriesUsed = 0;
dvmUnlockMutex(&gDvmJit.tableLock);
+ for (i=0; i < oldSize; i++) {
+ if (pOldTable[i].dPC) {
+ JitEntry *p;
+ u2 chain;
+ p = dvmJitLookupAndAdd(pOldTable[i].dPC);
+ p->dPC = pOldTable[i].dPC;
+ /*
+ * Compiler thread may have just updated the new entry's
+ * code address field, so don't blindly copy null.
+ */
+ if (pOldTable[i].codeAddress != NULL) {
+ p->codeAddress = pOldTable[i].codeAddress;
+ }
+ /* We need to preserve the new chain field, but copy the rest */
+ dvmLockMutex(&gDvmJit.tableLock);
+ chain = p->u.info.chain;
+ p->u = pOldTable[i].u;
+ p->u.info.chain = chain;
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ }
+ }
+
+ free(pOldTable);
+
/* Restart the world */
dvmResumeAllThreads(SUSPEND_FOR_JIT);