case SUSPEND_FOR_TBL_RESIZE: return "table-resize";
case SUSPEND_FOR_IC_PATCH: return "inline-cache-patch";
case SUSPEND_FOR_CC_RESET: return "reset-code-cache";
+ case SUSPEND_FOR_REFRESH: return "refresh jit status";
#endif
default: return "UNKNOWN";
}
SUSPEND_FOR_TBL_RESIZE, // jit-table resize
SUSPEND_FOR_IC_PATCH, // polymorphic callsite inline-cache patch
SUSPEND_FOR_CC_RESET, // code-cache reset
+ SUSPEND_FOR_REFRESH, // Reload data cached in interpState
#endif
} SuspendCause;
void dvmSuspendThread(Thread* thread);
}
/*
+ * Try grabbing a plain mutex. Returns 0 if successful.
+ */
+INLINE int dvmTryLockMutex(pthread_mutex_t* pMutex)
+{
+ return pthread_mutex_trylock(pMutex);
+}
+
+/*
* Unlock pthread mutex.
*/
INLINE void dvmUnlockMutex(pthread_mutex_t* pMutex)
return work;
}
+/*
+ * Attempt to enqueue a work order, returning true if successful.
+ * This routine will not block, but simply return if it couldn't
+ * aquire the lock or if the queue is full.
+ */
bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
{
int cc;
int i;
int numWork;
- int oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
bool result = true;
- dvmLockMutex(&gDvmJit.compilerLock);
+ if (dvmTryLockMutex(&gDvmJit.compilerLock)) {
+ return false; // Couldn't aquire the lock
+ }
/*
* Return if queue is full.
*/
if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE) {
result = false;
- goto done;
+ goto unlockAndExit;
}
for (numWork = gDvmJit.compilerQueueLength,
numWork--) {
/* Already enqueued */
if (gDvmJit.compilerWorkQueue[i++].pc == pc)
- goto done;
+ goto unlockAndExit;
/* Wrap around */
if (i == COMPILER_WORK_QUEUE_SIZE)
i = 0;
cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
assert(cc == 0);
-done:
+unlockAndExit:
dvmUnlockMutex(&gDvmJit.compilerLock);
- dvmChangeStatus(NULL, oldStatus);
return result;
}
dvmResumeAllThreads(SUSPEND_FOR_CC_RESET);
}
+bool compilerThreadStartup(void)
+{
+ JitEntry *pJitTable = NULL;
+ unsigned char *pJitProfTable = NULL;
+ unsigned int i;
+
+ if (!dvmCompilerArchInit())
+ goto fail;
+
+ /*
+ * Setup the code cache if we have not inherited a valid code cache
+ * from the zygote.
+ */
+ if (gDvmJit.codeCache == NULL) {
+ if (!dvmCompilerSetupCodeCache())
+ goto fail;
+ }
+
+ /* Allocate the initial arena block */
+ if (dvmCompilerHeapInit() == false) {
+ goto fail;
+ }
+
+ dvmInitMutex(&gDvmJit.compilerLock);
+ pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
+ pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
+
+ dvmLockMutex(&gDvmJit.compilerLock);
+
+ gDvmJit.haltCompilerThread = false;
+
+ /* Reset the work queue */
+ memset(gDvmJit.compilerWorkQueue, 0,
+ sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
+ gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
+ gDvmJit.compilerQueueLength = 0;
+
+ /* Track method-level compilation statistics */
+ gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
+
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+
+ /* Set up the JitTable */
+
+ /* Power of 2? */
+ assert(gDvmJit.jitTableSize &&
+ !(gDvmJit.jitTableSize & (gDvmJit.jitTableSize - 1)));
+
+ dvmInitMutex(&gDvmJit.tableLock);
+ dvmLockMutex(&gDvmJit.tableLock);
+ pJitTable = (JitEntry*)
+ calloc(gDvmJit.jitTableSize, sizeof(*pJitTable));
+ if (!pJitTable) {
+ LOGE("jit table allocation failed\n");
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ goto fail;
+ }
+ /*
+ * NOTE: the profile table must only be allocated once, globally.
+ * Profiling is turned on and off by nulling out gDvm.pJitProfTable
+ * and then restoring its original value. However, this action
+ * is not syncronized for speed so threads may continue to hold
+ * and update the profile table after profiling has been turned
+ * off by null'ng the global pointer. Be aware.
+ */
+ pJitProfTable = (unsigned char *)malloc(JIT_PROF_SIZE);
+ if (!pJitProfTable) {
+ LOGE("jit prof table allocation failed\n");
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ goto fail;
+ }
+ memset(pJitProfTable, gDvmJit.threshold, JIT_PROF_SIZE);
+ for (i=0; i < gDvmJit.jitTableSize; i++) {
+ pJitTable[i].u.info.chain = gDvmJit.jitTableSize;
+ }
+ /* Is chain field wide enough for termination pattern? */
+ assert(pJitTable[0].u.info.chain == gDvmJit.jitTableSize);
+
+ gDvmJit.pJitEntryTable = pJitTable;
+ gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+ gDvmJit.jitTableEntriesUsed = 0;
+ gDvmJit.compilerHighWater =
+ COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
+ gDvmJit.pProfTable = pJitProfTable;
+ dvmUnlockMutex(&gDvmJit.tableLock);
+
+ /* Signal running threads to refresh their cached pJitTable pointers */
+ dvmSuspendAllThreads(SUSPEND_FOR_REFRESH);
+ dvmResumeAllThreads(SUSPEND_FOR_REFRESH);
+ return true;
+
+fail:
+ return false;
+
+}
+
static void *compilerThreadStart(void *arg)
{
dvmChangeStatus(NULL, THREAD_VMWAIT);
/*
* Wait a little before recieving translation requests on the assumption
- * that process start-up code isn't worth compiling. The trace
- * selector won't attempt to request a translation if the queue is
- * filled, so we'll prevent by keeping the high water mark at zero
- * for a shore time.
+ * that process start-up code isn't worth compiling.
*/
- assert(gDvmJit.compilerHighWater == 0);
- usleep(1000);
- gDvmJit.compilerHighWater =
- COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
+ usleep(1 * 1000 * 1000);
+ compilerThreadStartup();
dvmLockMutex(&gDvmJit.compilerLock);
/*
continue;
} else {
do {
+ bool resizeFail = false;
CompilerWorkOrder work = workDequeue();
dvmUnlockMutex(&gDvmJit.compilerLock);
- /* Check whether there is a suspend request on me */
+ /*
+ * Check whether there is a suspend request on me. This
+ * is necessary to allow a clean shutdown.
+ */
dvmCheckSuspendPending(NULL);
/* Is JitTable filling up? */
if (gDvmJit.jitTableEntriesUsed >
(gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
- dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
+ resizeFail = dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
}
if (gDvmJit.haltCompilerThread) {
LOGD("Compiler shutdown in progress - discarding request");
- } else {
+ } else if (!resizeFail) {
/* If compilation failed, use interpret-template */
if (!dvmCompilerDoWork(&work)) {
work.result.codeAddress = gDvmJit.interpretTemplate;
* stale code stops leaking.
*/
#if 0
- if (gDvmJit.codeCacheFull == true) {
+ if (gDvmJit.codeCacheFull == true || resizeFail) {
if (gDvmJit.delayCodeCacheReset == 0) {
resetCodeCache();
assert(workQueueLength() == 0 ||
bool dvmCompilerStartup(void)
{
- /* Make sure the BBType enum is in sane state */
- assert(kChainingCellNormal == 0);
-
- /* Architecture-specific chores to initialize */
- if (!dvmCompilerArchInit())
- goto fail;
-
/*
- * Setup the code cache if it is not done so already. For apps it should be
- * done by the Zygote already, but for command-line dalvikvm invocation we
- * need to do it here.
+ * Defer initialization until we're sure JIT'ng makes sense. Launch
+ * the compiler thread, which will do the real initialization if and
+ * when it is signalled to do so.
*/
- if (gDvmJit.codeCache == NULL) {
- if (!dvmCompilerSetupCodeCache())
- goto fail;
- }
-
- /* Allocate the initial arena block */
- if (dvmCompilerHeapInit() == false) {
- goto fail;
- }
-
- dvmInitMutex(&gDvmJit.compilerLock);
- pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
- pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
-
- dvmLockMutex(&gDvmJit.compilerLock);
-
- gDvmJit.haltCompilerThread = false;
-
- /* Reset the work queue */
- memset(gDvmJit.compilerWorkQueue, 0,
- sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
- gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
- gDvmJit.compilerQueueLength = 0;
- /* Block new entries via HighWater until compiler thread is ready */
- gDvmJit.compilerHighWater = 0;
-
- assert(gDvmJit.compilerHighWater < COMPILER_WORK_QUEUE_SIZE);
- if (!dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
- compilerThreadStart, NULL)) {
- dvmUnlockMutex(&gDvmJit.compilerLock);
- goto fail;
- }
-
- /* Track method-level compilation statistics */
- gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
-
- dvmUnlockMutex(&gDvmJit.compilerLock);
-
- return true;
-
-fail:
- return false;
+ return dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
+ compilerThreadStart, NULL);
}
void dvmCompilerShutdown(void)
CompilationUnit cUnit;
CompilerMethodStats *methodStats;
+ /* If we've already compiled this trace, just return success */
+ if (dvmJitGetCodeAddr(startCodePtr)) {
+ return true;
+ }
+
compilationId++;
memset(&cUnit, 0, sizeof(CompilationUnit));
genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
/* Do the call */
opReg(cUnit, kOpBlx, r2);
+ /*
+ * Refresh Jit's on/off status, which may have changed if we were
+ * sent to VM_MONITOR state above.
+ * TUNING: pointer chase, but must reload following call
+ */
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState, ppJitProfTable), r0);
+ loadWordDisp(cUnit, r0, 0, r0);
+ storeWordDisp(cUnit, rGLUE, offsetof(InterpState, pJitProfTable), r0);
#if defined(WITH_DEADLOCK_PREDICTION)
if (isEnter) {
loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0);
}
case OP_CONST_WIDE_32: {
//TUNING: single routine to load constant pair for support doubles
+ //TUNING: load 0/-1 separately to avoid load dependency
rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
loadConstantValue(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
case OP_INT_TO_LONG:
rlSrc = updateLoc(cUnit, rlSrc);
rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
+ //TUNING: shouldn't loadValueDirect already check for phys reg?
if (rlSrc.location == kLocPhysReg) {
genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
} else {
rlDest = getDestLocWide(cUnit, mir, 0, 1);
rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
loadConstantValue(cUnit, rlResult.lowReg, BBBB);
+ //TUNING: do high separately to avoid load dependency
opRegRegImm(cUnit, kOpAsr, rlResult.highReg, rlResult.lowReg, 31);
storeValueWide(cUnit, rlDest, rlResult);
} else if (dalvikOpCode == OP_CONST_16) {
opCode = kThumbStrbRRI5;
} else if (thumb2Form) {
shortForm = true;
- opCode = kThumb2StrhRRI12;
+ opCode = kThumb2StrbRRI12;
}
break;
default:
}
genExportPC(cUnit, mir);
opReg(cUnit, kOpBlx, r7);
+ /*
+ * Refresh Jit's on/off status, which may have changed if we were
+ * sent to VM_MONITOR state above.
+ * TUNING: pointer chase, but must refresh following return from call
+ */
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState, ppJitProfTable), r0);
+ loadWordDisp(cUnit, r0, 0, r0);
+ storeWordDisp(cUnit, rGLUE, offsetof(InterpState, pJitProfTable), r0);
clobberCallRegs(cUnit);
LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+ @ Refresh Jit's on/off status
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable]
+
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
+ ldr r3, [r3] @ r1 <- pointer to Jit profile table
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
ldr r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ cache current JitProfTable
@ r0 = dalvikCallsitePC
bne .LhandleException @ no, handle exception
sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
- ldr r8, [r8] @ r3<- suspendCount (int)
+ ldr r8, [r8] @ r8<- suspendCount (int)
cmp r10, r9 @ bottom < interpStackEnd?
bxlt lr @ return to raise stack overflow excep.
@ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
add r12, lr, #2 @ setup the punt-to-interp address
sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
- ldr r8, [r8] @ r3<- suspendCount (int)
+ ldr r8, [r8] @ r8<- suspendCount (int)
cmp r10, r9 @ bottom < interpStackEnd?
bxlt r12 @ return to raise stack overflow excep.
@ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+ @ Refresh Jit's on/off status
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable]
+
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
+ ldr r3, [r3] @ r1 <- pointer to Jit profile table
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
ldr r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ cache current JitProfTable
@ r0 = dalvikCallsitePC
bne .LhandleException @ no, handle exception
sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
- ldr r8, [r8] @ r3<- suspendCount (int)
+ ldr r8, [r8] @ r8<- suspendCount (int)
cmp r10, r9 @ bottom < interpStackEnd?
bxlt lr @ return to raise stack overflow excep.
@ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
add r12, lr, #2 @ setup the punt-to-interp address
sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
- ldr r8, [r8] @ r3<- suspendCount (int)
+ ldr r8, [r8] @ r8<- suspendCount (int)
cmp r10, r9 @ bottom < interpStackEnd?
bxlt r12 @ return to raise stack overflow excep.
@ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+ @ Refresh Jit's on/off status
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable]
+
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
+ ldr r3, [r3] @ r1 <- pointer to Jit profile table
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
ldr r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ cache current JitProfTable
@ r0 = dalvikCallsitePC
bne .LhandleException @ no, handle exception
sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
- ldr r8, [r8] @ r3<- suspendCount (int)
+ ldr r8, [r8] @ r8<- suspendCount (int)
cmp r10, r9 @ bottom < interpStackEnd?
bxlt lr @ return to raise stack overflow excep.
@ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
add r12, lr, #2 @ setup the punt-to-interp address
sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
- ldr r8, [r8] @ r3<- suspendCount (int)
+ ldr r8, [r8] @ r8<- suspendCount (int)
cmp r10, r9 @ bottom < interpStackEnd?
bxlt r12 @ return to raise stack overflow excep.
@ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+ @ Refresh Jit's on/off status
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable]
+
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
+ ldr r3, [r3] @ r1 <- pointer to Jit profile table
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
ldr r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ cache current JitProfTable
@ r0 = dalvikCallsitePC
bne .LhandleException @ no, handle exception
void* jitResume;
u2* jitResumePC;
int jitThreshold;
+ /*
+ * ppJitProfTable holds the address of gDvmJit.pJitProfTable, which
+ * doubles as an on/off switch for the Jit. Because a change in
+ * the value of gDvmJit.pJitProfTable isn't reflected in the cached
+ * copy above (pJitProfTable), we need to periodically refresh it.
+ * ppJitProfTable is used for that purpose.
+ */
+ unsigned char** ppJitProfTable; // Used to refresh pJitProfTable
#endif
#if defined(WITH_PROFILER) || defined(WITH_DEBUGGER)
unsigned int i;
bool res = true; /* Assume success */
- // Create the compiler thread and setup miscellaneous chores */
- res &= dvmCompilerStartup();
-
- dvmInitMutex(&gDvmJit.tableLock);
- if (res && gDvm.executionMode == kExecutionModeJit) {
- JitEntry *pJitTable = NULL;
- unsigned char *pJitProfTable = NULL;
- // Power of 2?
- assert(gDvmJit.jitTableSize &&
- !(gDvmJit.jitTableSize & (gDvmJit.jitTableSize - 1)));
- dvmLockMutex(&gDvmJit.tableLock);
- pJitTable = (JitEntry*)
- calloc(gDvmJit.jitTableSize, sizeof(*pJitTable));
- if (!pJitTable) {
- LOGE("jit table allocation failed\n");
- res = false;
- goto done;
- }
- /*
- * NOTE: the profile table must only be allocated once, globally.
- * Profiling is turned on and off by nulling out gDvm.pJitProfTable
- * and then restoring its original value. However, this action
- * is not syncronized for speed so threads may continue to hold
- * and update the profile table after profiling has been turned
- * off by null'ng the global pointer. Be aware.
- */
- pJitProfTable = (unsigned char *)malloc(JIT_PROF_SIZE);
- if (!pJitProfTable) {
- LOGE("jit prof table allocation failed\n");
- res = false;
- goto done;
- }
- memset(pJitProfTable, gDvmJit.threshold, JIT_PROF_SIZE);
- for (i=0; i < gDvmJit.jitTableSize; i++) {
- pJitTable[i].u.info.chain = gDvmJit.jitTableSize;
- }
- /* Is chain field wide enough for termination pattern? */
- assert(pJitTable[0].u.info.chain == gDvmJit.jitTableSize);
-
-done:
- gDvmJit.pJitEntryTable = pJitTable;
- gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
- gDvmJit.jitTableEntriesUsed = 0;
- gDvmJit.pProfTable = pJitProfTable;
- dvmUnlockMutex(&gDvmJit.tableLock);
+ // Create the compiler thread, which will complete initialization
+ if (gDvm.executionMode == kExecutionModeJit) {
+ res = dvmCompilerStartup();
}
return res;
}
#endif
/*
+ * Find an entry in the JitTable, creating if necessary.
+ * Returns null if table is full.
+ */
+static JitEntry *lookupAndAdd(const u2* dPC, bool callerLocked)
+{
+ 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).
+ */
+ if (!callerLocked)
+ 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 */
+ if (!callerLocked)
+ 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) {
+ /*
+ * Initialize codeAddress and allocate the slot. Must
+ * happen in this order (since dPC is set, the entry is live.
+ */
+ gDvmJit.pJitEntryTable[idx].dPC = dPC;
+ gDvmJit.jitTableEntriesUsed++;
+ } else {
+ /* Table is full */
+ idx = chainEndMarker;
+ }
+ if (!callerLocked)
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ }
+ return (idx == chainEndMarker) ? NULL : &gDvmJit.pJitEntryTable[idx];
+}
+/*
* Adds to the current trace request one instruction at a time, just
* before that instruction is interpreted. This is the primary trace
* selection function. NOTE: return instruction are handled a little
#if defined(SHOW_TRACE)
LOGD("TraceGen: trace done, adding to queue");
#endif
- dvmCompilerWorkEnqueue(
- interpState->currTraceHead,kWorkOrderTrace,desc);
- setTraceConstruction(
- dvmJitLookupAndAdd(interpState->currTraceHead), false);
- if (gDvmJit.blockingMode) {
- dvmCompilerDrainQueue();
+ if (dvmCompilerWorkEnqueue(
+ interpState->currTraceHead,kWorkOrderTrace,desc)) {
+ /* Work order successfully enqueued */
+ if (gDvmJit.blockingMode) {
+ dvmCompilerDrainQueue();
+ }
}
+ /*
+ * Reset "trace in progress" flag whether or not we
+ * successfully entered a work order.
+ */
+ setTraceConstruction(
+ lookupAndAdd(interpState->currTraceHead, false), false);
switchInterp = !debugOrProfile;
}
break;
}
/*
- * 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) {
- /*
- * Initialize codeAddress and allocate the slot. Must
- * happen in this order (since dPC is set, the entry is live.
- */
- 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 initial state to
* JIT'd code, it must not be altered without first halting all
void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set) {
JitEntryInfoUnion oldValue;
JitEntryInfoUnion newValue;
- JitEntry *jitEntry = dvmJitLookupAndAdd(dPC);
+ JitEntry *jitEntry = lookupAndAdd(dPC, false);
assert(jitEntry);
/* Note: order of update is important */
do {
interpState->jitState = kJitNormal;
}
} else if (interpState->jitState == kJitTSelectRequest) {
- JitEntry *slot = dvmJitLookupAndAdd(interpState->pc);
+ JitEntry *slot = lookupAndAdd(interpState->pc, false);
if (slot == NULL) {
/*
* Table is full. This should have been
/*
* Resizes the JitTable. Must be a power of 2, and returns true on failure.
- * Stops all threads, and thus is a heavyweight operation.
+ * Stops all threads, and thus is a heavyweight operation. May only be called
+ * by the compiler thread.
*/
bool dvmJitResizeJitTable( unsigned int size )
{
JitEntry *pNewTable;
JitEntry *pOldTable;
+ JitEntry tempEntry;
u4 newMask;
unsigned int oldSize;
unsigned int i;
return true;
}
+ /* Make sure requested size is compatible with chain field width */
+ tempEntry.u.info.chain = size;
+ if (tempEntry.u.info.chain != size) {
+ LOGD("Jit: JitTable request of %d too big", size);
+ return true;
+ }
+
pNewTable = (JitEntry*)calloc(size, sizeof(*pNewTable));
if (pNewTable == NULL) {
return true;
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;
- }
+ p = lookupAndAdd(pOldTable[i].dPC, true /* holds tableLock*/ );
+ 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);
}
}
+ dvmUnlockMutex(&gDvmJit.tableLock);
free(pOldTable);
s8 dvmJitf2l(float f);
void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set);
void dvmJitAbortTraceSelect(InterpState* interpState);
-JitEntry *dvmJitLookupAndAdd(const u2* dPC);
-
#endif /*_DALVIK_INTERP_JIT*/
glue->pSelfSuspendCount = &self->suspendCount;
#if defined(WITH_JIT)
glue->pJitProfTable = gDvmJit.pProfTable;
+ glue->ppJitProfTable = &gDvmJit.pProfTable;
glue->jitThreshold = gDvmJit.threshold;
#endif
#if defined(WITH_DEBUGGER)
bx lr @ nothing to do, return
2: @ check suspend
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r10<-&gDvmJit.pJitProfTable
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r10 <- pJitProfTable
EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
b dvmCheckSuspendPending @ suspend if necessary, then return
3: @ debugger/profiler enabled, bail out
@ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
MTERP_OFFSET(offGlue_jitResume, MterpGlue, jitResume, 64)
MTERP_OFFSET(offGlue_jitResumePC, MterpGlue, jitResumePC, 68)
MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 72)
+MTERP_OFFSET(offGlue_ppJitProfTable, MterpGlue, ppJitProfTable, 76)
#endif
#elif defined(WITH_DEBUGGER)
MTERP_OFFSET(offGlue_pDebuggerActive, MterpGlue, pDebuggerActive, 40)
MTERP_OFFSET(offGlue_jitResume, MterpGlue, jitResume, 60)
MTERP_OFFSET(offGlue_jitResumePC, MterpGlue, jitResumePC, 64)
MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 68)
+MTERP_OFFSET(offGlue_jitppJitProfTable, MterpGlue, ppJitProfTable, 72)
#endif
#elif defined(WITH_PROFILER)
MTERP_OFFSET(offGlue_pActiveProfilers, MterpGlue, pActiveProfilers, 40)
MTERP_OFFSET(offGlue_jitResume, MterpGlue, jitResume, 60)
MTERP_OFFSET(offGlue_jitResumePC, MterpGlue, jitResumePC, 64)
MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 68)
+MTERP_OFFSET(offGlue_jitppJitProfTable, MterpGlue, ppJitProfTable, 72)
#endif
#else
MTERP_OFFSET(offGlue_entryPoint, MterpGlue, entryPoint, 40)
MTERP_OFFSET(offGlue_jitResume, MterpGlue, jitResume, 56)
MTERP_OFFSET(offGlue_jitResumePC, MterpGlue, jitResumePC, 60)
MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 64)
+MTERP_OFFSET(offGlue_jitppJitProfTable, MterpGlue, ppJitProfTable, 68)
#endif
#endif
/* make sure all JValue union members are stored at the same offset */
bx lr @ nothing to do, return
2: @ check suspend
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r10<-&gDvmJit.pJitProfTable
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r10 <- pJitProfTable
EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
b dvmCheckSuspendPending @ suspend if necessary, then return
3: @ debugger/profiler enabled, bail out
@ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
bx lr @ nothing to do, return
2: @ check suspend
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r10<-&gDvmJit.pJitProfTable
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r10 <- pJitProfTable
EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
b dvmCheckSuspendPending @ suspend if necessary, then return
3: @ debugger/profiler enabled, bail out
@ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
bx lr @ nothing to do, return
2: @ check suspend
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r10<-&gDvmJit.pJitProfTable
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r10 <- pJitProfTable
EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
b dvmCheckSuspendPending @ suspend if necessary, then return
3: @ debugger/profiler enabled, bail out
@ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
bx lr @ nothing to do, return
2: @ check suspend
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r10<-&gDvmJit.pJitProfTable
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r10 <- pJitProfTable
EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
b dvmCheckSuspendPending @ suspend if necessary, then return
3: @ debugger/profiler enabled, bail out
@ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST