blob: 014013db6e2462791b8f68cdcfb40d3937b151ae [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 Chengba4fc8b2009-06-01 13:00:29 -070090 gDvmJit.compilerWorkEnqueueIndex++;
91 if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE)
92 gDvmJit.compilerWorkEnqueueIndex = 0;
93 gDvmJit.compilerQueueLength++;
94 cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
95 assert(cc == 0);
96
97done:
98 dvmUnlockMutex(&gDvmJit.compilerLock);
Ben Cheng60c24f42010-01-04 12:29:56 -080099 dvmChangeStatus(NULL, oldStatus);
100 return result;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700101}
102
103/* Block until queue length is 0 */
104void dvmCompilerDrainQueue(void)
105{
Bill Buzbeed7269912009-11-10 14:31:32 -0800106 int oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700107 dvmLockMutex(&gDvmJit.compilerLock);
108 while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread) {
109 pthread_cond_wait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock);
110 }
111 dvmUnlockMutex(&gDvmJit.compilerLock);
Bill Buzbeed7269912009-11-10 14:31:32 -0800112 dvmChangeStatus(NULL, oldStatus);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700113}
114
Ben Cheng60c24f42010-01-04 12:29:56 -0800115bool dvmCompilerSetupCodeCache(void)
116{
117 extern void dvmCompilerTemplateStart(void);
118 extern void dmvCompilerTemplateEnd(void);
119
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));
126 return false;
127 }
128
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,
134 templateSize);
135
136 gDvmJit.templateSize = templateSize;
137 gDvmJit.codeCacheByteUsed = templateSize;
138
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);
142 return true;
143}
144
145static void resetCodeCache(void)
146{
147 Thread* self = dvmThreadSelf();
148 Thread* thread;
149
150 LOGD("Reset the JIT code cache (%d bytes used)", gDvmJit.codeCacheByteUsed);
151
152 /* Stop the world */
153 dvmSuspendAllThreads(SUSPEND_FOR_CC_RESET);
154
155 /* Wipe out the returnAddr field that soon will point to stale code */
156 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
157 if (thread == self)
158 continue;
159
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;
165 };
166 }
167
168 /* Reset the JitEntry table contents to the initial unpopulated state */
169 dvmJitResetTable();
170
171#if 0
172 /*
173 * Uncomment the following code when testing/debugging.
174 *
175 * Wipe out the code cache content to force immediate crashes if
176 * stale JIT'ed code is invoked.
177 */
178 memset(gDvmJit.codeCache,
179 (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed,
180 0);
181 cacheflush((intptr_t) gDvmJit.codeCache,
182 (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed, 0);
183#endif
184
185 /* Reset the current mark of used bytes to the end of template code */
186 gDvmJit.codeCacheByteUsed = gDvmJit.templateSize;
187 gDvmJit.numCompilations = 0;
188
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;
194
195 /* All clear now */
196 gDvmJit.codeCacheFull = false;
197
198 /* Resume all threads */
199 dvmResumeAllThreads(SUSPEND_FOR_CC_RESET);
200}
201
Ben Chengba4fc8b2009-06-01 13:00:29 -0700202static void *compilerThreadStart(void *arg)
203{
Ben Cheng5ccdf0b2009-10-08 16:09:49 -0700204 dvmChangeStatus(NULL, THREAD_VMWAIT);
205
Bill Buzbeeb1d80442009-12-17 14:55:21 -0800206 /*
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
211 * for a shore time.
212 */
213 assert(gDvmJit.compilerHighWater == 0);
214 usleep(1000);
215 gDvmJit.compilerHighWater =
216 COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
217
Ben Chengba4fc8b2009-06-01 13:00:29 -0700218 dvmLockMutex(&gDvmJit.compilerLock);
219 /*
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.
223 */
Ben Chengba4fc8b2009-06-01 13:00:29 -0700224 while (!gDvmJit.haltCompilerThread) {
225 if (workQueueLength() == 0) {
226 int cc;
227 cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
228 assert(cc == 0);
229 pthread_cond_wait(&gDvmJit.compilerQueueActivity,
230 &gDvmJit.compilerLock);
231 continue;
232 } else {
233 do {
Ben Chengba4fc8b2009-06-01 13:00:29 -0700234 CompilerWorkOrder work = workDequeue();
235 dvmUnlockMutex(&gDvmJit.compilerLock);
236 /* Check whether there is a suspend request on me */
237 dvmCheckSuspendPending(NULL);
Bill Buzbee27176222009-06-09 09:20:16 -0700238 /* Is JitTable filling up? */
239 if (gDvmJit.jitTableEntriesUsed >
240 (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
241 dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
242 }
Ben Chengba4fc8b2009-06-01 13:00:29 -0700243 if (gDvmJit.haltCompilerThread) {
244 LOGD("Compiler shutdown in progress - discarding request");
245 } else {
Bill Buzbeed7269912009-11-10 14:31:32 -0800246 /* If compilation failed, use interpret-template */
247 if (!dvmCompilerDoWork(&work)) {
248 work.result.codeAddress = gDvmJit.interpretTemplate;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700249 }
Ben Cheng60c24f42010-01-04 12:29:56 -0800250 if (!work.result.discardResult) {
251 dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
252 work.result.instructionSet);
253 }
Ben Chengba4fc8b2009-06-01 13:00:29 -0700254 }
255 free(work.info);
256 dvmLockMutex(&gDvmJit.compilerLock);
Ben Cheng60c24f42010-01-04 12:29:56 -0800257
258 if (gDvmJit.codeCacheFull == true) {
259 resetCodeCache();
260 }
Ben Chengba4fc8b2009-06-01 13:00:29 -0700261 } while (workQueueLength() != 0);
262 }
263 }
264 pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
265 dvmUnlockMutex(&gDvmJit.compilerLock);
Ben Chengef00a852009-06-22 22:53:35 -0700266
Ben Cheng5ccdf0b2009-10-08 16:09:49 -0700267 /*
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
270 * interpreted code.
271 */
272 dvmChangeStatus(NULL, THREAD_RUNNING);
273
Ben Chengef00a852009-06-22 22:53:35 -0700274 LOGD("Compiler thread shutting down\n");
Ben Chengba4fc8b2009-06-01 13:00:29 -0700275 return NULL;
276}
277
Ben Chengba4fc8b2009-06-01 13:00:29 -0700278bool dvmCompilerStartup(void)
279{
280 /* Make sure the BBType enum is in sane state */
Bill Buzbee1465db52009-09-23 17:17:35 -0700281 assert(kChainingCellNormal == 0);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700282
283 /* Architecture-specific chores to initialize */
284 if (!dvmCompilerArchInit())
285 goto fail;
286
287 /*
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.
291 */
292 if (gDvmJit.codeCache == NULL) {
293 if (!dvmCompilerSetupCodeCache())
294 goto fail;
295 }
296
297 /* Allocate the initial arena block */
298 if (dvmCompilerHeapInit() == false) {
299 goto fail;
300 }
301
302 dvmInitMutex(&gDvmJit.compilerLock);
303 pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
304 pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
305
306 dvmLockMutex(&gDvmJit.compilerLock);
307
308 gDvmJit.haltCompilerThread = false;
309
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;
Bill Buzbeeb1d80442009-12-17 14:55:21 -0800315 /* Block new entries via HighWater until compiler thread is ready */
316 gDvmJit.compilerHighWater = 0;
Ben Chengba4fc8b2009-06-01 13:00:29 -0700317
318 assert(gDvmJit.compilerHighWater < COMPILER_WORK_QUEUE_SIZE);
319 if (!dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
320 compilerThreadStart, NULL)) {
321 dvmUnlockMutex(&gDvmJit.compilerLock);
322 goto fail;
323 }
324
Ben Cheng8b258bf2009-06-24 17:27:07 -0700325 /* Track method-level compilation statistics */
326 gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
327
Ben Chengba4fc8b2009-06-01 13:00:29 -0700328 dvmUnlockMutex(&gDvmJit.compilerLock);
329
330 return true;
331
332fail:
333 return false;
334}
335
336void dvmCompilerShutdown(void)
337{
338 void *threadReturn;
339
340 if (gDvmJit.compilerHandle) {
341
342 gDvmJit.haltCompilerThread = true;
343
344 dvmLockMutex(&gDvmJit.compilerLock);
345 pthread_cond_signal(&gDvmJit.compilerQueueActivity);
346 dvmUnlockMutex(&gDvmJit.compilerLock);
347
Ben Chengef00a852009-06-22 22:53:35 -0700348 if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
349 LOGW("Compiler thread join failed\n");
350 else
351 LOGD("Compiler thread has shut down\n");
Ben Chengba4fc8b2009-06-01 13:00:29 -0700352 }
353}