blob: 3886cceb391e7d5abfd9043b7d497968525f2ff5 [file] [log] [blame]
Ben Chengba4fc8b2009-06-01 13:00:29 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include <sys/mman.h>
18#include <errno.h>
19
20#include "Dalvik.h"
21#include "interp/Jit.h"
22#include "CompilerInternals.h"
23
Ben Chengba4fc8b2009-06-01 13:00:29 -070024static inline bool workQueueLength(void)
25{
26 return gDvmJit.compilerQueueLength;
27}
28
29static CompilerWorkOrder workDequeue(void)
30{
31 assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind
32 != kWorkOrderInvalid);
33 CompilerWorkOrder work =
34 gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex];
35 gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind =
36 kWorkOrderInvalid;
37 if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) {
38 gDvmJit.compilerWorkDequeueIndex = 0;
39 }
40 gDvmJit.compilerQueueLength--;
Bill Buzbeef9f33282009-11-22 12:45:30 -080041 if (gDvmJit.compilerQueueLength == 0) {
42 int cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
43 }
Ben Chengba4fc8b2009-06-01 13:00:29 -070044
45 /* Remember the high water mark of the queue length */
46 if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued)
47 gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength;
48
49 return work;
50}
51
52bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
53{
54 int cc;
55 int i;
56 int numWork;
Ben Cheng60c24f42010-01-04 12:29:56 -080057 int oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
58 bool result = true;
Ben Chengba4fc8b2009-06-01 13:00:29 -070059
60 dvmLockMutex(&gDvmJit.compilerLock);
61
Ben Cheng7a0bcd02010-01-22 16:45:45 -080062 /*
63 * Return if queue is full.
64 * If the code cache is full, we will allow the work order to be added and
65 * we use that to trigger code cache reset.
66 */
67 if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE) {
Ben Cheng60c24f42010-01-04 12:29:56 -080068 result = false;
69 goto done;
Ben Chengba4fc8b2009-06-01 13:00:29 -070070 }
71
72 for (numWork = gDvmJit.compilerQueueLength,
73 i = gDvmJit.compilerWorkDequeueIndex;
74 numWork > 0;
75 numWork--) {
76 /* Already enqueued */
77 if (gDvmJit.compilerWorkQueue[i++].pc == pc)
78 goto done;
79 /* Wrap around */
80 if (i == COMPILER_WORK_QUEUE_SIZE)
81 i = 0;
82 }
83
Ben Chengccd6c012009-10-15 14:52:45 -070084 CompilerWorkOrder *newOrder =
85 &gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex];
86 newOrder->pc = pc;
87 newOrder->kind = kind;
88 newOrder->info = info;
89 newOrder->result.codeAddress = NULL;
90 newOrder->result.discardResult =
Ben Cheng60c24f42010-01-04 12:29:56 -080091 (kind == kWorkOrderTraceDebug || kind == kWorkOrderICPatch) ?
92 true : false;
Ben Cheng33672452010-01-12 14:59:30 -080093 newOrder->result.requestingThread = dvmThreadSelf();
94
Ben Chengba4fc8b2009-06-01 13:00:29 -070095 gDvmJit.compilerWorkEnqueueIndex++;
96 if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE)
97 gDvmJit.compilerWorkEnqueueIndex = 0;
98 gDvmJit.compilerQueueLength++;
99 cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
100 assert(cc == 0);
101
102done:
103 dvmUnlockMutex(&gDvmJit.compilerLock);
Ben Cheng60c24f42010-01-04 12:29:56 -0800104 dvmChangeStatus(NULL, oldStatus);
105 return result;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700106}
107
108/* Block until queue length is 0 */
109void dvmCompilerDrainQueue(void)
110{
Bill Buzbeed7269912009-11-10 14:31:32 -0800111 int oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700112 dvmLockMutex(&gDvmJit.compilerLock);
113 while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread) {
114 pthread_cond_wait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock);
115 }
116 dvmUnlockMutex(&gDvmJit.compilerLock);
Bill Buzbeed7269912009-11-10 14:31:32 -0800117 dvmChangeStatus(NULL, oldStatus);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700118}
119
Ben Cheng60c24f42010-01-04 12:29:56 -0800120bool dvmCompilerSetupCodeCache(void)
121{
122 extern void dvmCompilerTemplateStart(void);
123 extern void dmvCompilerTemplateEnd(void);
124
125 /* Allocate the code cache */
126 gDvmJit.codeCache = mmap(0, CODE_CACHE_SIZE,
127 PROT_READ | PROT_WRITE | PROT_EXEC,
128 MAP_PRIVATE | MAP_ANON, -1, 0);
129 if (gDvmJit.codeCache == MAP_FAILED) {
130 LOGE("Failed to create the code cache: %s\n", strerror(errno));
131 return false;
132 }
133
Ben Cheng7a0bcd02010-01-22 16:45:45 -0800134 // For debugging only
135 // LOGD("Code cache starts at %p", gDvmJit.codeCache);
136
Ben Cheng60c24f42010-01-04 12:29:56 -0800137 /* Copy the template code into the beginning of the code cache */
138 int templateSize = (intptr_t) dmvCompilerTemplateEnd -
139 (intptr_t) dvmCompilerTemplateStart;
140 memcpy((void *) gDvmJit.codeCache,
141 (void *) dvmCompilerTemplateStart,
142 templateSize);
143
144 gDvmJit.templateSize = templateSize;
145 gDvmJit.codeCacheByteUsed = templateSize;
146
147 /* Only flush the part in the code cache that is being used now */
148 cacheflush((intptr_t) gDvmJit.codeCache,
149 (intptr_t) gDvmJit.codeCache + templateSize, 0);
150 return true;
151}
152
Ben Cheng7a0bcd02010-01-22 16:45:45 -0800153static void crawlDalvikStack(Thread *thread, bool print)
154{
155 void *fp = thread->curFrame;
156 StackSaveArea* saveArea = NULL;
157 int stackLevel = 0;
158
159 if (print) {
160 LOGD("Crawling tid %d (%s / %p %s)", thread->systemTid,
161 dvmGetThreadStatusStr(thread->status),
162 thread->inJitCodeCache,
163 thread->inJitCodeCache ? "jit" : "interp");
164 }
165 /* Crawl the Dalvik stack frames to clear the returnAddr field */
166 while (fp != NULL) {
167 saveArea = SAVEAREA_FROM_FP(fp);
168
169 if (print) {
170 if (dvmIsBreakFrame(fp)) {
171 LOGD(" #%d: break frame (%p)",
172 stackLevel, saveArea->returnAddr);
173 }
174 else {
175 LOGD(" #%d: %s.%s%s (%p)",
176 stackLevel,
177 saveArea->method->clazz->descriptor,
178 saveArea->method->name,
179 dvmIsNativeMethod(saveArea->method) ?
180 " (native)" : "",
181 saveArea->returnAddr);
182 }
183 }
184 stackLevel++;
185 saveArea->returnAddr = NULL;
186 assert(fp != saveArea->prevFrame);
187 fp = saveArea->prevFrame;
188 }
189 /* Make sure the stack is fully unwound to the bottom */
190 assert(saveArea == NULL ||
191 (u1 *) (saveArea+1) == thread->interpStackStart);
192}
193
Ben Cheng60c24f42010-01-04 12:29:56 -0800194static void resetCodeCache(void)
195{
Ben Cheng60c24f42010-01-04 12:29:56 -0800196 Thread* thread;
Ben Cheng7a0bcd02010-01-22 16:45:45 -0800197 u8 startTime = dvmGetRelativeTimeUsec();
198 int inJit = 0;
Ben Cheng60c24f42010-01-04 12:29:56 -0800199
Ben Cheng7a0bcd02010-01-22 16:45:45 -0800200 LOGD("Reset the JIT code cache (%d bytes used / %d time(s))",
201 gDvmJit.codeCacheByteUsed, ++gDvmJit.numCodeCacheReset);
Ben Cheng60c24f42010-01-04 12:29:56 -0800202
203 /* Stop the world */
204 dvmSuspendAllThreads(SUSPEND_FOR_CC_RESET);
205
Ben Cheng7a0bcd02010-01-22 16:45:45 -0800206 /* If any thread is found stuck in the JIT state, don't reset the cache */
207 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
208 if (thread->inJitCodeCache) {
209 inJit++;
210 /*
211 * STOPSHIP
212 * Change the verbose mode to false after the new code receives
213 * more QA love.
214 */
215 crawlDalvikStack(thread, true);
216 }
217 }
218
219 if (inJit) {
220 /* Wait a while for the busy threads to rest and try again */
221 gDvmJit.delayCodeCacheReset = 256;
222 goto done;
223 }
224
225 /* Drain the work queue to free the work order */
226 while (workQueueLength()) {
227 CompilerWorkOrder work = workDequeue();
228 free(work.info);
229 }
230
Ben Cheng60c24f42010-01-04 12:29:56 -0800231 /* Wipe out the returnAddr field that soon will point to stale code */
232 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
Ben Cheng7a0bcd02010-01-22 16:45:45 -0800233 crawlDalvikStack(thread, false);
Ben Cheng60c24f42010-01-04 12:29:56 -0800234 }
235
236 /* Reset the JitEntry table contents to the initial unpopulated state */
237 dvmJitResetTable();
238
Ben Cheng60c24f42010-01-04 12:29:56 -0800239 /*
Ben Cheng60c24f42010-01-04 12:29:56 -0800240 * Wipe out the code cache content to force immediate crashes if
241 * stale JIT'ed code is invoked.
242 */
Ben Cheng7a0bcd02010-01-22 16:45:45 -0800243 memset((char *) gDvmJit.codeCache + gDvmJit.templateSize,
244 0,
245 gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
Ben Cheng60c24f42010-01-04 12:29:56 -0800246 cacheflush((intptr_t) gDvmJit.codeCache,
247 (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed, 0);
Ben Cheng60c24f42010-01-04 12:29:56 -0800248
249 /* Reset the current mark of used bytes to the end of template code */
250 gDvmJit.codeCacheByteUsed = gDvmJit.templateSize;
251 gDvmJit.numCompilations = 0;
252
253 /* Reset the work queue */
254 memset(gDvmJit.compilerWorkQueue, 0,
255 sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
256 gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
257 gDvmJit.compilerQueueLength = 0;
258
259 /* All clear now */
260 gDvmJit.codeCacheFull = false;
261
Ben Cheng7a0bcd02010-01-22 16:45:45 -0800262 LOGD("Code cache reset takes %lld usec",
263 dvmGetRelativeTimeUsec() - startTime);
264
265done:
Ben Cheng60c24f42010-01-04 12:29:56 -0800266 /* Resume all threads */
267 dvmResumeAllThreads(SUSPEND_FOR_CC_RESET);
268}
269
Ben Chengba4fc8b2009-06-01 13:00:29 -0700270static void *compilerThreadStart(void *arg)
271{
Ben Cheng5ccdf0b2009-10-08 16:09:49 -0700272 dvmChangeStatus(NULL, THREAD_VMWAIT);
273
Bill Buzbeeb1d80442009-12-17 14:55:21 -0800274 /*
275 * Wait a little before recieving translation requests on the assumption
276 * that process start-up code isn't worth compiling. The trace
277 * selector won't attempt to request a translation if the queue is
278 * filled, so we'll prevent by keeping the high water mark at zero
279 * for a shore time.
280 */
281 assert(gDvmJit.compilerHighWater == 0);
282 usleep(1000);
283 gDvmJit.compilerHighWater =
284 COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
285
Ben Chengba4fc8b2009-06-01 13:00:29 -0700286 dvmLockMutex(&gDvmJit.compilerLock);
287 /*
288 * Since the compiler thread will not touch any objects on the heap once
289 * being created, we just fake its state as VMWAIT so that it can be a
290 * bit late when there is suspend request pending.
291 */
Ben Chengba4fc8b2009-06-01 13:00:29 -0700292 while (!gDvmJit.haltCompilerThread) {
293 if (workQueueLength() == 0) {
294 int cc;
295 cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
296 assert(cc == 0);
297 pthread_cond_wait(&gDvmJit.compilerQueueActivity,
298 &gDvmJit.compilerLock);
299 continue;
300 } else {
301 do {
Ben Chengba4fc8b2009-06-01 13:00:29 -0700302 CompilerWorkOrder work = workDequeue();
303 dvmUnlockMutex(&gDvmJit.compilerLock);
304 /* Check whether there is a suspend request on me */
305 dvmCheckSuspendPending(NULL);
Bill Buzbee27176222009-06-09 09:20:16 -0700306 /* Is JitTable filling up? */
307 if (gDvmJit.jitTableEntriesUsed >
308 (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
309 dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
310 }
Ben Chengba4fc8b2009-06-01 13:00:29 -0700311 if (gDvmJit.haltCompilerThread) {
312 LOGD("Compiler shutdown in progress - discarding request");
313 } else {
Bill Buzbeed7269912009-11-10 14:31:32 -0800314 /* If compilation failed, use interpret-template */
315 if (!dvmCompilerDoWork(&work)) {
316 work.result.codeAddress = gDvmJit.interpretTemplate;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700317 }
Ben Cheng60c24f42010-01-04 12:29:56 -0800318 if (!work.result.discardResult) {
319 dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
320 work.result.instructionSet);
321 }
Ben Chengba4fc8b2009-06-01 13:00:29 -0700322 }
323 free(work.info);
324 dvmLockMutex(&gDvmJit.compilerLock);
Ben Cheng60c24f42010-01-04 12:29:56 -0800325
Ben Cheng49ca7862010-01-20 12:03:51 -0800326 /*
327 * FIXME - temporarily disable code cache reset until
328 * stale code stops leaking.
329 */
330#if 0
Ben Cheng60c24f42010-01-04 12:29:56 -0800331 if (gDvmJit.codeCacheFull == true) {
Ben Cheng7a0bcd02010-01-22 16:45:45 -0800332 if (gDvmJit.delayCodeCacheReset == 0) {
333 resetCodeCache();
334 assert(workQueueLength() == 0 ||
335 gDvmJit.delayCodeCacheReset != 0);
336 } else {
337 LOGD("Delay the next %d tries to reset code cache",
338 gDvmJit.delayCodeCacheReset);
339 gDvmJit.delayCodeCacheReset--;
340 }
Ben Cheng60c24f42010-01-04 12:29:56 -0800341 }
Ben Cheng49ca7862010-01-20 12:03:51 -0800342#endif
Ben Chengba4fc8b2009-06-01 13:00:29 -0700343 } while (workQueueLength() != 0);
344 }
345 }
346 pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
347 dvmUnlockMutex(&gDvmJit.compilerLock);
Ben Chengef00a852009-06-22 22:53:35 -0700348
Ben Cheng5ccdf0b2009-10-08 16:09:49 -0700349 /*
350 * As part of detaching the thread we need to call into Java code to update
351 * the ThreadGroup, and we should not be in VMWAIT state while executing
352 * interpreted code.
353 */
354 dvmChangeStatus(NULL, THREAD_RUNNING);
355
Ben Chengef00a852009-06-22 22:53:35 -0700356 LOGD("Compiler thread shutting down\n");
Ben Chengba4fc8b2009-06-01 13:00:29 -0700357 return NULL;
358}
359
Ben Chengba4fc8b2009-06-01 13:00:29 -0700360bool dvmCompilerStartup(void)
361{
362 /* Make sure the BBType enum is in sane state */
Bill Buzbee1465db52009-09-23 17:17:35 -0700363 assert(kChainingCellNormal == 0);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700364
365 /* Architecture-specific chores to initialize */
366 if (!dvmCompilerArchInit())
367 goto fail;
368
369 /*
370 * Setup the code cache if it is not done so already. For apps it should be
371 * done by the Zygote already, but for command-line dalvikvm invocation we
372 * need to do it here.
373 */
374 if (gDvmJit.codeCache == NULL) {
375 if (!dvmCompilerSetupCodeCache())
376 goto fail;
377 }
378
379 /* Allocate the initial arena block */
380 if (dvmCompilerHeapInit() == false) {
381 goto fail;
382 }
383
384 dvmInitMutex(&gDvmJit.compilerLock);
385 pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
386 pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
387
388 dvmLockMutex(&gDvmJit.compilerLock);
389
390 gDvmJit.haltCompilerThread = false;
391
392 /* Reset the work queue */
393 memset(gDvmJit.compilerWorkQueue, 0,
394 sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
395 gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
396 gDvmJit.compilerQueueLength = 0;
Bill Buzbeeb1d80442009-12-17 14:55:21 -0800397 /* Block new entries via HighWater until compiler thread is ready */
398 gDvmJit.compilerHighWater = 0;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700399
400 assert(gDvmJit.compilerHighWater < COMPILER_WORK_QUEUE_SIZE);
401 if (!dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
402 compilerThreadStart, NULL)) {
403 dvmUnlockMutex(&gDvmJit.compilerLock);
404 goto fail;
405 }
406
Ben Cheng8b258bf2009-06-24 17:27:07 -0700407 /* Track method-level compilation statistics */
408 gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
409
Ben Chengba4fc8b2009-06-01 13:00:29 -0700410 dvmUnlockMutex(&gDvmJit.compilerLock);
411
412 return true;
413
414fail:
415 return false;
416}
417
418void dvmCompilerShutdown(void)
419{
420 void *threadReturn;
421
422 if (gDvmJit.compilerHandle) {
423
424 gDvmJit.haltCompilerThread = true;
425
426 dvmLockMutex(&gDvmJit.compilerLock);
427 pthread_cond_signal(&gDvmJit.compilerQueueActivity);
428 dvmUnlockMutex(&gDvmJit.compilerLock);
429
Ben Chengef00a852009-06-22 22:53:35 -0700430 if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
431 LOGW("Compiler thread join failed\n");
432 else
433 LOGD("Compiler thread has shut down\n");
Ben Chengba4fc8b2009-06-01 13:00:29 -0700434 }
435}