OSDN Git Service

014013db6e2462791b8f68cdcfb40d3937b151ae
[android-x86/dalvik.git] / vm / compiler / Compiler.c
1 /*
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
24 static inline bool workQueueLength(void)
25 {
26     return gDvmJit.compilerQueueLength;
27 }
28
29 static 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--;
41     if (gDvmJit.compilerQueueLength == 0) {
42         int cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
43     }
44
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
52 bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
53 {
54     int cc;
55     int i;
56     int numWork;
57     int oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
58     bool result = true;
59
60     dvmLockMutex(&gDvmJit.compilerLock);
61
62     /* Queue full */
63     if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE ||
64         gDvmJit.codeCacheFull == true) {
65         result = false;
66         goto done;
67     }
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
81     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 =
88         (kind == kWorkOrderTraceDebug || kind == kWorkOrderICPatch) ?
89         true : false;
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);
95     assert(cc == 0);
96
97 done:
98     dvmUnlockMutex(&gDvmJit.compilerLock);
99     dvmChangeStatus(NULL, oldStatus);
100     return result;
101 }
102
103 /* Block until queue length is 0 */
104 void dvmCompilerDrainQueue(void)
105 {
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);
110     }
111     dvmUnlockMutex(&gDvmJit.compilerLock);
112     dvmChangeStatus(NULL, oldStatus);
113 }
114
115 bool 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
145 static 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
202 static void *compilerThreadStart(void *arg)
203 {
204     dvmChangeStatus(NULL, THREAD_VMWAIT);
205
206     /*
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
218     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      */
224     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 {
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);
242                 }
243                 if (gDvmJit.haltCompilerThread) {
244                     LOGD("Compiler shutdown in progress - discarding request");
245                 } else {
246                     /* If compilation failed, use interpret-template */
247                     if (!dvmCompilerDoWork(&work)) {
248                         work.result.codeAddress = gDvmJit.interpretTemplate;
249                     }
250                     if (!work.result.discardResult) {
251                         dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
252                                           work.result.instructionSet);
253                     }
254                 }
255                 free(work.info);
256                 dvmLockMutex(&gDvmJit.compilerLock);
257
258                 if (gDvmJit.codeCacheFull == true) {
259                     resetCodeCache();
260                 }
261             } while (workQueueLength() != 0);
262         }
263     }
264     pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
265     dvmUnlockMutex(&gDvmJit.compilerLock);
266
267     /*
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
274     LOGD("Compiler thread shutting down\n");
275     return NULL;
276 }
277
278 bool dvmCompilerStartup(void)
279 {
280     /* Make sure the BBType enum is in sane state */
281     assert(kChainingCellNormal == 0);
282
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;
315     /* Block new entries via HighWater until compiler thread is ready */
316     gDvmJit.compilerHighWater = 0;
317
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
325     /* Track method-level compilation statistics */
326     gDvmJit.methodStatsTable =  dvmHashTableCreate(32, NULL);
327
328     dvmUnlockMutex(&gDvmJit.compilerLock);
329
330     return true;
331
332 fail:
333     return false;
334 }
335
336 void 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
348         if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
349             LOGW("Compiler thread join failed\n");
350         else
351             LOGD("Compiler thread has shut down\n");
352     }
353 }