2 * Copyright (C) 2009 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 #include "interp/Jit.h"
22 #include "CompilerInternals.h"
24 static inline bool workQueueLength(void)
26 return gDvmJit.compilerQueueLength;
29 static CompilerWorkOrder workDequeue(void)
31 assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind
32 != kWorkOrderInvalid);
33 CompilerWorkOrder work =
34 gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex];
35 gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind =
37 if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) {
38 gDvmJit.compilerWorkDequeueIndex = 0;
40 gDvmJit.compilerQueueLength--;
41 if (gDvmJit.compilerQueueLength == 0) {
42 int cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
45 /* Remember the high water mark of the queue length */
46 if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued)
47 gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength;
52 bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
57 int oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
60 dvmLockMutex(&gDvmJit.compilerLock);
63 if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE ||
64 gDvmJit.codeCacheFull == true) {
69 for (numWork = gDvmJit.compilerQueueLength,
70 i = gDvmJit.compilerWorkDequeueIndex;
73 /* Already enqueued */
74 if (gDvmJit.compilerWorkQueue[i++].pc == pc)
77 if (i == COMPILER_WORK_QUEUE_SIZE)
81 CompilerWorkOrder *newOrder =
82 &gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex];
84 newOrder->kind = kind;
85 newOrder->info = info;
86 newOrder->result.codeAddress = NULL;
87 newOrder->result.discardResult =
88 (kind == kWorkOrderTraceDebug || kind == kWorkOrderICPatch) ?
90 gDvmJit.compilerWorkEnqueueIndex++;
91 if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE)
92 gDvmJit.compilerWorkEnqueueIndex = 0;
93 gDvmJit.compilerQueueLength++;
94 cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
98 dvmUnlockMutex(&gDvmJit.compilerLock);
99 dvmChangeStatus(NULL, oldStatus);
103 /* Block until queue length is 0 */
104 void dvmCompilerDrainQueue(void)
106 int oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
107 dvmLockMutex(&gDvmJit.compilerLock);
108 while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread) {
109 pthread_cond_wait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock);
111 dvmUnlockMutex(&gDvmJit.compilerLock);
112 dvmChangeStatus(NULL, oldStatus);
115 bool dvmCompilerSetupCodeCache(void)
117 extern void dvmCompilerTemplateStart(void);
118 extern void dmvCompilerTemplateEnd(void);
120 /* Allocate the code cache */
121 gDvmJit.codeCache = mmap(0, CODE_CACHE_SIZE,
122 PROT_READ | PROT_WRITE | PROT_EXEC,
123 MAP_PRIVATE | MAP_ANON, -1, 0);
124 if (gDvmJit.codeCache == MAP_FAILED) {
125 LOGE("Failed to create the code cache: %s\n", strerror(errno));
129 /* Copy the template code into the beginning of the code cache */
130 int templateSize = (intptr_t) dmvCompilerTemplateEnd -
131 (intptr_t) dvmCompilerTemplateStart;
132 memcpy((void *) gDvmJit.codeCache,
133 (void *) dvmCompilerTemplateStart,
136 gDvmJit.templateSize = templateSize;
137 gDvmJit.codeCacheByteUsed = templateSize;
139 /* Only flush the part in the code cache that is being used now */
140 cacheflush((intptr_t) gDvmJit.codeCache,
141 (intptr_t) gDvmJit.codeCache + templateSize, 0);
145 static void resetCodeCache(void)
147 Thread* self = dvmThreadSelf();
150 LOGD("Reset the JIT code cache (%d bytes used)", gDvmJit.codeCacheByteUsed);
153 dvmSuspendAllThreads(SUSPEND_FOR_CC_RESET);
155 /* Wipe out the returnAddr field that soon will point to stale code */
156 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
160 /* Crawl the Dalvik stack frames */
161 StackSaveArea *ssaPtr = ((StackSaveArea *) thread->curFrame) - 1;
162 while (ssaPtr != ((StackSaveArea *) NULL) - 1) {
163 ssaPtr->returnAddr = NULL;
164 ssaPtr = ((StackSaveArea *) ssaPtr->prevFrame) - 1;
168 /* Reset the JitEntry table contents to the initial unpopulated state */
173 * Uncomment the following code when testing/debugging.
175 * Wipe out the code cache content to force immediate crashes if
176 * stale JIT'ed code is invoked.
178 memset(gDvmJit.codeCache,
179 (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed,
181 cacheflush((intptr_t) gDvmJit.codeCache,
182 (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed, 0);
185 /* Reset the current mark of used bytes to the end of template code */
186 gDvmJit.codeCacheByteUsed = gDvmJit.templateSize;
187 gDvmJit.numCompilations = 0;
189 /* Reset the work queue */
190 memset(gDvmJit.compilerWorkQueue, 0,
191 sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
192 gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
193 gDvmJit.compilerQueueLength = 0;
196 gDvmJit.codeCacheFull = false;
198 /* Resume all threads */
199 dvmResumeAllThreads(SUSPEND_FOR_CC_RESET);
202 static void *compilerThreadStart(void *arg)
204 dvmChangeStatus(NULL, THREAD_VMWAIT);
207 * Wait a little before recieving translation requests on the assumption
208 * that process start-up code isn't worth compiling. The trace
209 * selector won't attempt to request a translation if the queue is
210 * filled, so we'll prevent by keeping the high water mark at zero
213 assert(gDvmJit.compilerHighWater == 0);
215 gDvmJit.compilerHighWater =
216 COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
218 dvmLockMutex(&gDvmJit.compilerLock);
220 * Since the compiler thread will not touch any objects on the heap once
221 * being created, we just fake its state as VMWAIT so that it can be a
222 * bit late when there is suspend request pending.
224 while (!gDvmJit.haltCompilerThread) {
225 if (workQueueLength() == 0) {
227 cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
229 pthread_cond_wait(&gDvmJit.compilerQueueActivity,
230 &gDvmJit.compilerLock);
234 CompilerWorkOrder work = workDequeue();
235 dvmUnlockMutex(&gDvmJit.compilerLock);
236 /* Check whether there is a suspend request on me */
237 dvmCheckSuspendPending(NULL);
238 /* Is JitTable filling up? */
239 if (gDvmJit.jitTableEntriesUsed >
240 (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
241 dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
243 if (gDvmJit.haltCompilerThread) {
244 LOGD("Compiler shutdown in progress - discarding request");
246 /* If compilation failed, use interpret-template */
247 if (!dvmCompilerDoWork(&work)) {
248 work.result.codeAddress = gDvmJit.interpretTemplate;
250 if (!work.result.discardResult) {
251 dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
252 work.result.instructionSet);
256 dvmLockMutex(&gDvmJit.compilerLock);
258 if (gDvmJit.codeCacheFull == true) {
261 } while (workQueueLength() != 0);
264 pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
265 dvmUnlockMutex(&gDvmJit.compilerLock);
268 * As part of detaching the thread we need to call into Java code to update
269 * the ThreadGroup, and we should not be in VMWAIT state while executing
272 dvmChangeStatus(NULL, THREAD_RUNNING);
274 LOGD("Compiler thread shutting down\n");
278 bool dvmCompilerStartup(void)
280 /* Make sure the BBType enum is in sane state */
281 assert(kChainingCellNormal == 0);
283 /* Architecture-specific chores to initialize */
284 if (!dvmCompilerArchInit())
288 * Setup the code cache if it is not done so already. For apps it should be
289 * done by the Zygote already, but for command-line dalvikvm invocation we
290 * need to do it here.
292 if (gDvmJit.codeCache == NULL) {
293 if (!dvmCompilerSetupCodeCache())
297 /* Allocate the initial arena block */
298 if (dvmCompilerHeapInit() == false) {
302 dvmInitMutex(&gDvmJit.compilerLock);
303 pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
304 pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
306 dvmLockMutex(&gDvmJit.compilerLock);
308 gDvmJit.haltCompilerThread = false;
310 /* Reset the work queue */
311 memset(gDvmJit.compilerWorkQueue, 0,
312 sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
313 gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
314 gDvmJit.compilerQueueLength = 0;
315 /* Block new entries via HighWater until compiler thread is ready */
316 gDvmJit.compilerHighWater = 0;
318 assert(gDvmJit.compilerHighWater < COMPILER_WORK_QUEUE_SIZE);
319 if (!dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
320 compilerThreadStart, NULL)) {
321 dvmUnlockMutex(&gDvmJit.compilerLock);
325 /* Track method-level compilation statistics */
326 gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
328 dvmUnlockMutex(&gDvmJit.compilerLock);
336 void dvmCompilerShutdown(void)
340 if (gDvmJit.compilerHandle) {
342 gDvmJit.haltCompilerThread = true;
344 dvmLockMutex(&gDvmJit.compilerLock);
345 pthread_cond_signal(&gDvmJit.compilerQueueActivity);
346 dvmUnlockMutex(&gDvmJit.compilerLock);
348 if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
349 LOGW("Compiler thread join failed\n");
351 LOGD("Compiler thread has shut down\n");