blob: bf44467e116996646c98e3eecc6d628c50e508f9 [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
62 /* Queue full */
63 if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE ||
64 gDvmJit.codeCacheFull == true) {
Ben Cheng60c24f42010-01-04 12:29:56 -080065 result = false;
66 goto done;
Ben Chengba4fc8b2009-06-01 13:00:29 -070067 }
68
69 for (numWork = gDvmJit.compilerQueueLength,
70 i = gDvmJit.compilerWorkDequeueIndex;
71 numWork > 0;
72 numWork--) {
73 /* Already enqueued */
74 if (gDvmJit.compilerWorkQueue[i++].pc == pc)
75 goto done;
76 /* Wrap around */
77 if (i == COMPILER_WORK_QUEUE_SIZE)
78 i = 0;
79 }
80
Ben Chengccd6c012009-10-15 14:52:45 -070081 CompilerWorkOrder *newOrder =
82 &gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex];
83 newOrder->pc = pc;
84 newOrder->kind = kind;
85 newOrder->info = info;
86 newOrder->result.codeAddress = NULL;
87 newOrder->result.discardResult =
Ben Cheng60c24f42010-01-04 12:29:56 -080088 (kind == kWorkOrderTraceDebug || kind == kWorkOrderICPatch) ?
89 true : false;
Ben Cheng33672452010-01-12 14:59:30 -080090 newOrder->result.requestingThread = dvmThreadSelf();
91
Ben Chengba4fc8b2009-06-01 13:00:29 -070092 gDvmJit.compilerWorkEnqueueIndex++;
93 if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE)
94 gDvmJit.compilerWorkEnqueueIndex = 0;
95 gDvmJit.compilerQueueLength++;
96 cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
97 assert(cc == 0);
98
99done:
100 dvmUnlockMutex(&gDvmJit.compilerLock);
Ben Cheng60c24f42010-01-04 12:29:56 -0800101 dvmChangeStatus(NULL, oldStatus);
102 return result;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700103}
104
105/* Block until queue length is 0 */
106void dvmCompilerDrainQueue(void)
107{
Bill Buzbeed7269912009-11-10 14:31:32 -0800108 int oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700109 dvmLockMutex(&gDvmJit.compilerLock);
110 while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread) {
111 pthread_cond_wait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock);
112 }
113 dvmUnlockMutex(&gDvmJit.compilerLock);
Bill Buzbeed7269912009-11-10 14:31:32 -0800114 dvmChangeStatus(NULL, oldStatus);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700115}
116
Ben Cheng60c24f42010-01-04 12:29:56 -0800117bool dvmCompilerSetupCodeCache(void)
118{
119 extern void dvmCompilerTemplateStart(void);
120 extern void dmvCompilerTemplateEnd(void);
121
122 /* Allocate the code cache */
123 gDvmJit.codeCache = mmap(0, CODE_CACHE_SIZE,
124 PROT_READ | PROT_WRITE | PROT_EXEC,
125 MAP_PRIVATE | MAP_ANON, -1, 0);
126 if (gDvmJit.codeCache == MAP_FAILED) {
127 LOGE("Failed to create the code cache: %s\n", strerror(errno));
128 return false;
129 }
130
131 /* Copy the template code into the beginning of the code cache */
132 int templateSize = (intptr_t) dmvCompilerTemplateEnd -
133 (intptr_t) dvmCompilerTemplateStart;
134 memcpy((void *) gDvmJit.codeCache,
135 (void *) dvmCompilerTemplateStart,
136 templateSize);
137
138 gDvmJit.templateSize = templateSize;
139 gDvmJit.codeCacheByteUsed = templateSize;
140
141 /* Only flush the part in the code cache that is being used now */
142 cacheflush((intptr_t) gDvmJit.codeCache,
143 (intptr_t) gDvmJit.codeCache + templateSize, 0);
144 return true;
145}
146
147static void resetCodeCache(void)
148{
149 Thread* self = dvmThreadSelf();
150 Thread* thread;
151
152 LOGD("Reset the JIT code cache (%d bytes used)", gDvmJit.codeCacheByteUsed);
153
154 /* Stop the world */
155 dvmSuspendAllThreads(SUSPEND_FOR_CC_RESET);
156
157 /* Wipe out the returnAddr field that soon will point to stale code */
158 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
159 if (thread == self)
160 continue;
161
162 /* Crawl the Dalvik stack frames */
163 StackSaveArea *ssaPtr = ((StackSaveArea *) thread->curFrame) - 1;
164 while (ssaPtr != ((StackSaveArea *) NULL) - 1) {
165 ssaPtr->returnAddr = NULL;
166 ssaPtr = ((StackSaveArea *) ssaPtr->prevFrame) - 1;
167 };
168 }
169
170 /* Reset the JitEntry table contents to the initial unpopulated state */
171 dvmJitResetTable();
172
173#if 0
174 /*
175 * Uncomment the following code when testing/debugging.
176 *
177 * Wipe out the code cache content to force immediate crashes if
178 * stale JIT'ed code is invoked.
179 */
180 memset(gDvmJit.codeCache,
181 (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed,
182 0);
183 cacheflush((intptr_t) gDvmJit.codeCache,
184 (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed, 0);
185#endif
186
187 /* Reset the current mark of used bytes to the end of template code */
188 gDvmJit.codeCacheByteUsed = gDvmJit.templateSize;
189 gDvmJit.numCompilations = 0;
190
191 /* Reset the work queue */
192 memset(gDvmJit.compilerWorkQueue, 0,
193 sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
194 gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
195 gDvmJit.compilerQueueLength = 0;
196
197 /* All clear now */
198 gDvmJit.codeCacheFull = false;
199
200 /* Resume all threads */
201 dvmResumeAllThreads(SUSPEND_FOR_CC_RESET);
202}
203
Ben Chengba4fc8b2009-06-01 13:00:29 -0700204static void *compilerThreadStart(void *arg)
205{
Ben Cheng5ccdf0b2009-10-08 16:09:49 -0700206 dvmChangeStatus(NULL, THREAD_VMWAIT);
207
Bill Buzbeeb1d80442009-12-17 14:55:21 -0800208 /*
209 * Wait a little before recieving translation requests on the assumption
210 * that process start-up code isn't worth compiling. The trace
211 * selector won't attempt to request a translation if the queue is
212 * filled, so we'll prevent by keeping the high water mark at zero
213 * for a shore time.
214 */
215 assert(gDvmJit.compilerHighWater == 0);
216 usleep(1000);
217 gDvmJit.compilerHighWater =
218 COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
219
Ben Chengba4fc8b2009-06-01 13:00:29 -0700220 dvmLockMutex(&gDvmJit.compilerLock);
221 /*
222 * Since the compiler thread will not touch any objects on the heap once
223 * being created, we just fake its state as VMWAIT so that it can be a
224 * bit late when there is suspend request pending.
225 */
Ben Chengba4fc8b2009-06-01 13:00:29 -0700226 while (!gDvmJit.haltCompilerThread) {
227 if (workQueueLength() == 0) {
228 int cc;
229 cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
230 assert(cc == 0);
231 pthread_cond_wait(&gDvmJit.compilerQueueActivity,
232 &gDvmJit.compilerLock);
233 continue;
234 } else {
235 do {
Ben Chengba4fc8b2009-06-01 13:00:29 -0700236 CompilerWorkOrder work = workDequeue();
237 dvmUnlockMutex(&gDvmJit.compilerLock);
238 /* Check whether there is a suspend request on me */
239 dvmCheckSuspendPending(NULL);
Bill Buzbee27176222009-06-09 09:20:16 -0700240 /* Is JitTable filling up? */
241 if (gDvmJit.jitTableEntriesUsed >
242 (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
243 dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
244 }
Ben Chengba4fc8b2009-06-01 13:00:29 -0700245 if (gDvmJit.haltCompilerThread) {
246 LOGD("Compiler shutdown in progress - discarding request");
247 } else {
Bill Buzbeed7269912009-11-10 14:31:32 -0800248 /* If compilation failed, use interpret-template */
249 if (!dvmCompilerDoWork(&work)) {
250 work.result.codeAddress = gDvmJit.interpretTemplate;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700251 }
Ben Cheng60c24f42010-01-04 12:29:56 -0800252 if (!work.result.discardResult) {
253 dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
254 work.result.instructionSet);
255 }
Ben Chengba4fc8b2009-06-01 13:00:29 -0700256 }
257 free(work.info);
258 dvmLockMutex(&gDvmJit.compilerLock);
Ben Cheng60c24f42010-01-04 12:29:56 -0800259
Ben Cheng49ca7862010-01-20 12:03:51 -0800260 /*
261 * FIXME - temporarily disable code cache reset until
262 * stale code stops leaking.
263 */
264#if 0
Ben Cheng60c24f42010-01-04 12:29:56 -0800265 if (gDvmJit.codeCacheFull == true) {
266 resetCodeCache();
267 }
Ben Cheng49ca7862010-01-20 12:03:51 -0800268#endif
Ben Chengba4fc8b2009-06-01 13:00:29 -0700269 } while (workQueueLength() != 0);
270 }
271 }
272 pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
273 dvmUnlockMutex(&gDvmJit.compilerLock);
Ben Chengef00a852009-06-22 22:53:35 -0700274
Ben Cheng5ccdf0b2009-10-08 16:09:49 -0700275 /*
276 * As part of detaching the thread we need to call into Java code to update
277 * the ThreadGroup, and we should not be in VMWAIT state while executing
278 * interpreted code.
279 */
280 dvmChangeStatus(NULL, THREAD_RUNNING);
281
Ben Chengef00a852009-06-22 22:53:35 -0700282 LOGD("Compiler thread shutting down\n");
Ben Chengba4fc8b2009-06-01 13:00:29 -0700283 return NULL;
284}
285
Ben Chengba4fc8b2009-06-01 13:00:29 -0700286bool dvmCompilerStartup(void)
287{
288 /* Make sure the BBType enum is in sane state */
Bill Buzbee1465db52009-09-23 17:17:35 -0700289 assert(kChainingCellNormal == 0);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700290
291 /* Architecture-specific chores to initialize */
292 if (!dvmCompilerArchInit())
293 goto fail;
294
295 /*
296 * Setup the code cache if it is not done so already. For apps it should be
297 * done by the Zygote already, but for command-line dalvikvm invocation we
298 * need to do it here.
299 */
300 if (gDvmJit.codeCache == NULL) {
301 if (!dvmCompilerSetupCodeCache())
302 goto fail;
303 }
304
305 /* Allocate the initial arena block */
306 if (dvmCompilerHeapInit() == false) {
307 goto fail;
308 }
309
310 dvmInitMutex(&gDvmJit.compilerLock);
311 pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
312 pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
313
314 dvmLockMutex(&gDvmJit.compilerLock);
315
316 gDvmJit.haltCompilerThread = false;
317
318 /* Reset the work queue */
319 memset(gDvmJit.compilerWorkQueue, 0,
320 sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
321 gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
322 gDvmJit.compilerQueueLength = 0;
Bill Buzbeeb1d80442009-12-17 14:55:21 -0800323 /* Block new entries via HighWater until compiler thread is ready */
324 gDvmJit.compilerHighWater = 0;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700325
326 assert(gDvmJit.compilerHighWater < COMPILER_WORK_QUEUE_SIZE);
327 if (!dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
328 compilerThreadStart, NULL)) {
329 dvmUnlockMutex(&gDvmJit.compilerLock);
330 goto fail;
331 }
332
Ben Cheng8b258bf2009-06-24 17:27:07 -0700333 /* Track method-level compilation statistics */
334 gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
335
Ben Chengba4fc8b2009-06-01 13:00:29 -0700336 dvmUnlockMutex(&gDvmJit.compilerLock);
337
338 return true;
339
340fail:
341 return false;
342}
343
344void dvmCompilerShutdown(void)
345{
346 void *threadReturn;
347
348 if (gDvmJit.compilerHandle) {
349
350 gDvmJit.haltCompilerThread = true;
351
352 dvmLockMutex(&gDvmJit.compilerLock);
353 pthread_cond_signal(&gDvmJit.compilerQueueActivity);
354 dvmUnlockMutex(&gDvmJit.compilerLock);
355
Ben Chengef00a852009-06-22 22:53:35 -0700356 if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
357 LOGW("Compiler thread join failed\n");
358 else
359 LOGD("Compiler thread has shut down\n");
Ben Chengba4fc8b2009-06-01 13:00:29 -0700360 }
361}