diff options
Diffstat (limited to 'vm/compiler/Compiler.c')
-rw-r--r-- | vm/compiler/Compiler.c | 755 |
1 files changed, 755 insertions, 0 deletions
diff --git a/vm/compiler/Compiler.c b/vm/compiler/Compiler.c new file mode 100644 index 0000000..60f060c --- /dev/null +++ b/vm/compiler/Compiler.c @@ -0,0 +1,755 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/mman.h> +#include <errno.h> +#include <cutils/ashmem.h> + +#include "Dalvik.h" +#include "interp/Jit.h" +#include "CompilerInternals.h" + +static inline bool workQueueLength(void) +{ + return gDvmJit.compilerQueueLength; +} + +static CompilerWorkOrder workDequeue(void) +{ + assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind + != kWorkOrderInvalid); + CompilerWorkOrder work = + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex]; + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind = + kWorkOrderInvalid; + if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) { + gDvmJit.compilerWorkDequeueIndex = 0; + } + gDvmJit.compilerQueueLength--; + if (gDvmJit.compilerQueueLength == 0) { + dvmSignalCond(&gDvmJit.compilerQueueEmpty); + } + + /* Remember the high water mark of the queue length */ + if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued) + gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength; + + 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. + * + * NOTE: Make sure that the caller frees the info pointer if the return value + * is false. + */ +bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info) +{ + int cc; + int i; + int numWork; + bool result = true; + + if (dvmTryLockMutex(&gDvmJit.compilerLock)) { + return false; // Couldn't acquire the lock + } + + /* + * Return if queue or code cache is full. + */ + if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE || + gDvmJit.codeCacheFull == true) { + result = false; + goto unlockAndExit; + } + + for (numWork = gDvmJit.compilerQueueLength, + i = gDvmJit.compilerWorkDequeueIndex; + numWork > 0; + numWork--) { + /* Already enqueued */ + if (gDvmJit.compilerWorkQueue[i++].pc == pc) + goto unlockAndExit; + /* Wrap around */ + if (i == COMPILER_WORK_QUEUE_SIZE) + i = 0; + } + + CompilerWorkOrder *newOrder = + &gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex]; + newOrder->pc = pc; + newOrder->kind = kind; + newOrder->info = info; + newOrder->result.methodCompilationAborted = NULL; + newOrder->result.codeAddress = NULL; + newOrder->result.discardResult = + (kind == kWorkOrderTraceDebug) ? true : false; + newOrder->result.requestingThread = dvmThreadSelf(); + + gDvmJit.compilerWorkEnqueueIndex++; + if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE) + gDvmJit.compilerWorkEnqueueIndex = 0; + gDvmJit.compilerQueueLength++; + cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity); + assert(cc == 0); + +unlockAndExit: + dvmUnlockMutex(&gDvmJit.compilerLock); + return result; +} + +/* Block until the queue length is 0, or there is a pending suspend request */ +void dvmCompilerDrainQueue(void) +{ + Thread *self = dvmThreadSelf(); + + dvmLockMutex(&gDvmJit.compilerLock); + while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread && + self->suspendCount == 0) { + /* + * Use timed wait here - more than one mutator threads may be blocked + * but the compiler thread will only signal once when the queue is + * emptied. Furthermore, the compiler thread may have been shutdown + * so the blocked thread may never get the wakeup signal. + */ + dvmRelativeCondWait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock, 1000, 0); + } + dvmUnlockMutex(&gDvmJit.compilerLock); +} + +bool dvmCompilerSetupCodeCache(void) +{ + extern void dvmCompilerTemplateStart(void); + extern void dmvCompilerTemplateEnd(void); + int fd; + + /* Allocate the code cache */ + fd = ashmem_create_region("dalvik-jit-code-cache", gDvmJit.codeCacheSize); + if (fd < 0) { + LOGE("Could not create %u-byte ashmem region for the JIT code cache", + gDvmJit.codeCacheSize); + return false; + } + gDvmJit.codeCache = mmap(NULL, gDvmJit.codeCacheSize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE , fd, 0); + close(fd); + if (gDvmJit.codeCache == MAP_FAILED) { + LOGE("Failed to mmap the JIT code cache: %s\n", strerror(errno)); + return false; + } + + gDvmJit.pageSizeMask = getpagesize() - 1; + + /* This can be found through "dalvik-jit-code-cache" in /proc/<pid>/maps */ + // LOGD("Code cache starts at %p", gDvmJit.codeCache); + + /* Copy the template code into the beginning of the code cache */ + int templateSize = (intptr_t) dmvCompilerTemplateEnd - + (intptr_t) dvmCompilerTemplateStart; + memcpy((void *) gDvmJit.codeCache, + (void *) dvmCompilerTemplateStart, + templateSize); + + /* + * Work around a CPU bug by keeping the 32-bit ARM handler code in its own + * page. + */ + if (dvmCompilerInstructionSet() == DALVIK_JIT_THUMB2) { + templateSize = (templateSize + 4095) & ~4095; + } + + gDvmJit.templateSize = templateSize; + gDvmJit.codeCacheByteUsed = templateSize; + + /* Only flush the part in the code cache that is being used now */ + cacheflush((intptr_t) gDvmJit.codeCache, + (intptr_t) gDvmJit.codeCache + templateSize, 0); + + int result = mprotect(gDvmJit.codeCache, gDvmJit.codeCacheSize, + PROTECT_CODE_CACHE_ATTRS); + + if (result == -1) { + LOGE("Failed to remove the write permission for the code cache"); + dvmAbort(); + } + + return true; +} + +static void crawlDalvikStack(Thread *thread, bool print) +{ + void *fp = thread->curFrame; + StackSaveArea* saveArea = NULL; + int stackLevel = 0; + + if (print) { + LOGD("Crawling tid %d (%s / %p %s)", thread->systemTid, + dvmGetThreadStatusStr(thread->status), + thread->inJitCodeCache, + thread->inJitCodeCache ? "jit" : "interp"); + } + /* Crawl the Dalvik stack frames to clear the returnAddr field */ + while (fp != NULL) { + saveArea = SAVEAREA_FROM_FP(fp); + + if (print) { + if (dvmIsBreakFrame(fp)) { + LOGD(" #%d: break frame (%p)", + stackLevel, saveArea->returnAddr); + } + else { + LOGD(" #%d: %s.%s%s (%p)", + stackLevel, + saveArea->method->clazz->descriptor, + saveArea->method->name, + dvmIsNativeMethod(saveArea->method) ? + " (native)" : "", + saveArea->returnAddr); + } + } + stackLevel++; + saveArea->returnAddr = NULL; + assert(fp != saveArea->prevFrame); + fp = saveArea->prevFrame; + } + /* Make sure the stack is fully unwound to the bottom */ + assert(saveArea == NULL || + (u1 *) (saveArea+1) == thread->interpStackStart); +} + +static void resetCodeCache(void) +{ + Thread* thread; + u8 startTime = dvmGetRelativeTimeUsec(); + int inJit = 0; + int byteUsed = gDvmJit.codeCacheByteUsed; + + /* If any thread is found stuck in the JIT state, don't reset the cache */ + for (thread = gDvm.threadList; thread != NULL; thread = thread->next) { + /* + * Crawl the stack to wipe out the returnAddr field so that + * 1) the soon-to-be-deleted code in the JIT cache won't be used + * 2) or the thread stuck in the JIT land will soon return + * to the interpreter land + */ + crawlDalvikStack(thread, false); + if (thread->inJitCodeCache) { + inJit++; + } + } + + if (inJit) { + LOGD("JIT code cache reset delayed (%d bytes %d/%d)", + gDvmJit.codeCacheByteUsed, gDvmJit.numCodeCacheReset, + ++gDvmJit.numCodeCacheResetDelayed); + return; + } + + /* Lock the mutex to clean up the work queue */ + dvmLockMutex(&gDvmJit.compilerLock); + + /* Drain the work queue to free the work orders */ + while (workQueueLength()) { + CompilerWorkOrder work = workDequeue(); + free(work.info); + } + + /* Reset the JitEntry table contents to the initial unpopulated state */ + dvmJitResetTable(); + + UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed); + /* + * Wipe out the code cache content to force immediate crashes if + * stale JIT'ed code is invoked. + */ + memset((char *) gDvmJit.codeCache + gDvmJit.templateSize, + 0, + gDvmJit.codeCacheByteUsed - gDvmJit.templateSize); + cacheflush((intptr_t) gDvmJit.codeCache, + (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed, 0); + + PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed); + + /* Reset the current mark of used bytes to the end of template code */ + gDvmJit.codeCacheByteUsed = gDvmJit.templateSize; + gDvmJit.numCompilations = 0; + + /* Reset the work queue */ + memset(gDvmJit.compilerWorkQueue, 0, + sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE); + gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0; + gDvmJit.compilerQueueLength = 0; + + /* Reset the IC patch work queue */ + dvmLockMutex(&gDvmJit.compilerICPatchLock); + gDvmJit.compilerICPatchIndex = 0; + dvmUnlockMutex(&gDvmJit.compilerICPatchLock); + + /* All clear now */ + gDvmJit.codeCacheFull = false; + + dvmUnlockMutex(&gDvmJit.compilerLock); + + LOGD("JIT code cache reset in %lld ms (%d bytes %d/%d)", + (dvmGetRelativeTimeUsec() - startTime) / 1000, + byteUsed, ++gDvmJit.numCodeCacheReset, + gDvmJit.numCodeCacheResetDelayed); +} + +/* + * Perform actions that are only safe when all threads are suspended. Currently + * we do: + * 1) Check if the code cache is full. If so reset it and restart populating it + * from scratch. + * 2) Patch predicted chaining cells by consuming recorded work orders. + */ +void dvmCompilerPerformSafePointChecks(void) +{ + if (gDvmJit.codeCacheFull) { + resetCodeCache(); + } + dvmCompilerPatchInlineCache(); +} + +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; + } + + dvmLockMutex(&gDvmJit.compilerLock); + + /* Track method-level compilation statistics */ + gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL); + +#if defined(WITH_JIT_TUNING) + gDvm.verboseShutdown = true; +#endif + + 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); + /* + * If the VM is launched with wait-on-the-debugger, we will need to hide + * the profile table here + */ + gDvmJit.pProfTable = dvmDebuggerOrProfilerActive() ? NULL : pJitProfTable; + gDvmJit.pProfTableCopy = pJitProfTable; + dvmUnlockMutex(&gDvmJit.tableLock); + + /* Signal running threads to refresh their cached pJitTable pointers */ + dvmSuspendAllThreads(SUSPEND_FOR_REFRESH); + dvmResumeAllThreads(SUSPEND_FOR_REFRESH); + + /* Enable signature breakpoints by customizing the following code */ +#if defined(SIGNATURE_BREAKPOINT) + /* + * Suppose one sees the following native crash in the bugreport: + * I/DEBUG ( 1638): Build fingerprint: 'unknown' + * I/DEBUG ( 1638): pid: 2468, tid: 2507 >>> com.google.android.gallery3d + * I/DEBUG ( 1638): signal 11 (SIGSEGV), fault addr 00001400 + * I/DEBUG ( 1638): r0 44ea7190 r1 44e4f7b8 r2 44ebc710 r3 00000000 + * I/DEBUG ( 1638): r4 00000a00 r5 41862dec r6 4710dc10 r7 00000280 + * I/DEBUG ( 1638): r8 ad010f40 r9 46a37a12 10 001116b0 fp 42a78208 + * I/DEBUG ( 1638): ip 00000090 sp 4710dbc8 lr ad060e67 pc 46b90682 + * cpsr 00000030 + * I/DEBUG ( 1638): #00 pc 46b90682 /dev/ashmem/dalvik-jit-code-cache + * I/DEBUG ( 1638): #01 pc 00060e62 /system/lib/libdvm.so + * + * I/DEBUG ( 1638): code around pc: + * I/DEBUG ( 1638): 46b90660 6888d01c 34091dcc d2174287 4a186b68 + * I/DEBUG ( 1638): 46b90670 d0052800 68006809 28004790 6b68d00e + * I/DEBUG ( 1638): 46b90680 512000bc 37016eaf 6ea866af 6f696028 + * I/DEBUG ( 1638): 46b90690 682a6069 429a686b e003da08 6df1480b + * I/DEBUG ( 1638): 46b906a0 1c2d4788 47806d70 46a378fa 47806d70 + * + * Clearly it is a JIT bug. To find out which translation contains the + * offending code, the content of the memory dump around the faulting PC + * can be pasted into the gDvmJit.signatureBreakpoint[] array and next time + * when a similar compilation is being created, the JIT compiler replay the + * trace in the verbose mode and one can investigate the instruction + * sequence in details. + * + * The length of the signature may need additional experiments to determine. + * The rule of thumb is don't include PC-relative instructions in the + * signature since it may be affected by the alignment of the compiled code. + * However, a signature that's too short might increase the chance of false + * positive matches. Using gdbjithelper to disassembly the memory content + * first might be a good companion approach. + * + * For example, if the next 4 words starting from 46b90680 is pasted into + * the data structure: + */ + + gDvmJit.signatureBreakpointSize = 4; + gDvmJit.signatureBreakpoint = + malloc(sizeof(u4) * gDvmJit.signatureBreakpointSize); + gDvmJit.signatureBreakpoint[0] = 0x512000bc; + gDvmJit.signatureBreakpoint[1] = 0x37016eaf; + gDvmJit.signatureBreakpoint[2] = 0x6ea866af; + gDvmJit.signatureBreakpoint[3] = 0x6f696028; + + /* + * The following log will be printed when a match is found in subsequent + * testings: + * + * D/dalvikvm( 2468): Signature match starting from offset 0x34 (4 words) + * D/dalvikvm( 2468): -------- + * D/dalvikvm( 2468): Compiler: Building trace for computeVisibleItems, + * offset 0x1f7 + * D/dalvikvm( 2468): 0x46a37a12: 0x0090 add-int v42, v5, v26 + * D/dalvikvm( 2468): 0x46a37a16: 0x004d aput-object v13, v14, v42 + * D/dalvikvm( 2468): 0x46a37a1a: 0x0028 goto, (#0), (#0) + * D/dalvikvm( 2468): 0x46a3794e: 0x00d8 add-int/lit8 v26, v26, (#1) + * D/dalvikvm( 2468): 0x46a37952: 0x0028 goto, (#0), (#0) + * D/dalvikvm( 2468): 0x46a378ee: 0x0002 move/from16 v0, v26, (#0) + * D/dalvikvm( 2468): 0x46a378f2: 0x0002 move/from16 v1, v29, (#0) + * D/dalvikvm( 2468): 0x46a378f6: 0x0035 if-ge v0, v1, (#10) + * D/dalvikvm( 2468): TRACEINFO (554): 0x46a37624 + * Lcom/cooliris/media/GridLayer;computeVisibleItems 0x1f7 14 of 934, 8 + * blocks + * : + * : + * D/dalvikvm( 2468): 0x20 (0020): ldr r0, [r5, #52] + * D/dalvikvm( 2468): 0x22 (0022): ldr r2, [pc, #96] + * D/dalvikvm( 2468): 0x24 (0024): cmp r0, #0 + * D/dalvikvm( 2468): 0x26 (0026): beq 0x00000034 + * D/dalvikvm( 2468): 0x28 (0028): ldr r1, [r1, #0] + * D/dalvikvm( 2468): 0x2a (002a): ldr r0, [r0, #0] + * D/dalvikvm( 2468): 0x2c (002c): blx r2 + * D/dalvikvm( 2468): 0x2e (002e): cmp r0, #0 + * D/dalvikvm( 2468): 0x30 (0030): beq 0x00000050 + * D/dalvikvm( 2468): 0x32 (0032): ldr r0, [r5, #52] + * D/dalvikvm( 2468): 0x34 (0034): lsls r4, r7, #2 + * D/dalvikvm( 2468): 0x36 (0036): str r0, [r4, r4] + * D/dalvikvm( 2468): -------- dalvik offset: 0x01fb @ goto, (#0), (#0) + * D/dalvikvm( 2468): L0x0195: + * D/dalvikvm( 2468): -------- dalvik offset: 0x0195 @ add-int/lit8 v26, + * v26, (#1) + * D/dalvikvm( 2468): 0x38 (0038): ldr r7, [r5, #104] + * D/dalvikvm( 2468): 0x3a (003a): adds r7, r7, #1 + * D/dalvikvm( 2468): 0x3c (003c): str r7, [r5, #104] + * D/dalvikvm( 2468): -------- dalvik offset: 0x0197 @ goto, (#0), (#0) + * D/dalvikvm( 2468): L0x0165: + * D/dalvikvm( 2468): -------- dalvik offset: 0x0165 @ move/from16 v0, v26, + * (#0) + * D/dalvikvm( 2468): 0x3e (003e): ldr r0, [r5, #104] + * D/dalvikvm( 2468): 0x40 (0040): str r0, [r5, #0] + * + * The "str r0, [r4, r4]" is indeed the culprit of the native crash. + */ +#endif + + return true; + +fail: + return false; + +} + +static void *compilerThreadStart(void *arg) +{ + dvmChangeStatus(NULL, THREAD_VMWAIT); + + /* + * If we're not running stand-alone, wait a little before + * recieving translation requests on the assumption that process start + * up code isn't worth compiling. We'll resume when the framework + * signals us that the first screen draw has happened, or the timer + * below expires (to catch daemons). + * + * There is a theoretical race between the callback to + * VMRuntime.startJitCompiation and when the compiler thread reaches this + * point. In case the callback happens earlier, in order not to permanently + * hold the system_server (which is not using the timed wait) in + * interpreter-only mode we bypass the delay here. + */ + if (gDvmJit.runningInAndroidFramework && + !gDvmJit.alreadyEnabledViaFramework) { + /* + * If the current VM instance is the system server (detected by having + * 0 in gDvm.systemServerPid), we will use the indefinite wait on the + * conditional variable to determine whether to start the JIT or not. + * If the system server detects that the whole system is booted in + * safe mode, the conditional variable will never be signaled and the + * system server will remain in the interpreter-only mode. All + * subsequent apps will be started with the --enable-safemode flag + * explicitly appended. + */ + if (gDvm.systemServerPid == 0) { + dvmLockMutex(&gDvmJit.compilerLock); + pthread_cond_wait(&gDvmJit.compilerQueueActivity, + &gDvmJit.compilerLock); + dvmUnlockMutex(&gDvmJit.compilerLock); + LOGD("JIT started for system_server"); + } else { + dvmLockMutex(&gDvmJit.compilerLock); + /* + * TUNING: experiment with the delay & perhaps make it + * target-specific + */ + dvmRelativeCondWait(&gDvmJit.compilerQueueActivity, + &gDvmJit.compilerLock, 3000, 0); + dvmUnlockMutex(&gDvmJit.compilerLock); + } + if (gDvmJit.haltCompilerThread) { + return NULL; + } + } + + compilerThreadStartup(); + + dvmLockMutex(&gDvmJit.compilerLock); + /* + * Since the compiler thread will not touch any objects on the heap once + * being created, we just fake its state as VMWAIT so that it can be a + * bit late when there is suspend request pending. + */ + while (!gDvmJit.haltCompilerThread) { + if (workQueueLength() == 0) { + int cc; + cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty); + assert(cc == 0); + pthread_cond_wait(&gDvmJit.compilerQueueActivity, + &gDvmJit.compilerLock); + continue; + } else { + do { + CompilerWorkOrder work = workDequeue(); + dvmUnlockMutex(&gDvmJit.compilerLock); +#if defined(WITH_JIT_TUNING) + u8 startTime = dvmGetRelativeTimeUsec(); +#endif + /* + * Check whether there is a suspend request on me. This + * is necessary to allow a clean shutdown. + * + * However, in the blocking stress testing mode, let the + * compiler thread continue doing compilations to unblock + * other requesting threads. This may occasionally cause + * shutdown from proceeding cleanly in the standalone invocation + * of the vm but this should be acceptable. + */ + if (!gDvmJit.blockingMode) + dvmCheckSuspendPending(dvmThreadSelf()); + /* Is JitTable filling up? */ + if (gDvmJit.jitTableEntriesUsed > + (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) { + bool resizeFail = + dvmJitResizeJitTable(gDvmJit.jitTableSize * 2); + /* + * If the jit table is full, consider it's time to reset + * the code cache too. + */ + gDvmJit.codeCacheFull |= resizeFail; + } + if (gDvmJit.haltCompilerThread) { + LOGD("Compiler shutdown in progress - discarding request"); + } else if (!gDvmJit.codeCacheFull) { + bool compileOK = false; + jmp_buf jmpBuf; + work.bailPtr = &jmpBuf; + bool aborted = setjmp(jmpBuf); + if (!aborted) { + compileOK = dvmCompilerDoWork(&work); + } + if (aborted || !compileOK) { + dvmCompilerArenaReset(); + } else if (!work.result.discardResult && + work.result.codeAddress) { + /* Make sure that proper code addr is installed */ + assert(work.result.codeAddress != NULL); + dvmJitSetCodeAddr(work.pc, work.result.codeAddress, + work.result.instructionSet); + } + } + free(work.info); +#if defined(WITH_JIT_TUNING) + gDvmJit.jitTime += dvmGetRelativeTimeUsec() - startTime; +#endif + dvmLockMutex(&gDvmJit.compilerLock); + } while (workQueueLength() != 0); + } + } + pthread_cond_signal(&gDvmJit.compilerQueueEmpty); + dvmUnlockMutex(&gDvmJit.compilerLock); + + /* + * As part of detaching the thread we need to call into Java code to update + * the ThreadGroup, and we should not be in VMWAIT state while executing + * interpreted code. + */ + dvmChangeStatus(NULL, THREAD_RUNNING); + + if (gDvm.verboseShutdown) + LOGD("Compiler thread shutting down\n"); + return NULL; +} + +bool dvmCompilerStartup(void) +{ + + dvmInitMutex(&gDvmJit.compilerLock); + dvmInitMutex(&gDvmJit.compilerICPatchLock); + dvmInitMutex(&gDvmJit.codeCacheProtectionLock); + dvmLockMutex(&gDvmJit.compilerLock); + pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL); + pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL); + + /* Reset the work queue */ + gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0; + gDvmJit.compilerQueueLength = 0; + dvmUnlockMutex(&gDvmJit.compilerLock); + + /* + * Defer rest of 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. + */ + return dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler", + compilerThreadStart, NULL); +} + +void dvmCompilerShutdown(void) +{ + void *threadReturn; + + /* Disable new translation requests */ + gDvmJit.pProfTable = NULL; + gDvmJit.pProfTableCopy = NULL; + + if (gDvm.verboseShutdown) { + dvmCompilerDumpStats(); + while (gDvmJit.compilerQueueLength) + sleep(5); + } + + if (gDvmJit.compilerHandle) { + + gDvmJit.haltCompilerThread = true; + + dvmLockMutex(&gDvmJit.compilerLock); + pthread_cond_signal(&gDvmJit.compilerQueueActivity); + dvmUnlockMutex(&gDvmJit.compilerLock); + + if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0) + LOGW("Compiler thread join failed\n"); + else if (gDvm.verboseShutdown) + LOGD("Compiler thread has shut down\n"); + } + + /* Break loops within the translation cache */ + dvmJitUnchainAll(); + + /* + * NOTE: our current implementatation doesn't allow for the compiler + * thread to be restarted after it exits here. We aren't freeing + * the JitTable or the ProfTable because threads which still may be + * running or in the process of shutting down may hold references to + * them. + */ +} + +void dvmCompilerStateRefresh() +{ + bool jitActive; + bool jitActivate; + bool needUnchain = false; + + /* + * The tableLock might not be initialized yet by the compiler thread if + * debugger is attached from the very beginning of the VM launch. If + * pProfTableCopy is NULL, the lock is not initialized yet and we don't + * need to refresh anything either. + */ + if (gDvmJit.pProfTableCopy == NULL) { + return; + } + + dvmLockMutex(&gDvmJit.tableLock); + jitActive = gDvmJit.pProfTable != NULL; + jitActivate = !(gDvm.debuggerActive || (gDvm.activeProfilers > 0)); + + if (jitActivate && !jitActive) { + gDvmJit.pProfTable = gDvmJit.pProfTableCopy; + } else if (!jitActivate && jitActive) { + gDvmJit.pProfTable = NULL; + needUnchain = true; + } + dvmUnlockMutex(&gDvmJit.tableLock); + if (needUnchain) + dvmJitUnchainAll(); +} |