Jit stress mode: translate everything we can and self verify.
This represents a general clean-up of some existing command-line
options: -Xjitthreshold:num and -Xjitblocking. The jit threshold
controls how quickly we treat a Dalvik address as a potential trace
head. Normally this is set around 200 (and the range is 0..255, where
0 is in effect 256 and 1 means begin trace selection on first visit).
-Xjitblocking forces the system to pause execution whenever a translation
request is made and resume when that translation is complete. Normally
the system make a request but continues execution (to avoid jitter).
Additionally, if WITH_SELF_VERIFICATION is defined, we force blocking
to be true, and set the threshold to 1. And finally, we treat
threshold==1 as a special case and disable the 2nd-level trace-building
filter - which causes the system to immediately start trace selection.
diff --git a/vm/interp/InterpDefs.h b/vm/interp/InterpDefs.h
index 41a2bb8..744033e 100644
--- a/vm/interp/InterpDefs.h
+++ b/vm/interp/InterpDefs.h
@@ -150,7 +150,7 @@
JitState jitState;
void* jitResume;
u2* jitResumePC;
- const u2* jitTraceInProgress;
+ int jitThreshold;
#endif
#if defined(WITH_PROFILER) || defined(WITH_DEBUGGER)
diff --git a/vm/interp/Jit.c b/vm/interp/Jit.c
index d9b9caa..4c09979 100644
--- a/vm/interp/Jit.c
+++ b/vm/interp/Jit.c
@@ -348,6 +348,12 @@
unsigned int i;
bool res = true; /* Assume success */
+#if defined(WITH_SELF_VERIFICATION)
+ // Force JIT into blocking, translate everything mode
+ gDvmJit.threshold = 1;
+ gDvmJit.blockingMode = true;
+#endif
+
// Create the compiler thread and setup miscellaneous chores */
res &= dvmCompilerStartup();
@@ -380,7 +386,7 @@
res = false;
goto done;
}
- memset(pJitProfTable,0,JIT_PROF_SIZE);
+ memset(pJitProfTable,gDvmJit.threshold,JIT_PROF_SIZE);
for (i=0; i < gDvmJit.jitTableSize; i++) {
pJitTable[i].u.info.chain = gDvmJit.jitTableSize;
}
@@ -514,6 +520,32 @@
}
}
+void setTraceConstruction(JitEntry *slot, bool value)
+{
+
+ JitEntryInfoUnion oldValue;
+ JitEntryInfoUnion newValue;
+ do {
+ oldValue = slot->u;
+ newValue = oldValue;
+ newValue.info.traceConstruction = value;
+ } while (!ATOMIC_CMP_SWAP( &slot->u.infoWord,
+ oldValue.infoWord, newValue.infoWord));
+}
+
+void resetTracehead(InterpState* interpState, JitEntry *slot)
+{
+ slot->codeAddress = gDvmJit.interpretTemplate;
+ setTraceConstruction(slot, false);
+}
+
+/* Clean up any pending trace builds */
+void dvmJitAbortTraceSelect(InterpState* interpState)
+{
+ if (interpState->jitState == kJitTSelect)
+ interpState->jitState = kJitTSelectAbort;
+}
+
/*
* Adds to the current trace request one instruction at a time, just
* before that instruction is interpreted. This is the primary trace
@@ -539,6 +571,7 @@
|| gDvm.activeProfilers
#endif
);
+
/* Prepare to handle last PC and stage the current PC */
const u2 *lastPC = interpState->lastPC;
interpState->lastPC = pc;
@@ -625,6 +658,8 @@
case kJitTSelectEnd:
{
if (interpState->totalTraceLen == 0) {
+ /* Bad trace - mark as untranslatable */
+ dvmJitAbortTraceSelect(interpState);
switchInterp = !debugOrProfile;
break;
}
@@ -635,6 +670,7 @@
LOGE("Out of memory in trace selection");
dvmJitStopTranslationRequests();
interpState->jitState = kJitTSelectAbort;
+ dvmJitAbortTraceSelect(interpState);
switchInterp = !debugOrProfile;
break;
}
@@ -650,7 +686,8 @@
#endif
dvmCompilerWorkEnqueue(
interpState->currTraceHead,kWorkOrderTrace,desc);
- interpState->jitTraceInProgress = NULL;
+ setTraceConstruction(
+ dvmJitLookupAndAdd(interpState->currTraceHead), false);
if (gDvmJit.blockingMode) {
dvmCompilerDrainQueue();
}
@@ -668,6 +705,7 @@
#if defined(SHOW_TRACE)
LOGD("TraceGen: trace abort");
#endif
+ dvmJitAbortTraceSelect(interpState);
interpState->jitState = kJitNormal;
switchInterp = !debugOrProfile;
break;
@@ -864,7 +902,6 @@
* otherwise. NOTE: may be called even when trace selection is not being
* requested
*/
-
bool dvmJitCheckTraceRequest(Thread* self, InterpState* interpState)
{
bool res = false; /* Assume success */
@@ -873,11 +910,6 @@
* If previous trace-building attempt failed, force it's head to be
* interpret-only.
*/
- if (interpState->jitTraceInProgress) {
- JitEntry *slot = dvmJitLookupAndAdd(interpState->jitTraceInProgress);
- slot->codeAddress = gDvmJit.interpretTemplate;
- interpState->jitTraceInProgress = NULL;
- }
if (gDvmJit.pJitEntryTable != NULL) {
/* Two-level filtering scheme */
for (i=0; i< JIT_TRACE_THRESH_FILTER_SIZE; i++) {
@@ -894,6 +926,10 @@
interpState->threshFilter[i] = interpState->pc;
res = true;
}
+
+ /* If stress mode (threshold==1), always translate */
+ res &= (gDvmJit.threshold != 1);
+
/*
* If the compiler is backlogged, or if a debugger or profiler is
* active, cancel any JIT actions
@@ -919,26 +955,38 @@
interpState->jitState = kJitTSelectAbort;
LOGD("JIT: JitTable full, disabling profiling");
dvmJitStopTranslationRequests();
- } else if (slot->u.info.traceRequested) {
- /* Trace already requested - revert to interpreter */
+ } else if (slot->u.info.traceConstruction) {
+ /*
+ * Trace already request in progress, but most likely it
+ * aborted without cleaning up. Assume the worst and
+ * mark trace head as untranslatable. If we're wrong,
+ * the compiler thread will correct the entry when the
+ * translation is completed. The downside here is that
+ * some existing translation may chain to the interpret-only
+ * template instead of the real translation during this
+ * window. Performance, but not correctness, issue.
+ */
+ interpState->jitState = kJitTSelectAbort;
+ resetTracehead(interpState, slot);
+ } else if (slot->codeAddress) {
+ /* Nothing to do here - just return */
interpState->jitState = kJitTSelectAbort;
} else {
- /* 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));
+ /*
+ * Mark request. Note, we are not guaranteed exclusivity
+ * here. A window exists for another thread to be
+ * attempting to build this same trace. Rather than
+ * bear the cost of locking, we'll just allow that to
+ * happen. The compiler thread, if it chooses, can
+ * discard redundant requests.
+ */
+ setTraceConstruction(slot, true);
}
}
switch (interpState->jitState) {
case kJitTSelectRequest:
interpState->jitState = kJitTSelect;
interpState->currTraceHead = interpState->pc;
- interpState->jitTraceInProgress = interpState->pc;
interpState->currTraceRun = 0;
interpState->totalTraceLen = 0;
interpState->currRunHead = interpState->pc;
diff --git a/vm/interp/Jit.h b/vm/interp/Jit.h
index a69a1f0..b093164 100644
--- a/vm/interp/Jit.h
+++ b/vm/interp/Jit.h
@@ -89,13 +89,13 @@
*/
typedef struct JitEntryInfo {
- unsigned int traceRequested:1; /* already requested a translation */
+ unsigned int traceConstruction:1; /* build underway? */
unsigned int isMethodEntry:1;
unsigned int inlineCandidate:1;
unsigned int profileEnabled:1;
JitInstructionSetType instructionSet:4;
unsigned int unused:8;
- u2 chain; /* Index of next in chain */
+ u2 chain; /* Index of next in chain */
} JitEntryInfo;
typedef union JitEntryInfoUnion {
@@ -122,6 +122,8 @@
s8 dvmJitd2l(double d);
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*/