blob: e7973e38ea7fcc5b060e5fdaae832d2064d112cd [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
260 if (gDvmJit.codeCacheFull == true) {
261 resetCodeCache();
262 }
Ben Chengba4fc8b2009-06-01 13:00:29 -0700263 } while (workQueueLength() != 0);
264 }
265 }
266 pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
267 dvmUnlockMutex(&gDvmJit.compilerLock);
Ben Chengef00a852009-06-22 22:53:35 -0700268
Ben Cheng5ccdf0b2009-10-08 16:09:49 -0700269 /*
270 * As part of detaching the thread we need to call into Java code to update
271 * the ThreadGroup, and we should not be in VMWAIT state while executing
272 * interpreted code.
273 */
274 dvmChangeStatus(NULL, THREAD_RUNNING);
275
Ben Chengef00a852009-06-22 22:53:35 -0700276 LOGD("Compiler thread shutting down\n");
Ben Chengba4fc8b2009-06-01 13:00:29 -0700277 return NULL;
278}
279
Ben Chengba4fc8b2009-06-01 13:00:29 -0700280bool dvmCompilerStartup(void)
281{
282 /* Make sure the BBType enum is in sane state */
Bill Buzbee1465db52009-09-23 17:17:35 -0700283 assert(kChainingCellNormal == 0);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700284
285 /* Architecture-specific chores to initialize */
286 if (!dvmCompilerArchInit())
287 goto fail;
288
289 /*
290 * Setup the code cache if it is not done so already. For apps it should be
291 * done by the Zygote already, but for command-line dalvikvm invocation we
292 * need to do it here.
293 */
294 if (gDvmJit.codeCache == NULL) {
295 if (!dvmCompilerSetupCodeCache())
296 goto fail;
297 }
298
299 /* Allocate the initial arena block */
300 if (dvmCompilerHeapInit() == false) {
301 goto fail;
302 }
303
304 dvmInitMutex(&gDvmJit.compilerLock);
305 pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
306 pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
307
308 dvmLockMutex(&gDvmJit.compilerLock);
309
310 gDvmJit.haltCompilerThread = false;
311
312 /* Reset the work queue */
313 memset(gDvmJit.compilerWorkQueue, 0,
314 sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
315 gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
316 gDvmJit.compilerQueueLength = 0;
Bill Buzbeeb1d80442009-12-17 14:55:21 -0800317 /* Block new entries via HighWater until compiler thread is ready */
318 gDvmJit.compilerHighWater = 0;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700319
320 assert(gDvmJit.compilerHighWater < COMPILER_WORK_QUEUE_SIZE);
321 if (!dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
322 compilerThreadStart, NULL)) {
323 dvmUnlockMutex(&gDvmJit.compilerLock);
324 goto fail;
325 }
326
Ben Cheng8b258bf2009-06-24 17:27:07 -0700327 /* Track method-level compilation statistics */
328 gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
329
Ben Chengba4fc8b2009-06-01 13:00:29 -0700330 dvmUnlockMutex(&gDvmJit.compilerLock);
331
332 return true;
333
334fail:
335 return false;
336}
337
338void dvmCompilerShutdown(void)
339{
340 void *threadReturn;
341
342 if (gDvmJit.compilerHandle) {
343
344 gDvmJit.haltCompilerThread = true;
345
346 dvmLockMutex(&gDvmJit.compilerLock);
347 pthread_cond_signal(&gDvmJit.compilerQueueActivity);
348 dvmUnlockMutex(&gDvmJit.compilerLock);
349
Ben Chengef00a852009-06-22 22:53:35 -0700350 if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
351 LOGW("Compiler thread join failed\n");
352 else
353 LOGD("Compiler thread has shut down\n");
Ben Chengba4fc8b2009-06-01 13:00:29 -0700354 }
355}