Jit: Rework delayed start plus misc. cleanup
Defer initialization of jit to support upcoming feature to wait until
first screen is painted to start in order to avoid wasting effort on
jit'ng initialization code. Timed delay in place for the moment.
To change the on/off state, call dvmSuspendAllThreads(), update the
value of gDvmJit.pJitTable and then dvmResumeAllThreads().
Each time a thread goes through the heavyweight check suspend path, returns
from a monitor lock/unlock or returns from a JNI call, it will refresh
its on/off state.
Also:
Recognize and handle failure to increase size of JitTable.
Avoid repeated lock/unlock of JitTable modification mutex during resize
Make all work order enqueue actions non-blocking, which includes adding
a non-blocking mutex lock: dvmTryLockMutex().
Fix bug Jeff noticed where we were using a half-word form of a Thumb2
instruction rather than the byte form.
Minor comment changes.
diff --git a/vm/Thread.c b/vm/Thread.c
index 05c89e2..e26e679 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -525,6 +525,7 @@
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";
}
diff --git a/vm/Thread.h b/vm/Thread.h
index 5e0ad4d..964c968 100644
--- a/vm/Thread.h
+++ b/vm/Thread.h
@@ -300,6 +300,7 @@
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);
@@ -378,6 +379,14 @@
}
/*
+ * 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)
diff --git a/vm/compiler/Compiler.c b/vm/compiler/Compiler.c
index 3886cce..ceb1da1 100644
--- a/vm/compiler/Compiler.c
+++ b/vm/compiler/Compiler.c
@@ -49,15 +49,21 @@
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.
@@ -66,7 +72,7 @@
*/
if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE) {
result = false;
- goto done;
+ goto unlockAndExit;
}
for (numWork = gDvmJit.compilerQueueLength,
@@ -75,7 +81,7 @@
numWork--) {
/* Already enqueued */
if (gDvmJit.compilerWorkQueue[i++].pc == pc)
- goto done;
+ goto unlockAndExit;
/* Wrap around */
if (i == COMPILER_WORK_QUEUE_SIZE)
i = 0;
@@ -99,9 +105,8 @@
cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
assert(cc == 0);
-done:
+unlockAndExit:
dvmUnlockMutex(&gDvmJit.compilerLock);
- dvmChangeStatus(NULL, oldStatus);
return result;
}
@@ -267,21 +272,112 @@
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);
/*
@@ -299,18 +395,22 @@
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;
@@ -328,7 +428,7 @@
* stale code stops leaking.
*/
#if 0
- if (gDvmJit.codeCacheFull == true) {
+ if (gDvmJit.codeCacheFull == true || resizeFail) {
if (gDvmJit.delayCodeCacheReset == 0) {
resetCodeCache();
assert(workQueueLength() == 0 ||
@@ -359,60 +459,13 @@
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)
diff --git a/vm/compiler/Frontend.c b/vm/compiler/Frontend.c
index a4a82c9..347bc50 100644
--- a/vm/compiler/Frontend.c
+++ b/vm/compiler/Frontend.c
@@ -299,6 +299,11 @@
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));
diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c
index 8861102..648d8f4 100644
--- a/vm/compiler/codegen/arm/CodegenDriver.c
+++ b/vm/compiler/codegen/arm/CodegenDriver.c
@@ -1709,6 +1709,14 @@
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);
@@ -1786,6 +1794,7 @@
}
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,
@@ -2165,6 +2174,7 @@
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 {
@@ -2227,6 +2237,7 @@
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) {
diff --git a/vm/compiler/codegen/arm/Thumb2/Factory.c b/vm/compiler/codegen/arm/Thumb2/Factory.c
index dfa60fe..ea93bbf 100644
--- a/vm/compiler/codegen/arm/Thumb2/Factory.c
+++ b/vm/compiler/codegen/arm/Thumb2/Factory.c
@@ -964,7 +964,7 @@
opCode = kThumbStrbRRI5;
} else if (thumb2Form) {
shortForm = true;
- opCode = kThumb2StrhRRI12;
+ opCode = kThumb2StrbRRI12;
}
break;
default:
diff --git a/vm/compiler/codegen/arm/Thumb2/Gen.c b/vm/compiler/codegen/arm/Thumb2/Gen.c
index f4c7aa2..5f2a6a2 100644
--- a/vm/compiler/codegen/arm/Thumb2/Gen.c
+++ b/vm/compiler/codegen/arm/Thumb2/Gen.c
@@ -241,6 +241,14 @@
}
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);
diff --git a/vm/compiler/template/armv5te/footer.S b/vm/compiler/template/armv5te/footer.S
index c1487dd..5d76f48 100644
--- a/vm/compiler/template/armv5te/footer.S
+++ b/vm/compiler/template/armv5te/footer.S
@@ -25,15 +25,20 @@
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
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
index 36d3ea1..afd69f3 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
@@ -243,7 +243,7 @@
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
@@ -306,7 +306,7 @@
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
@@ -1417,15 +1417,20 @@
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
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
index 48dd707..d73e010 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
@@ -243,7 +243,7 @@
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
@@ -306,7 +306,7 @@
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
@@ -1142,15 +1142,20 @@
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
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
index 469919a..12af0d2 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
@@ -243,7 +243,7 @@
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
@@ -306,7 +306,7 @@
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
@@ -1417,15 +1417,20 @@
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
diff --git a/vm/interp/InterpDefs.h b/vm/interp/InterpDefs.h
index 2928371..3231968 100644
--- a/vm/interp/InterpDefs.h
+++ b/vm/interp/InterpDefs.h
@@ -159,6 +159,14 @@
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)
diff --git a/vm/interp/Jit.c b/vm/interp/Jit.c
index 2d35e51..34a7736 100644
--- a/vm/interp/Jit.c
+++ b/vm/interp/Jit.c
@@ -348,51 +348,9 @@
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;
}
@@ -553,6 +511,94 @@
#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
@@ -707,13 +753,19 @@
#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;
@@ -811,91 +863,6 @@
}
/*
- * 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
@@ -905,7 +872,7 @@
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 {
@@ -966,7 +933,7 @@
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
@@ -1041,12 +1008,14 @@
/*
* 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;
@@ -1062,6 +1031,13 @@
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;
@@ -1081,29 +1057,20 @@
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);
diff --git a/vm/interp/Jit.h b/vm/interp/Jit.h
index 177a734..3afaa6a 100644
--- a/vm/interp/Jit.h
+++ b/vm/interp/Jit.h
@@ -123,7 +123,5 @@
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*/
diff --git a/vm/mterp/Mterp.c b/vm/mterp/Mterp.c
index d14f6b2..ca2ca16 100644
--- a/vm/mterp/Mterp.c
+++ b/vm/mterp/Mterp.c
@@ -83,6 +83,7 @@
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)
diff --git a/vm/mterp/armv5te/footer.S b/vm/mterp/armv5te/footer.S
index 7476e29..3f4321c 100644
--- a/vm/mterp/armv5te/footer.S
+++ b/vm/mterp/armv5te/footer.S
@@ -377,8 +377,20 @@
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
@@ -574,13 +586,23 @@
@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
diff --git a/vm/mterp/common/asm-constants.h b/vm/mterp/common/asm-constants.h
index d6ff482..a3c8cec 100644
--- a/vm/mterp/common/asm-constants.h
+++ b/vm/mterp/common/asm-constants.h
@@ -107,6 +107,7 @@
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)
@@ -117,6 +118,7 @@
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)
@@ -127,6 +129,7 @@
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)
@@ -136,6 +139,7 @@
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 */
diff --git a/vm/mterp/out/InterpAsm-armv4t.S b/vm/mterp/out/InterpAsm-armv4t.S
index ba248de..da3199e 100644
--- a/vm/mterp/out/InterpAsm-armv4t.S
+++ b/vm/mterp/out/InterpAsm-armv4t.S
@@ -9904,8 +9904,20 @@
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
@@ -10101,13 +10113,23 @@
@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
diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S
index 966e4f0..336d9f2 100644
--- a/vm/mterp/out/InterpAsm-armv5te-vfp.S
+++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S
@@ -9422,8 +9422,20 @@
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
@@ -9619,13 +9631,23 @@
@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
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S
index da56e6c..b947e27 100644
--- a/vm/mterp/out/InterpAsm-armv5te.S
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -9898,8 +9898,20 @@
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
@@ -10095,13 +10107,23 @@
@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
diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S
index 606b064..0d1bcc8 100644
--- a/vm/mterp/out/InterpAsm-armv7-a.S
+++ b/vm/mterp/out/InterpAsm-armv7-a.S
@@ -9358,8 +9358,20 @@
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
@@ -9555,13 +9567,23 @@
@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