Merge "[JIT] Clear inCodeCacheFlag on extended singlestep" into dalvik-dev
diff --git a/tests/090-loop-formation/expected.txt b/tests/090-loop-formation/expected.txt
new file mode 100644
index 0000000..b7e0bb3
--- /dev/null
+++ b/tests/090-loop-formation/expected.txt
@@ -0,0 +1,5 @@
+counter1 is 0
+counter2 is 32767
+counter3 is 32767
+counter4 is 0
+counter5 is 65534
diff --git a/tests/090-loop-formation/info.txt b/tests/090-loop-formation/info.txt
new file mode 100644
index 0000000..98d1d4b
--- /dev/null
+++ b/tests/090-loop-formation/info.txt
@@ -0,0 +1,3 @@
+Test loop formation heuristics and code generation. Basically the problem to
+catch here is to make sure that some never-exercised code blocks are included
+in the loop region, and the JIT compiler won't choke on unresolved fields.
diff --git a/tests/090-loop-formation/src/Main.java b/tests/090-loop-formation/src/Main.java
new file mode 100644
index 0000000..7c16667
--- /dev/null
+++ b/tests/090-loop-formation/src/Main.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Create two versions of loops where the unresolved field is on either the
+ * taken or the non-taken path to make sure that the loop detection code bails
+ * on unresolved fields.
+ */
+public class Main {
+ static int counter1;
+ static int counter2;
+ static int counter3;
+ static int counter4;
+ static int counter5;
+
+ public static void main(String[] args) {
+ /* counter1 is not resolved */
+ for (int i = 0; i < 32767; i++) {
+ if (i < 0) {
+ counter1++;
+ } else {
+ counter2++;
+ }
+ counter5++;
+ }
+
+ /* counter4 is not resolved */
+ for (int i = 0; i < 32767; i++) {
+ if (i >= 0) {
+ counter3++;
+ } else {
+ counter4++;
+ }
+ counter5++;
+ }
+
+ System.out.println("counter1 is " + counter1);
+ System.out.println("counter2 is " + counter2);
+ System.out.println("counter3 is " + counter3);
+ System.out.println("counter4 is " + counter4);
+ System.out.println("counter5 is " + counter5);
+ }
+}
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
index c9b0e27..d655a3a 100644
--- a/vm/Dvm.mk
+++ b/vm/Dvm.mk
@@ -120,8 +120,6 @@
alloc/CardTable.c \
alloc/HeapBitmap.c.arm \
alloc/HeapDebug.c \
- alloc/HeapTable.c \
- alloc/HeapWorker.c \
alloc/Heap.c.arm \
alloc/DdmHeap.c \
alloc/Verify.c \
diff --git a/vm/Globals.h b/vm/Globals.h
index 1cb4526..faafcc6 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -367,11 +367,14 @@
int offJavaLangRefReference_queueNext;
int offJavaLangRefReference_pendingNext;
- /* method pointers - java.lang.ref.Reference */
- Method* methJavaLangRefReference_enqueueInternal;
+ /* field offsets - java.lang.ref.FinalizerReference */
+ int offJavaLangRefFinalizerReference_zombie;
- /* more method pointers - java.lang.ref.FinalizerReference */
- Method* methJavaLangRefFinalizerReferenceAdd;
+ /* method pointers - java.lang.ref.ReferenceQueue */
+ Method* methJavaLangRefReferenceQueueAdd;
+
+ /* method pointers - java.lang.ref.FinalizerReference */
+ Method* methJavaLangRefFinalizerReferenceAdd;
/* constructor method pointers; no vtable involved, so use Method* */
Method* methJavaLangStackTraceElement_init;
@@ -581,19 +584,6 @@
*/
LinearAllocHdr* pBootLoaderAlloc;
-
- /*
- * Heap worker thread.
- */
- bool heapWorkerInitialized;
- bool heapWorkerReady;
- bool haltHeapWorker;
- pthread_t heapWorkerHandle;
- pthread_mutex_t heapWorkerLock;
- pthread_cond_t heapWorkerCond;
- pthread_cond_t heapWorkerIdleCond;
- pthread_mutex_t heapWorkerListLock;
-
/*
* Compute some stats on loaded classes.
*/
diff --git a/vm/Init.c b/vm/Init.c
index f1afa85..a8cf258 100644
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -1314,6 +1314,9 @@
if (!dvmDebuggerStartup())
goto fail;
+ if (!dvmGcStartupClasses())
+ goto fail;
+
/*
* Init for either zygote mode or non-zygote mode. The key difference
* is that we don't start any additional threads in Zygote mode.
diff --git a/vm/InitRefs.c b/vm/InitRefs.c
index b5b7526..5a9ba2a 100644
--- a/vm/InitRefs.c
+++ b/vm/InitRefs.c
@@ -320,6 +320,11 @@
{ NULL, NULL, NULL }
};
+ static struct FieldInfo infoFinalizerReference[] = {
+ { &gDvm.offJavaLangRefFinalizerReference_zombie, "zombie", "Ljava/lang/Object;" },
+ { NULL, NULL, NULL }
+ };
+
static struct FieldInfo infoConstructor[] = {
{ &gDvm.offJavaLangReflectConstructor_slot, "slot", "I" },
{ &gDvm.offJavaLangReflectConstructor_declClass, "declaringClass", "Ljava/lang/Class;" },
@@ -357,6 +362,7 @@
{ "Ljava/lang/ThreadGroup;", infoThreadGroup },
{ "Ljava/lang/Throwable;", infoThrowable },
{ "Ljava/lang/VMThread;", infoVMThread },
+ { "Ljava/lang/ref/FinalizerReference;", infoFinalizerReference },
{ "Ljava/lang/reflect/Constructor;", infoConstructor },
{ "Ljava/lang/reflect/Field;", infoField },
{ "Ljava/lang/reflect/Method;", infoMethod },
@@ -468,6 +474,8 @@
"Lorg/apache/harmony/dalvik/ddmc/DdmServer;", "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;" },
{ &gDvm.methDalvikDdmcServer_broadcast,
"Lorg/apache/harmony/dalvik/ddmc/DdmServer;", "broadcast", "(I)V" },
+ { &gDvm.methJavaLangRefReferenceQueueAdd,
+ "Ljava/lang/ref/ReferenceQueue;", "add", "(Ljava/lang/ref/Reference;)V" },
{ NULL, NULL, NULL, NULL }
};
@@ -587,26 +595,16 @@
/* (documented in header) */
bool dvmFindReferenceMembers(ClassObject* classReference) {
- if (gDvm.methJavaLangRefReference_enqueueInternal != NULL) {
- LOGE("Attempt to set up class Reference more than once\n");
- return false;
- }
-
if (strcmp(classReference->descriptor, "Ljava/lang/ref/Reference;") != 0) {
LOGE("Attempt to set up the wrong class as Reference\n");
return false;
}
-
- /* Note: enqueueInternal() is private and thus a direct method. */
-
return initFieldOffset(classReference, &gDvm.offJavaLangRefReference_pendingNext,
- "pendingNext", "Ljava/lang/Object;")
+ "pendingNext", "Ljava/lang/ref/Reference;")
&& initFieldOffset(classReference, &gDvm.offJavaLangRefReference_queue,
"queue", "Ljava/lang/ref/ReferenceQueue;")
&& initFieldOffset(classReference, &gDvm.offJavaLangRefReference_queueNext,
"queueNext", "Ljava/lang/ref/Reference;")
&& initFieldOffset(classReference, &gDvm.offJavaLangRefReference_referent,
- "referent", "Ljava/lang/Object;")
- && initDirectMethodReferenceByClass(&gDvm.methJavaLangRefReference_enqueueInternal,
- classReference, "enqueueInternal", "()Z");
+ "referent", "Ljava/lang/Object;");
}
diff --git a/vm/Thread.c b/vm/Thread.c
index cdf49bc..087c2e0 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -3467,13 +3467,11 @@
#ifdef HAVE_ANDROID_OS
dvmPrintDebugMessage(target,
- "(mutexes: tll=%x tsl=%x tscl=%x ghl=%x hwl=%x hwll=%x)\n",
+ "(mutexes: tll=%x tsl=%x tscl=%x ghl=%x)\n",
gDvm.threadListLock.value,
gDvm._threadSuspendLock.value,
gDvm.threadSuspendCountLock.value,
- gDvm.gcHeapLock.value,
- gDvm.heapWorkerLock.value,
- gDvm.heapWorkerListLock.value);
+ gDvm.gcHeapLock.value);
#endif
if (grabLock)
diff --git a/vm/alloc/Alloc.c b/vm/alloc/Alloc.c
index 6286f8d..33f45e8 100644
--- a/vm/alloc/Alloc.c
+++ b/vm/alloc/Alloc.c
@@ -20,7 +20,6 @@
#include "alloc/Heap.h"
#include "alloc/HeapInternal.h"
#include "alloc/HeapSource.h"
-#include "alloc/HeapWorker.h"
/*
* Initialize the GC universe.
@@ -41,9 +40,6 @@
*/
bool dvmGcStartupAfterZygote(void)
{
- if (!dvmHeapWorkerStartup()) {
- return false;
- }
return dvmHeapStartupAfterZygote();
}
@@ -52,7 +48,6 @@
*/
void dvmGcThreadShutdown(void)
{
- dvmHeapWorkerShutdown();
dvmHeapThreadShutdown();
}
@@ -73,6 +68,44 @@
return dvmHeapSourceStartupBeforeFork();
}
+bool dvmGcStartupClasses(void)
+{
+ {
+ const char *klassName = "Ljava/lang/ref/ReferenceQueueThread;";
+ ClassObject *klass = dvmFindSystemClass(klassName);
+ if (klass == NULL) {
+ return false;
+ }
+ const char *methodName = "startReferenceQueue";
+ Method *method = dvmFindDirectMethodByDescriptor(klass, methodName, "()V");
+ if (method == NULL) {
+ return false;
+ }
+ Thread *self = dvmThreadSelf();
+ assert(self != NULL);
+ JValue unusedResult;
+ dvmCallMethod(self, method, NULL, &unusedResult);
+ }
+ {
+ const char *klassName = "Ljava/lang/FinalizerThread;";
+ ClassObject *klass = dvmFindSystemClass(klassName);
+ if (klass == NULL) {
+ return false;
+ }
+ const char *methodName = "startFinalizer";
+ Method *method = dvmFindDirectMethodByDescriptor(klass, methodName, "()V");
+ if (method == NULL) {
+ return false;
+ }
+ Thread *self = dvmThreadSelf();
+ assert(self != NULL);
+ JValue unusedResult;
+ dvmCallMethod(self, method, NULL, &unusedResult);
+ }
+
+ return true;
+}
+
/*
* Create a "stock instance" of an exception class.
*/
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
index 2dc2ae6..aeba3f5 100644
--- a/vm/alloc/Alloc.h
+++ b/vm/alloc/Alloc.h
@@ -29,6 +29,7 @@
bool dvmGcStartupAfterZygote(void);
void dvmGcShutdown(void);
void dvmGcThreadShutdown(void);
+bool dvmGcStartupClasses(void);
/*
* Do any last-minute preparation before we call fork() for the first time.
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index 5d88bc3..f38eb12 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -19,12 +19,10 @@
#include "Dalvik.h"
#include "alloc/HeapBitmap.h"
#include "alloc/Verify.h"
-#include "alloc/HeapTable.h"
#include "alloc/Heap.h"
#include "alloc/HeapInternal.h"
#include "alloc/DdmHeap.h"
#include "alloc/HeapSource.h"
-#include "alloc/HeapWorker.h"
#include "alloc/MarkSweep.h"
#include "utils/threads.h" // need Android thread priorities
@@ -91,9 +89,6 @@
if (gcHeap == NULL) {
return false;
}
- gcHeap->heapWorkerCurrentObject = NULL;
- gcHeap->heapWorkerCurrentMethod = NULL;
- gcHeap->heapWorkerInterpStartTime = 0LL;
gcHeap->ddmHpifWhen = 0;
gcHeap->ddmHpsgWhen = 0;
gcHeap->ddmHpsgWhat = 0;
@@ -101,22 +96,15 @@
gcHeap->ddmNhsgWhat = 0;
gDvm.gcHeap = gcHeap;
- /* Set up the lists and lock we'll use for finalizable
- * and reference objects.
+ /* Set up the lists we'll use for cleared reference objects.
*/
- dvmInitMutex(&gDvm.heapWorkerListLock);
- gcHeap->referenceOperations = NULL;
+ gcHeap->clearedReferences = NULL;
if (!dvmCardTableStartup(gDvm.heapMaximumSize)) {
LOGE_HEAP("card table startup failed.");
return false;
}
- /* Initialize the HeapWorker locks and other state
- * that the GC uses.
- */
- dvmInitializeHeapWorkerState();
-
return true;
}
@@ -130,13 +118,6 @@
//TODO: make sure we're locked
if (gDvm.gcHeap != NULL) {
dvmCardTableShutdown();
- /* Tables are allocated on the native heap; they need to be
- * cleaned up explicitly. The process may stick around, so we
- * don't want to leak any native memory.
- */
- dvmHeapFreeLargeTable(gDvm.gcHeap->referenceOperations);
- gDvm.gcHeap->referenceOperations = NULL;
-
/* Destroy the heap. Any outstanding pointers will point to
* unmapped memory (unless/until someone else maps it). This
* frees gDvm.gcHeap as a side-effect.
@@ -177,33 +158,6 @@
dvmUnlockMutex(&gDvm.gcHeapLock);
}
-/* Pop an object from the list of pending finalizations and
- * reference clears/enqueues, and return the object.
- * The caller must call dvmReleaseTrackedAlloc()
- * on the object when finished.
- *
- * Typically only called by the heap worker thread.
- */
-Object *dvmGetNextHeapWorkerObject()
-{
- Object *obj;
- GcHeap *gcHeap = gDvm.gcHeap;
-
- dvmLockMutex(&gDvm.heapWorkerListLock);
-
- obj = dvmHeapGetNextObjectFromLargeTable(&gcHeap->referenceOperations);
- if (obj != NULL) {
- /* Don't let the GC collect the object until the
- * worker thread is done with it.
- */
- dvmAddTrackedAlloc(obj, NULL);
- }
-
- dvmUnlockMutex(&gDvm.heapWorkerListLock);
-
- return obj;
-}
-
/* Do a full garbage collection, which may grow the
* heap as a side-effect if the live set is large.
*/
@@ -577,13 +531,6 @@
gcHeap->gcRunning = true;
- /*
- * Grab the heapWorkerLock to prevent the HeapWorker thread from
- * doing work. If it's executing a finalizer or an enqueue operation
- * it won't be holding the lock, so this should return quickly.
- */
- dvmLockMutex(&gDvm.heapWorkerLock);
-
rootSuspend = dvmGetRelativeTimeMsec();
dvmSuspendAllThreads(SUSPEND_FOR_GC);
rootStart = dvmGetRelativeTimeMsec();
@@ -596,21 +543,6 @@
if (!spec->isConcurrent) {
oldThreadPriority = raiseThreadPriority();
}
-
- /* Make sure that the HeapWorker thread hasn't become
- * wedged inside interp code. If it has, this call will
- * print a message and abort the VM.
- */
- dvmAssertHeapWorkerThreadRunning();
-
- /* Lock the pendingFinalizationRefs list.
- *
- * Acquire the lock after suspending so the finalizer
- * thread can't block in the RUNNING state while
- * we try to suspend.
- */
- dvmLockMutex(&gDvm.heapWorkerListLock);
-
if (gDvm.preVerify) {
LOGV_HEAP("Verifying roots and heap before GC");
verifyRootsAndHeap();
@@ -634,9 +566,11 @@
/* dvmHeapScanMarkedObjects() will build the lists of known
* instances of the Reference classes.
*/
- gcHeap->softReferences = NULL;
- gcHeap->weakReferences = NULL;
- gcHeap->phantomReferences = NULL;
+ assert(gcHeap->softReferences == NULL);
+ assert(gcHeap->weakReferences == NULL);
+ assert(gcHeap->finalizerReferences == NULL);
+ assert(gcHeap->phantomReferences == NULL);
+ assert(gcHeap->clearedReferences == NULL);
if (spec->isConcurrent) {
/*
@@ -747,25 +681,12 @@
currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
- /* Now that we've freed up the GC heap, return any large
- * free chunks back to the system. They'll get paged back
- * in the next time they're used. Don't do it immediately,
- * though; if the process is still allocating a bunch of
- * memory, we'll be taking a ton of page faults that we don't
- * necessarily need to.
- *
- * Cancel any old scheduled trims, and schedule a new one.
- */
- dvmScheduleHeapSourceTrim(5); // in seconds
-
dvmMethodTraceGCEnd();
LOGV_HEAP("GC finished");
gcHeap->gcRunning = false;
LOGV_HEAP("Resuming threads");
- dvmUnlockMutex(&gDvm.heapWorkerListLock);
- dvmUnlockMutex(&gDvm.heapWorkerLock);
if (spec->isConcurrent) {
/*
@@ -787,6 +708,11 @@
}
}
+ /*
+ * Move queue of pending references back into Java.
+ */
+ dvmEnqueueClearedReferences(&gDvm.gcHeap->clearedReferences);
+
percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);
if (!spec->isConcurrent) {
u4 markSweepTime = dirtyEnd - rootStart;
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
index 7f8c9c5..1d1be07 100644
--- a/vm/alloc/HeapInternal.h
+++ b/vm/alloc/HeapInternal.h
@@ -19,9 +19,6 @@
#ifndef _DALVIK_ALLOC_HEAP_INTERNAL
#define _DALVIK_ALLOC_HEAP_INTERNAL
-#include <time.h> // for struct timespec
-
-#include "HeapTable.h"
#include "MarkSweep.h"
typedef struct HeapSource HeapSource;
@@ -39,34 +36,9 @@
Object *finalizerReferences;
Object *phantomReferences;
- /* The list of Reference objects that need to be cleared and/or
- * enqueued. The bottom two bits of the object pointers indicate
- * whether they should be cleared and/or enqueued.
- *
- * This table is protected by gDvm.heapWorkerListLock, which must
- * be acquired after the heap lock.
+ /* The list of Reference objects that need to be enqueued.
*/
- LargeHeapRefTable *referenceOperations;
-
- /* If non-null, the method that the HeapWorker is currently
- * executing.
- */
- Object *heapWorkerCurrentObject;
- Method *heapWorkerCurrentMethod;
-
- /* If heapWorkerCurrentObject is non-null, this gives the time when
- * HeapWorker started executing that method. The time value must come
- * from dvmGetRelativeTimeUsec().
- *
- * The "Cpu" entry tracks the per-thread CPU timer (when available).
- */
- u8 heapWorkerInterpStartTime;
- u8 heapWorkerInterpCpuStartTime;
-
- /* If any fields are non-zero, indicates the next (absolute) time that
- * the HeapWorker thread should call dvmHeapSourceTrim().
- */
- struct timespec heapWorkerNextTrim;
+ Object *clearedReferences;
/* The current state of the mark step.
* Only valid during a GC.
diff --git a/vm/alloc/HeapTable.c b/vm/alloc/HeapTable.c
deleted file mode 100644
index 6c9034a..0000000
--- a/vm/alloc/HeapTable.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Dalvik.h"
-#include "alloc/HeapTable.h"
-#include "alloc/HeapInternal.h"
-
-#include <limits.h> // for INT_MAX
-
-static const int kLargeHeapRefTableNElems = 1024;
-static const int kFinalizableRefDefault = 128;
-
-bool dvmHeapInitHeapRefTable(ReferenceTable *refs)
-{
- return dvmInitReferenceTable(refs, kFinalizableRefDefault, INT_MAX);
-}
-
-/*
- * Large, non-contiguous reference tables
- */
-
-bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref)
-{
- LargeHeapRefTable *table;
-
- assert(tableP != NULL);
- assert(ref != NULL);
-
- /* Make sure that a table with a free slot is
- * at the head of the list.
- */
- if (*tableP != NULL) {
- table = *tableP;
- LargeHeapRefTable *prevTable;
-
- /* Find an empty slot for this reference.
- */
- prevTable = NULL;
- while (table != NULL && dvmIsReferenceTableFull(&table->refs)) {
- prevTable = table;
- table = table->next;
- }
- if (table != NULL) {
- if (prevTable != NULL) {
- /* Move the table to the head of the list.
- */
- prevTable->next = table->next;
- table->next = *tableP;
- *tableP = table;
- }
- /* else it's already at the head. */
-
- goto insert;
- }
- /* else all tables are already full;
- * fall through to the alloc case.
- */
- }
-
- /* Allocate a new table.
- */
- table = (LargeHeapRefTable *)calloc(1, sizeof(LargeHeapRefTable));
- if (table == NULL) {
- LOGE_HEAP("Can't allocate a new large ref table\n");
- return false;
- }
- if (!dvmInitReferenceTable(&table->refs,
- kLargeHeapRefTableNElems,
- INT_MAX)) {
- LOGE_HEAP("Can't initialize a new large ref table\n");
- free(table);
- return false;
- }
-
- /* Stick it at the head.
- */
- table->next = *tableP;
- *tableP = table;
-
-insert:
- /* Insert the reference.
- */
- assert(table == *tableP);
- assert(table != NULL);
- assert(!dvmIsReferenceTableFull(&table->refs));
- *table->refs.nextEntry++ = ref;
-
- return true;
-}
-
-bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP, ReferenceTable *refs)
-{
- LargeHeapRefTable *table;
-
- /* Allocate a node.
- */
- table = (LargeHeapRefTable *)calloc(1, sizeof(LargeHeapRefTable));
- if (table == NULL) {
- LOGE_HEAP("Can't allocate a new large ref table\n");
- return false;
- }
- table->refs = *refs;
-
- /* Insert the table into the list.
- */
- table->next = *tableP;
- *tableP = table;
-
- return true;
-}
-
-/* Frees everything associated with the LargeHeapRefTable.
- */
-void dvmHeapFreeLargeTable(LargeHeapRefTable *table)
-{
- while (table != NULL) {
- LargeHeapRefTable *next = table->next;
- dvmClearReferenceTable(&table->refs);
- free(table);
- table = next;
- }
-}
-
-Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable)
-{
- LargeHeapRefTable *table;
- Object *obj;
-
- assert(pTable != NULL);
-
- obj = NULL;
- table = *pTable;
- if (table != NULL) {
- ReferenceTable *refs = &table->refs;
-
- /* We should never have an empty table node in the list.
- */
- assert(dvmReferenceTableEntries(refs) != 0);
-
- /* Remove and return the last entry in the list.
- */
- obj = *--refs->nextEntry;
-
- /* If this was the last entry in the table node,
- * free it and patch up the list.
- */
- if (refs->nextEntry == refs->table) {
- *pTable = table->next;
- dvmClearReferenceTable(refs);
- free(table);
- }
- }
-
- return obj;
-}
diff --git a/vm/alloc/HeapTable.h b/vm/alloc/HeapTable.h
deleted file mode 100644
index 175111d..0000000
--- a/vm/alloc/HeapTable.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef _DALVIK_ALLOC_HEAP_TABLE
-#define _DALVIK_ALLOC_HEAP_TABLE
-
-#include "ReferenceTable.h"
-
-struct LargeHeapRefTable {
- struct LargeHeapRefTable *next;
- ReferenceTable refs;
-};
-
-typedef struct LargeHeapRefTable LargeHeapRefTable;
-
-bool dvmHeapInitHeapRefTable(ReferenceTable *refs);
-void dvmHeapFreeLargeTable(LargeHeapRefTable *table);
-bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref);
-void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table);
-bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP,
- ReferenceTable *refs);
-Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable);
-
-#endif // _DALVIK_ALLOC_HEAP_TABLE
diff --git a/vm/alloc/HeapWorker.c b/vm/alloc/HeapWorker.c
deleted file mode 100644
index 6017381..0000000
--- a/vm/alloc/HeapWorker.c
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * An async worker thread to handle certain heap operations that need
- * to be done in a separate thread to avoid synchronization problems.
- * HeapWorkers and reference enqueuing are handled by this thread.
- * The VM does all clearing.
- */
-#include "Dalvik.h"
-#include "alloc/HeapInternal.h"
-#include "alloc/HeapWorker.h"
-
-#include <sys/time.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <signal.h>
-#include <errno.h> // for ETIMEDOUT, etc.
-
-static void* heapWorkerThreadStart(void* arg);
-
-/*
- * Initialize any HeapWorker state that Heap.c
- * cares about. This lets the GC start before the
- * HeapWorker thread is initialized.
- */
-void dvmInitializeHeapWorkerState()
-{
- assert(!gDvm.heapWorkerInitialized);
-
- dvmInitMutex(&gDvm.heapWorkerLock);
- pthread_cond_init(&gDvm.heapWorkerCond, NULL);
- pthread_cond_init(&gDvm.heapWorkerIdleCond, NULL);
-
- gDvm.heapWorkerInitialized = true;
-}
-
-/*
- * Crank up the heap worker thread.
- *
- * Does not return until the thread is ready for business.
- */
-bool dvmHeapWorkerStartup(void)
-{
- assert(!gDvm.haltHeapWorker);
- assert(!gDvm.heapWorkerReady);
- assert(gDvm.heapWorkerHandle == 0);
- assert(gDvm.heapWorkerInitialized);
-
- /* use heapWorkerLock/heapWorkerCond to communicate readiness */
- dvmLockMutex(&gDvm.heapWorkerLock);
-
-//BUG: If a GC happens in here or in the new thread while we hold the lock,
-// the GC will deadlock when trying to acquire heapWorkerLock.
- if (!dvmCreateInternalThread(&gDvm.heapWorkerHandle,
- "HeapWorker", heapWorkerThreadStart, NULL))
- {
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- return false;
- }
-
- /*
- * Wait for the heap worker to come up. We know the thread was created,
- * so this should not get stuck.
- */
- while (!gDvm.heapWorkerReady) {
- dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
- }
-
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- return true;
-}
-
-/*
- * Shut down the heap worker thread if it was started.
- */
-void dvmHeapWorkerShutdown(void)
-{
- void* threadReturn;
-
- /* note: assuming that (pthread_t)0 is not a valid thread handle */
- if (gDvm.heapWorkerHandle != 0) {
- gDvm.haltHeapWorker = true;
- dvmSignalHeapWorker(true);
-
- /*
- * We may not want to wait for the heapWorkers to complete. It's
- * a good idea to do so, in case they're holding some sort of OS
- * resource that doesn't get reclaimed when the process exits
- * (e.g. an open temp file).
- */
- if (pthread_join(gDvm.heapWorkerHandle, &threadReturn) != 0)
- LOGW("HeapWorker thread join failed\n");
- else if (gDvm.verboseShutdown)
- LOGD("HeapWorker thread has shut down\n");
-
- gDvm.heapWorkerReady = false;
- }
-}
-
-/* Make sure that the HeapWorker thread hasn't spent an inordinate
- * amount of time inside a finalizer.
- *
- * Aborts the VM if the thread appears to be wedged.
- *
- * The caller must hold the heapWorkerLock to guarantee an atomic
- * read of the watchdog values.
- */
-void dvmAssertHeapWorkerThreadRunning()
-{
- if (gDvm.gcHeap->heapWorkerCurrentObject != NULL) {
- static const u8 HEAP_WORKER_WATCHDOG_TIMEOUT = 10*1000*1000LL; // 10sec
-
- u8 heapWorkerInterpStartTime = gDvm.gcHeap->heapWorkerInterpStartTime;
- u8 now = dvmGetRelativeTimeUsec();
- u8 delta = now - heapWorkerInterpStartTime;
-
- if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT &&
- (gDvm.debuggerActive || gDvm.nativeDebuggerActive))
- {
- /*
- * Debugger suspension can block the thread indefinitely. For
- * best results we should reset this explicitly whenever the
- * HeapWorker thread is resumed. Unfortunately this is also
- * affected by native debuggers, and we have no visibility
- * into how they're manipulating us. So, we ignore the
- * watchdog and just reset the timer.
- */
- LOGI("Debugger is attached -- suppressing HeapWorker watchdog\n");
- gDvm.gcHeap->heapWorkerInterpStartTime = now; /* reset timer */
- } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT) {
- /*
- * Before we give up entirely, see if maybe we're just not
- * getting any CPU time because we're stuck in a background
- * process group. If we successfully move the thread into the
- * foreground we'll just leave it there (it doesn't do anything
- * if the process isn't GCing).
- */
- dvmLockThreadList(NULL);
- Thread* thread = dvmGetThreadByHandle(gDvm.heapWorkerHandle);
- dvmUnlockThreadList();
-
- if (thread != NULL) {
- int priChangeFlags, threadPrio;
- SchedPolicy threadPolicy;
- priChangeFlags = dvmRaiseThreadPriorityIfNeeded(thread,
- &threadPrio, &threadPolicy);
- if (priChangeFlags != 0) {
- LOGI("HeapWorker watchdog expired, raising priority"
- " and retrying\n");
- gDvm.gcHeap->heapWorkerInterpStartTime = now;
- return;
- }
- }
-
- char* desc = dexProtoCopyMethodDescriptor(
- &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
- LOGE("HeapWorker is wedged: %lldms spent inside %s.%s%s\n",
- delta / 1000,
- gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
- gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
- free(desc);
- dvmDumpAllThreads(true);
-
- /* try to get a debuggerd dump from the target thread */
- dvmNukeThread(thread);
-
- /* abort the VM */
- dvmAbort();
- } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT / 2) {
- char* desc = dexProtoCopyMethodDescriptor(
- &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
- LOGW("HeapWorker may be wedged: %lldms spent inside %s.%s%s\n",
- delta / 1000,
- gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
- gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
- free(desc);
- }
- }
-}
-
-/*
- * Acquires a mutex, transitioning to the VMWAIT state if the mutex is
- * held. This allows the thread to suspend while it waits for another
- * thread to release the mutex.
- */
-static void lockMutex(pthread_mutex_t *mu)
-{
- Thread *self;
- ThreadStatus oldStatus;
-
- assert(mu != NULL);
- if (dvmTryLockMutex(mu) != 0) {
- self = dvmThreadSelf();
- assert(self != NULL);
- oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
- dvmLockMutex(mu);
- dvmChangeStatus(self, oldStatus);
- }
-}
-
-static void callMethod(Thread *self, Object *obj, Method *method)
-{
- JValue unused;
-
- /* Keep track of the method we're about to call and
- * the current time so that other threads can detect
- * when this thread wedges and provide useful information.
- */
- gDvm.gcHeap->heapWorkerInterpStartTime = dvmGetRelativeTimeUsec();
- gDvm.gcHeap->heapWorkerInterpCpuStartTime = dvmGetThreadCpuTimeUsec();
- gDvm.gcHeap->heapWorkerCurrentMethod = method;
- gDvm.gcHeap->heapWorkerCurrentObject = obj;
-
- /* Call the method.
- *
- * Don't hold the lock when executing interpreted
- * code. It may suspend, and the GC needs to grab
- * heapWorkerLock.
- */
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- if (false) {
- /* Log entry/exit; this will likely flood the log enough to
- * cause "logcat" to drop entries.
- */
- char tmpTag[16];
- sprintf(tmpTag, "HW%d", self->systemTid);
- LOG(LOG_DEBUG, tmpTag, "Call %s\n", method->clazz->descriptor);
- dvmCallMethod(self, method, obj, &unused);
- LOG(LOG_DEBUG, tmpTag, " done\n");
- } else {
- dvmCallMethod(self, method, obj, &unused);
- }
- /*
- * Reacquire the heap worker lock in a suspend-friendly way.
- */
- lockMutex(&gDvm.heapWorkerLock);
-
- gDvm.gcHeap->heapWorkerCurrentObject = NULL;
- gDvm.gcHeap->heapWorkerCurrentMethod = NULL;
- gDvm.gcHeap->heapWorkerInterpStartTime = 0LL;
-
- /* Exceptions thrown during these calls interrupt
- * the method, but are otherwise ignored.
- */
- if (dvmCheckException(self)) {
-#if DVM_SHOW_EXCEPTION >= 1
- LOGI("Uncaught exception thrown by finalizer (will be discarded):\n");
- dvmLogExceptionStackTrace();
-#endif
- dvmClearException(self);
- }
-}
-
-/* Process all enqueued heap work, including finalizers and reference
- * enqueueing. Clearing has already been done by the VM.
- *
- * Caller must hold gDvm.heapWorkerLock.
- */
-static void doHeapWork(Thread *self)
-{
- Object *obj;
- size_t numReferencesEnqueued;
-
- assert(gDvm.methJavaLangRefReference_enqueueInternal != NULL);
- numReferencesEnqueued = 0;
- while ((obj = dvmGetNextHeapWorkerObject()) != NULL) {
- /* Make sure the object hasn't been collected since
- * being scheduled.
- */
- assert(dvmIsValidObject(obj));
-
- /* Call the appropriate method(s).
- */
- assert(dvmGetFieldObject(
- obj, gDvm.offJavaLangRefReference_queue) != NULL);
- assert(dvmGetFieldObject(
- obj, gDvm.offJavaLangRefReference_queueNext) == NULL);
- numReferencesEnqueued++;
- callMethod(self, obj, gDvm.methJavaLangRefReference_enqueueInternal);
-
- /* Let the GC collect the object.
- */
- dvmReleaseTrackedAlloc(obj, self);
- }
- LOGV("Enqueued %zd references", numReferencesEnqueued);
-}
-
-/*
- * The heap worker thread sits quietly until the GC tells it there's work
- * to do.
- */
-static void* heapWorkerThreadStart(void* arg)
-{
- Thread *self = dvmThreadSelf();
-
- UNUSED_PARAMETER(arg);
-
- LOGV("HeapWorker thread started (threadid=%d)\n", self->threadId);
-
- /* tell the main thread that we're ready */
- lockMutex(&gDvm.heapWorkerLock);
- gDvm.heapWorkerReady = true;
- dvmSignalCond(&gDvm.heapWorkerCond);
- dvmUnlockMutex(&gDvm.heapWorkerLock);
-
- lockMutex(&gDvm.heapWorkerLock);
- while (!gDvm.haltHeapWorker) {
- struct timespec trimtime;
- bool timedwait = false;
-
- /* We're done running interpreted code for now. */
- dvmChangeStatus(NULL, THREAD_VMWAIT);
-
- /* Signal anyone who wants to know when we're done. */
- dvmBroadcastCond(&gDvm.heapWorkerIdleCond);
-
- /* Trim the heap if we were asked to. */
- trimtime = gDvm.gcHeap->heapWorkerNextTrim;
- if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) {
- struct timespec now;
-
-#ifdef HAVE_TIMEDWAIT_MONOTONIC
- clock_gettime(CLOCK_MONOTONIC, &now); // relative time
-#else
- struct timeval tvnow;
- gettimeofday(&tvnow, NULL); // absolute time
- now.tv_sec = tvnow.tv_sec;
- now.tv_nsec = tvnow.tv_usec * 1000;
-#endif
-
- if (trimtime.tv_sec < now.tv_sec ||
- (trimtime.tv_sec == now.tv_sec &&
- trimtime.tv_nsec <= now.tv_nsec))
- {
- size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];
-
- /*
- * Acquire the gcHeapLock. The requires releasing the
- * heapWorkerLock before the gcHeapLock is acquired.
- * It is possible that the gcHeapLock may be acquired
- * during a concurrent GC in which case heapWorkerLock
- * is held by the GC and we are unable to make forward
- * progress. We avoid deadlock by releasing the
- * gcHeapLock and then waiting to be signaled when the
- * GC completes. There is no guarantee that the next
- * time we are run will coincide with GC inactivity so
- * the check and wait must be performed within a loop.
- */
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- dvmLockHeap();
- dvmWaitForConcurrentGcToComplete();
- dvmLockMutex(&gDvm.heapWorkerLock);
-
- memset(madvisedSizes, 0, sizeof(madvisedSizes));
- dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
-
- dvmUnlockHeap();
-
- trimtime.tv_sec = 0;
- trimtime.tv_nsec = 0;
- gDvm.gcHeap->heapWorkerNextTrim = trimtime;
- } else {
- timedwait = true;
- }
- }
-
- /* sleep until signaled */
- if (timedwait) {
- int cc __attribute__ ((__unused__));
-#ifdef HAVE_TIMEDWAIT_MONOTONIC
- cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond,
- &gDvm.heapWorkerLock, &trimtime);
-#else
- cc = pthread_cond_timedwait(&gDvm.heapWorkerCond,
- &gDvm.heapWorkerLock, &trimtime);
-#endif
- assert(cc == 0 || cc == ETIMEDOUT);
- } else {
- dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
- }
-
- /*
- * Return to the running state before doing heap work. This
- * will block if the GC has initiated a suspend. We release
- * the heapWorkerLock beforehand for the GC to make progress
- * and wait to be signaled after the GC completes. There is
- * no guarantee that the next time we are run will coincide
- * with GC inactivity so the check and wait must be performed
- * within a loop.
- */
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- dvmChangeStatus(NULL, THREAD_RUNNING);
- dvmLockHeap();
- dvmWaitForConcurrentGcToComplete();
- dvmLockMutex(&gDvm.heapWorkerLock);
- dvmUnlockHeap();
- LOGV("HeapWorker is awake\n");
-
- /* Process any events in the queue.
- */
- doHeapWork(self);
- }
- dvmUnlockMutex(&gDvm.heapWorkerLock);
-
- if (gDvm.verboseShutdown)
- LOGD("HeapWorker thread shutting down\n");
- return NULL;
-}
-
-/*
- * Wake up the heap worker to let it know that there's work to be done.
- */
-void dvmSignalHeapWorker(bool shouldLock)
-{
- if (shouldLock) {
- dvmLockMutex(&gDvm.heapWorkerLock);
- }
-
- dvmSignalCond(&gDvm.heapWorkerCond);
-
- if (shouldLock) {
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- }
-}
-
-/*
- * Requests that dvmHeapSourceTrim() be called no sooner
- * than timeoutSec seconds from now. If timeoutSec
- * is zero, any pending trim is cancelled.
- *
- * Caller must hold heapWorkerLock.
- */
-void dvmScheduleHeapSourceTrim(size_t timeoutSec)
-{
- GcHeap *gcHeap = gDvm.gcHeap;
- struct timespec timeout;
-
- if (timeoutSec == 0) {
- timeout.tv_sec = 0;
- timeout.tv_nsec = 0;
- /* Don't wake up the thread just to tell it to cancel.
- * If it wakes up naturally, we can avoid the extra
- * context switch.
- */
- } else {
-#ifdef HAVE_TIMEDWAIT_MONOTONIC
- clock_gettime(CLOCK_MONOTONIC, &timeout);
- timeout.tv_sec += timeoutSec;
-#else
- struct timeval now;
- gettimeofday(&now, NULL);
- timeout.tv_sec = now.tv_sec + timeoutSec;
- timeout.tv_nsec = now.tv_usec * 1000;
-#endif
- dvmSignalHeapWorker(false);
- }
- gcHeap->heapWorkerNextTrim = timeout;
-}
diff --git a/vm/alloc/HeapWorker.h b/vm/alloc/HeapWorker.h
deleted file mode 100644
index 67babc3..0000000
--- a/vm/alloc/HeapWorker.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * Manage async heap tasks.
- */
-#ifndef _DALVIK_ALLOC_HEAP_WORKER
-#define _DALVIK_ALLOC_HEAP_WORKER
-
-/*
- * Initialize any HeapWorker state that Heap.c
- * cares about. This lets the GC start before the
- * HeapWorker thread is initialized.
- */
-void dvmInitializeHeapWorkerState(void);
-
-/*
- * Initialization. Starts/stops the worker thread.
- */
-bool dvmHeapWorkerStartup(void);
-void dvmHeapWorkerShutdown(void);
-
-/*
- * Tell the worker thread to wake up and do work.
- * If shouldLock is false, the caller must have already
- * acquired gDvm.heapWorkerLock.
- */
-void dvmSignalHeapWorker(bool shouldLock);
-
-/*
- * Requests that dvmHeapSourceTrim() be called no sooner
- * than timeoutSec seconds from now. If timeoutSec
- * is zero, any pending trim is cancelled.
- *
- * Caller must hold heapWorkerLock.
- */
-void dvmScheduleHeapSourceTrim(size_t timeoutSec);
-
-/* Make sure that the HeapWorker thread hasn't spent an inordinate
- * amount of time inside interpreted code.
- *
- * Aborts the VM if the thread appears to be wedged.
- *
- * The caller must hold the heapWorkerLock.
- */
-void dvmAssertHeapWorkerThreadRunning();
-
-/*
- * Called by the worker thread to get the next object
- * to finalize/enqueue/clear. Implemented in Heap.c.
- *
- * @param op The operation to perform on the returned object.
- * Must be non-NULL.
- * @return The object to operate on, or NULL.
- */
-Object *dvmGetNextHeapWorkerObject();
-
-#endif /*_DALVIK_ALLOC_HEAP_WORKER*/
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index 69b75de..e26394c 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -20,7 +20,6 @@
#include "alloc/HeapBitmapInlines.h"
#include "alloc/HeapInternal.h"
#include "alloc/HeapSource.h"
-#include "alloc/HeapWorker.h"
#include "alloc/MarkSweep.h"
#include "alloc/Visit.h"
#include "alloc/VisitInlines.h"
@@ -342,8 +341,6 @@
* - Primitive classes
* - Special objects
* - gDvm.outOfMemoryObj
- * - Objects allocated with ALLOC_NO_GC
- * - Objects pending finalization (but not yet finalized)
* - Objects in debugger object registry
*
* Don't need:
@@ -795,11 +792,7 @@
assert(ref != NULL);
assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
- if (!dvmHeapAddRefToLargeTable(&gDvm.gcHeap->referenceOperations, ref)) {
- LOGE_HEAP("enqueueReference(): no room for any more "
- "reference operations\n");
- dvmAbort();
- }
+ enqueuePendingReference(ref, &gDvm.gcHeap->clearedReferences);
}
/*
@@ -857,11 +850,9 @@
GcMarkContext *ctx;
Object *ref, *referent;
size_t referentOffset;
- bool doSignal;
ctx = &gDvm.gcHeap->markContext;
referentOffset = gDvm.offJavaLangRefReference_referent;
- doSignal = false;
while (*list != NULL) {
ref = dequeuePendingReference(list);
referent = dvmGetFieldObject(ref, referentOffset);
@@ -870,31 +861,23 @@
clearReference(ref);
if (isEnqueuable(ref)) {
enqueueReference(ref);
- doSignal = true;
}
}
}
- /*
- * If we cleared a reference with a reference queue we must notify
- * the heap worker to append the reference.
- */
- if (doSignal) {
- dvmSignalHeapWorker(false);
- }
assert(*list == NULL);
}
/*
* Enqueues finalizer references with white referents. White
- * referents are blackened, moved to the pendingNext field, and the
+ * referents are blackened, moved to the zombie field, and the
* referent field is cleared.
*/
static void enqueueFinalizerReferences(Object **list)
{
GcMarkContext *ctx = &gDvm.gcHeap->markContext;
size_t referentOffset = gDvm.offJavaLangRefReference_referent;
- size_t pendingNextOffset = gDvm.offJavaLangRefReference_pendingNext;
- bool doSignal = false;
+ size_t zombieOffset = gDvm.offJavaLangRefFinalizerReference_zombie;
+ bool hasEnqueued = false;
while (*list != NULL) {
Object *ref = dequeuePendingReference(list);
Object *referent = dvmGetFieldObject(ref, referentOffset);
@@ -902,15 +885,14 @@
markObject(referent, ctx);
/* If the referent is non-null the reference must queuable. */
assert(isEnqueuable(ref));
- dvmSetFieldObject(ref, pendingNextOffset, referent);
+ dvmSetFieldObject(ref, zombieOffset, referent);
clearReference(ref);
enqueueReference(ref);
- doSignal = true;
+ hasEnqueued = true;
}
}
- if (doSignal) {
+ if (hasEnqueued) {
processMarkStack(ctx);
- dvmSignalHeapWorker(false);
}
assert(*list == NULL);
}
@@ -928,8 +910,8 @@
assert(self != NULL);
Method *meth = gDvm.methJavaLangRefFinalizerReferenceAdd;
assert(meth != NULL);
- JValue unused;
- dvmCallMethod(self, meth, obj, &unused, obj);
+ JValue unusedResult;
+ dvmCallMethod(self, meth, NULL, &unusedResult, obj);
}
/*
@@ -982,6 +964,24 @@
assert(*phantomReferences == NULL);
}
+/*
+ * Pushes a list of cleared references out to the managed heap.
+ */
+void dvmEnqueueClearedReferences(Object **cleared)
+{
+ assert(cleared != NULL);
+ if (*cleared != NULL) {
+ Thread *self = dvmThreadSelf();
+ assert(self != NULL);
+ Method *meth = gDvm.methJavaLangRefReferenceQueueAdd;
+ assert(meth != NULL);
+ JValue unused;
+ Object *reference = *cleared;
+ dvmCallMethod(self, meth, NULL, &unused, reference);
+ *cleared = NULL;
+ }
+}
+
void dvmHeapFinishMarkStep()
{
GcMarkContext *ctx;
diff --git a/vm/alloc/MarkSweep.h b/vm/alloc/MarkSweep.h
index c9f11e4..94bf3ad 100644
--- a/vm/alloc/MarkSweep.h
+++ b/vm/alloc/MarkSweep.h
@@ -59,5 +59,6 @@
void dvmHeapSweepSystemWeaks(void);
void dvmHeapSweepUnmarkedObjects(bool isPartial, bool isConcurrent,
size_t *numObjects, size_t *numBytes);
+void dvmEnqueueClearedReferences(Object **references);
#endif // _DALVIK_ALLOC_MARK_SWEEP
diff --git a/vm/alloc/Visit.c b/vm/alloc/Visit.c
index 634611c..6ca1439 100644
--- a/vm/alloc/Visit.c
+++ b/vm/alloc/Visit.c
@@ -83,20 +83,6 @@
}
/*
- * Visits a large heap reference table. These objects are list heads.
- * As such, it is valid for table to be NULL.
- */
-static void visitLargeHeapRefTable(RootVisitor *visitor,
- LargeHeapRefTable *table,
- RootType type, void *arg)
-{
- assert(visitor != NULL);
- for (; table != NULL; table = table->next) {
- visitReferenceTable(visitor, &table->refs, 0, type, arg);
- }
-}
-
-/*
* Visits all stack slots except those belonging to native method
* arguments.
*/
@@ -250,7 +236,6 @@
dvmLockMutex(&gDvm.jniPinRefLock);
visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_VM_INTERNAL, arg);
dvmUnlockMutex(&gDvm.jniPinRefLock);
- visitLargeHeapRefTable(visitor, gDvm.gcHeap->referenceOperations, ROOT_REFERENCE_CLEANUP, arg);
visitThreads(visitor, arg);
(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
(*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
diff --git a/vm/alloc/Visit.h b/vm/alloc/Visit.h
index e7c52e5..a66839d 100644
--- a/vm/alloc/Visit.h
+++ b/vm/alloc/Visit.h
@@ -30,9 +30,7 @@
ROOT_MONITOR_USED,
ROOT_THREAD_OBJECT,
ROOT_INTERNED_STRING,
- ROOT_FINALIZING,
ROOT_DEBUGGER,
- ROOT_REFERENCE_CLEANUP,
ROOT_VM_INTERNAL,
ROOT_JNI_MONITOR,
} RootType;
diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h
index 12add75..35d34b3 100644
--- a/vm/compiler/Compiler.h
+++ b/vm/compiler/Compiler.h
@@ -209,6 +209,7 @@
void dvmInitializeSSAConversion(struct CompilationUnit *cUnit);
int dvmConvertSSARegToDalvik(const struct CompilationUnit *cUnit, int ssaReg);
bool dvmCompilerLoopOpt(struct CompilationUnit *cUnit);
+void dvmCompilerInsertBackwardChaining(struct CompilationUnit *cUnit);
void dvmCompilerNonLoopAnalysis(struct CompilationUnit *cUnit);
bool dvmCompilerFindLocalLiveIn(struct CompilationUnit *cUnit,
struct BasicBlock *bb);
@@ -239,4 +240,5 @@
void *dvmCompilerGetInterpretTemplate();
JitInstructionSetType dvmCompilerGetInterpretTemplateSet();
u8 dvmGetRegResourceMask(int reg);
+void dvmDumpCFG(struct CompilationUnit *cUnit, const char *dirPrefix);
#endif /* _DALVIK_VM_COMPILER */
diff --git a/vm/compiler/CompilerIR.h b/vm/compiler/CompilerIR.h
index 7b9987b..bdb69ce 100644
--- a/vm/compiler/CompilerIR.h
+++ b/vm/compiler/CompilerIR.h
@@ -54,11 +54,9 @@
kChainingCellGap,
/* Don't insert new fields between Gap and Last */
kChainingCellLast = kChainingCellGap + 1,
- kMethodEntryBlock,
- kTraceEntryBlock,
+ kEntryBlock,
kDalvikByteCode,
- kTraceExitBlock,
- kMethodExitBlock,
+ kExitBlock,
kPCReconstruction,
kExceptionHandling,
kCatchEntry,
@@ -255,12 +253,13 @@
*/
const u2 *switchOverflowPad;
- /* New fields only for method-based compilation */
JitMode jitMode;
int numReachableBlocks;
int numDalvikRegisters; // method->registersSize + inlined
BasicBlock *entryBlock;
BasicBlock *exitBlock;
+ BasicBlock *puntBlock; // punting to interp for exceptions
+ BasicBlock *backChainBlock; // for loop-trace
BasicBlock *curBlock;
BasicBlock *nextCodegenBlock; // for extended trace codegen
GrowableList dfsOrder;
@@ -272,6 +271,7 @@
BitVector *tempSSARegisterV; // numSSARegs
bool printSSANames;
void *blockLabelList;
+ bool quitLoopMode; // cold path/complex bytecode
} CompilationUnit;
#if defined(WITH_SELF_VERIFICATION)
diff --git a/vm/compiler/Dataflow.c b/vm/compiler/Dataflow.c
index 26068a1..c3355e9 100644
--- a/vm/compiler/Dataflow.c
+++ b/vm/compiler/Dataflow.c
@@ -2190,8 +2190,7 @@
GrowableList *ivList = cUnit->loopAnalysis->ivList;
MIR *mir;
- if (bb->blockType != kDalvikByteCode &&
- bb->blockType != kTraceEntryBlock) {
+ if (bb->blockType != kDalvikByteCode && bb->blockType != kEntryBlock) {
return false;
}
@@ -2395,9 +2394,8 @@
if (bb == NULL) break;
if (bb->hidden == true) continue;
if (bb->blockType == kDalvikByteCode ||
- bb->blockType == kTraceEntryBlock ||
- bb->blockType == kMethodEntryBlock ||
- bb->blockType == kMethodExitBlock) {
+ bb->blockType == kEntryBlock ||
+ bb->blockType == kExitBlock) {
bb->dataFlowInfo = (BasicBlockDataFlow *)
dvmCompilerNew(sizeof(BasicBlockDataFlow),
true);
diff --git a/vm/compiler/Frontend.c b/vm/compiler/Frontend.c
index 8272fb6..1338acc 100644
--- a/vm/compiler/Frontend.c
+++ b/vm/compiler/Frontend.c
@@ -566,6 +566,7 @@
/* Handle the fallthrough path */
bottomBlock->fallThrough = origBlock->fallThrough;
origBlock->fallThrough = bottomBlock;
+ origBlock->needFallThroughBranch = true;
dvmCompilerSetBit(bottomBlock->predecessors, origBlock->id);
if (bottomBlock->fallThrough) {
dvmCompilerClearBit(bottomBlock->fallThrough->predecessors,
@@ -633,7 +634,7 @@
}
/* Dump the CFG into a DOT graph */
-void dumpCFG(CompilationUnit *cUnit, const char *dirPrefix)
+void dvmDumpCFG(CompilationUnit *cUnit, const char *dirPrefix)
{
const Method *method = cUnit->method;
FILE *file;
@@ -686,14 +687,16 @@
BasicBlock *bb = (BasicBlock *) dvmGrowableListGetElement(blockList,
blockIdx);
if (bb == NULL) break;
- if (bb->blockType == kMethodEntryBlock) {
+ if (bb->blockType == kEntryBlock) {
fprintf(file, " entry [shape=Mdiamond];\n");
- } else if (bb->blockType == kMethodExitBlock) {
+ } else if (bb->blockType == kExitBlock) {
fprintf(file, " exit [shape=Mdiamond];\n");
} else if (bb->blockType == kDalvikByteCode) {
fprintf(file, " block%04x [shape=record,label = \"{ \\\n",
bb->startOffset);
const MIR *mir;
+ fprintf(file, " {block id %d\\l}%s\\\n", bb->id,
+ bb->firstMIRInsn ? " | " : " ");
for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
fprintf(file, " {%04x %s\\l}%s\\\n", mir->offset,
mir->ssaRep ?
@@ -834,7 +837,7 @@
char blockName1[BLOCK_NAME_LEN], blockName2[BLOCK_NAME_LEN];
dvmGetBlockName(bb, blockName1);
dvmGetBlockName(predBB, blockName2);
- dumpCFG(cUnit, "/sdcard/cfg/");
+ dvmDumpCFG(cUnit, "/sdcard/cfg/");
LOGE("Successor %s not found from %s",
blockName1, blockName2);
dvmAbort();
@@ -1186,8 +1189,8 @@
cUnit.tryBlockAddr = tryBlockAddr;
/* Create the default entry and exit blocks and enter them to the list */
- BasicBlock *entryBlock = dvmCompilerNewBB(kMethodEntryBlock, numBlocks++);
- BasicBlock *exitBlock = dvmCompilerNewBB(kMethodExitBlock, numBlocks++);
+ BasicBlock *entryBlock = dvmCompilerNewBB(kEntryBlock, numBlocks++);
+ BasicBlock *exitBlock = dvmCompilerNewBB(kExitBlock, numBlocks++);
cUnit.entryBlock = entryBlock;
cUnit.exitBlock = exitBlock;
@@ -1308,7 +1311,7 @@
dvmCompilerMethodMIR2LIR(&cUnit);
// Debugging only
- //dumpCFG(&cUnit, "/sdcard/cfg/");
+ //dvmDumpCFG(&cUnit, "/sdcard/cfg/");
/* Method is not empty */
if (cUnit.firstLIRInsn) {
@@ -1348,8 +1351,8 @@
curBlock->visited = true;
- if (curBlock->blockType == kMethodEntryBlock ||
- curBlock->blockType == kMethodExitBlock) {
+ if (curBlock->blockType == kEntryBlock ||
+ curBlock->blockType == kExitBlock) {
return false;
}
@@ -1397,6 +1400,34 @@
break;
}
curOffset += width;
+ BasicBlock *nextBlock = findBlock(cUnit, curOffset,
+ /* split */
+ false,
+ /* create */
+ false);
+ if (nextBlock) {
+ /*
+ * The next instruction could be the target of a previously parsed
+ * forward branch so a block is already created. If the current
+ * instruction is not an unconditional branch, connect them through
+ * the fall-through link.
+ */
+ assert(curBlock->fallThrough == NULL ||
+ curBlock->fallThrough == nextBlock ||
+ curBlock->fallThrough == cUnit->exitBlock);
+
+ if ((curBlock->fallThrough == NULL) &&
+ (flags & kInstrCanContinue)) {
+ curBlock->needFallThroughBranch = true;
+ curBlock->fallThrough = nextBlock;
+ dvmCompilerSetBit(nextBlock->predecessors, curBlock->id);
+ }
+ /* Block has been visited - no more parsing needed */
+ if (nextBlock->visited == true) {
+ return true;
+ }
+ curBlock = nextBlock;
+ }
}
return true;
}
@@ -1410,6 +1441,10 @@
int numBlocks = 0;
unsigned int curOffset = startOffset;
bool changed;
+ BasicBlock *bb;
+#if defined(WITH_JIT_TUNING)
+ CompilerMethodStats *methodStats;
+#endif
cUnit->jitMode = kJitLoop;
@@ -1420,9 +1455,9 @@
dvmInitGrowableList(&cUnit->pcReconstructionList, 8);
/* Create the default entry and exit blocks and enter them to the list */
- BasicBlock *entryBlock = dvmCompilerNewBB(kMethodEntryBlock, numBlocks++);
+ BasicBlock *entryBlock = dvmCompilerNewBB(kEntryBlock, numBlocks++);
entryBlock->startOffset = curOffset;
- BasicBlock *exitBlock = dvmCompilerNewBB(kMethodExitBlock, numBlocks++);
+ BasicBlock *exitBlock = dvmCompilerNewBB(kExitBlock, numBlocks++);
cUnit->entryBlock = entryBlock;
cUnit->exitBlock = exitBlock;
@@ -1452,6 +1487,20 @@
changed = exhaustTrace(cUnit, curBlock);
} while (changed);
+ /* Backward chaining block */
+ bb = dvmCompilerNewBB(kChainingCellBackwardBranch, cUnit->numBlocks++);
+ dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+ cUnit->backChainBlock = bb;
+
+ /* A special block to host PC reconstruction code */
+ bb = dvmCompilerNewBB(kPCReconstruction, cUnit->numBlocks++);
+ dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+
+ /* And one final block that publishes the PC and raises the exception */
+ bb = dvmCompilerNewBB(kExceptionHandling, cUnit->numBlocks++);
+ dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+ cUnit->puntBlock = bb;
+
cUnit->numDalvikRegisters = cUnit->method->registersSize;
/* Verify if all blocks are connected as claimed */
@@ -1465,15 +1514,75 @@
if (!dvmCompilerBuildLoop(cUnit))
goto bail;
+ dvmCompilerLoopOpt(cUnit);
+
+ /*
+ * Change the backward branch to the backward chaining cell after dataflow
+ * analsys/optimizations are done.
+ */
+ dvmCompilerInsertBackwardChaining(cUnit);
+
dvmCompilerInitializeRegAlloc(cUnit);
/* Allocate Registers using simple local allocation scheme */
dvmCompilerLocalRegAlloc(cUnit);
- if (gDvmJit.receivedSIGUSR2) {
- dumpCFG(cUnit, "/sdcard/cfg/");
+ /* Convert MIR to LIR, etc. */
+ dvmCompilerMIR2LIR(cUnit);
+
+ /* Loop contains never executed blocks / heavy instructions */
+ if (cUnit->quitLoopMode) {
+ if (cUnit->printMe || gDvmJit.receivedSIGUSR2) {
+ LOGD("Loop trace @ offset %04x aborted due to unresolved code info",
+ cUnit->entryBlock->startOffset);
+ }
+ goto bail;
}
+ /* Convert LIR into machine code. Loop for recoverable retries */
+ do {
+ dvmCompilerAssembleLIR(cUnit, info);
+ cUnit->assemblerRetries++;
+ if (cUnit->printMe && cUnit->assemblerStatus != kSuccess)
+ LOGD("Assembler abort #%d on %d", cUnit->assemblerRetries,
+ cUnit->assemblerStatus);
+ } while (cUnit->assemblerStatus == kRetryAll);
+
+ /* Loop is too big - bail out */
+ if (cUnit->assemblerStatus == kRetryHalve) {
+ goto bail;
+ }
+
+ if (cUnit->printMe || gDvmJit.receivedSIGUSR2) {
+ LOGD("Loop trace @ offset %04x", cUnit->entryBlock->startOffset);
+ dvmCompilerCodegenDump(cUnit);
+ }
+
+ /*
+ * If this trace uses class objects as constants,
+ * dvmJitInstallClassObjectPointers will switch the thread state
+ * to running and look up the class pointers using the descriptor/loader
+ * tuple stored in the callsite info structure. We need to make this window
+ * as short as possible since it is blocking GC.
+ */
+ if (cUnit->hasClassLiterals && info->codeAddress) {
+ dvmJitInstallClassObjectPointers(cUnit, (char *) info->codeAddress);
+ }
+
+ /*
+ * Since callsiteinfo is allocated from the arena, delay the reset until
+ * class pointers are resolved.
+ */
+ dvmCompilerArenaReset();
+
+ assert(cUnit->assemblerStatus == kSuccess);
+#if defined(WITH_JIT_TUNING)
+ /* Locate the entry to store compilation statistics for this method */
+ methodStats = dvmCompilerAnalyzeMethodBody(desc->method, false);
+ methodStats->nativeSize += cUnit->totalSize;
+#endif
+ return info->codeAddress != NULL;
+
bail:
/* Retry the original trace with JIT_OPT_NO_LOOP disabled */
dvmCompilerArenaReset();
@@ -1539,6 +1648,10 @@
/* Setup the method */
cUnit.method = desc->method;
+ /* Store the trace descriptor and set the initial mode */
+ cUnit.traceDesc = desc;
+ cUnit.jitMode = kJitTrace;
+
/* Initialize the PC reconstruction list */
dvmInitGrowableList(&cUnit.pcReconstructionList, 8);
@@ -1620,7 +1733,7 @@
}
/* Allocate the entry block */
- curBB = dvmCompilerNewBB(kTraceEntryBlock, numBlocks++);
+ curBB = dvmCompilerNewBB(kEntryBlock, numBlocks++);
dvmInsertGrowableList(blockList, (intptr_t) curBB);
curBB->startOffset = curOffset;
@@ -1714,7 +1827,6 @@
for (blockId = 0; blockId < blockList->numUsed; blockId++) {
curBB = (BasicBlock *) dvmGrowableListGetElement(blockList, blockId);
MIR *lastInsn = curBB->lastMIRInsn;
- BasicBlock *backwardCell;
/* Skip empty blocks */
if (lastInsn == NULL) {
continue;
@@ -1743,7 +1855,6 @@
targetOffset < curOffset &&
(optHints & JIT_OPT_NO_LOOP) == 0) {
dvmCompilerArenaReset();
- /* TODO - constructed loop is abandoned for now */
return compileLoop(&cUnit, startOffset, desc, numMaxInsts,
info, bailPtr, optHints);
}
@@ -1782,46 +1893,6 @@
curBB->needFallThroughBranch =
((flags & (kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
kInstrInvoke)) == 0);
-
- /* Only form a loop if JIT_OPT_NO_LOOP is not set */
- if (curBB->taken == NULL &&
- curBB->fallThrough == NULL &&
- flags == (kInstrCanBranch | kInstrCanContinue) &&
- fallThroughOffset == entryCodeBB->startOffset &&
- JIT_OPT_NO_LOOP != (optHints & JIT_OPT_NO_LOOP)) {
- BasicBlock *loopBranch = curBB;
- BasicBlock *exitBB;
- BasicBlock *exitChainingCell;
-
- if (cUnit.printMe) {
- LOGD("Natural loop detected!");
- }
- exitBB = dvmCompilerNewBB(kTraceExitBlock, numBlocks++);
- dvmInsertGrowableList(blockList, (intptr_t) exitBB);
- exitBB->startOffset = targetOffset;
- exitBB->needFallThroughBranch = true;
-
- loopBranch->taken = exitBB;
- dvmCompilerSetBit(exitBB->predecessors, loopBranch->id);
- backwardCell =
- dvmCompilerNewBB(kChainingCellBackwardBranch, numBlocks++);
- dvmInsertGrowableList(blockList, (intptr_t) backwardCell);
- backwardCell->startOffset = entryCodeBB->startOffset;
- loopBranch->fallThrough = backwardCell;
- dvmCompilerSetBit(backwardCell->predecessors, loopBranch->id);
-
- /* Create the chaining cell as the fallthrough of the exit block */
- exitChainingCell = dvmCompilerNewBB(kChainingCellNormal,
- numBlocks++);
- dvmInsertGrowableList(blockList, (intptr_t) exitChainingCell);
- exitChainingCell->startOffset = targetOffset;
-
- exitBB->fallThrough = exitChainingCell;
- dvmCompilerSetBit(exitChainingCell->predecessors, exitBB->id);
-
- cUnit.hasLoop = true;
- }
-
if (lastInsn->dalvikInsn.opcode == OP_PACKED_SWITCH ||
lastInsn->dalvikInsn.opcode == OP_SPARSE_SWITCH) {
int i;
@@ -1936,6 +2007,7 @@
/* And one final block that publishes the PC and raise the exception */
curBB = dvmCompilerNewBB(kExceptionHandling, numBlocks++);
dvmInsertGrowableList(blockList, (intptr_t) curBB);
+ cUnit.puntBlock = curBB;
if (cUnit.printMe) {
char* signature =
@@ -1953,7 +2025,6 @@
free(signature);
}
- cUnit.traceDesc = desc;
cUnit.numBlocks = numBlocks;
/* Set the instruction set to use (NOTE: later components may change it) */
@@ -1969,26 +2040,7 @@
/* Preparation for SSA conversion */
dvmInitializeSSAConversion(&cUnit);
- if (cUnit.hasLoop) {
- /*
- * Loop is not optimizable (for example lack of a single induction
- * variable), punt and recompile the trace with loop optimization
- * disabled.
- */
- bool loopOpt = dvmCompilerLoopOpt(&cUnit);
- if (loopOpt == false) {
- if (cUnit.printMe) {
- LOGD("Loop is not optimizable - retry codegen");
- }
- /* Reset the compiler resource pool */
- dvmCompilerArenaReset();
- return dvmCompileTrace(desc, cUnit.numInsts, info, bailPtr,
- optHints | JIT_OPT_NO_LOOP);
- }
- }
- else {
- dvmCompilerNonLoopAnalysis(&cUnit);
- }
+ dvmCompilerNonLoopAnalysis(&cUnit);
dvmCompilerInitializeRegAlloc(&cUnit); // Needs to happen after SSA naming
diff --git a/vm/compiler/Loop.c b/vm/compiler/Loop.c
index b9ad3d3..ba8714c 100644
--- a/vm/compiler/Loop.c
+++ b/vm/compiler/Loop.c
@@ -21,89 +21,16 @@
#define DEBUG_LOOP(X)
-/*
- * Given the current simple natural loops, the phi node placement can be
- * determined in the following fashion:
- * entry (B0)
- * +---v v
- * | loop body (B1)
- * | v
- * | loop back (B2)
- * +---+ v
- * exit (B3)
- *
- * 1) Add live-ins of B1 to B0 as defs
- * 2) The intersect of defs(B0)/defs(B1) and defs(B2)/def(B0) are the variables
- * that need PHI nodes in B1.
- */
-static void handlePhiPlacement(CompilationUnit *cUnit)
-{
- BasicBlock *entry =
- (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 0);
- BasicBlock *loopBody =
- (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 1);
- BasicBlock *loopBranch =
- (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 2);
- dvmCopyBitVector(entry->dataFlowInfo->defV,
- loopBody->dataFlowInfo->liveInV);
-
- BitVector *phiV = dvmCompilerAllocBitVector(cUnit->method->registersSize,
- false);
- BitVector *phi2V = dvmCompilerAllocBitVector(cUnit->method->registersSize,
- false);
- dvmIntersectBitVectors(phiV, entry->dataFlowInfo->defV,
- loopBody->dataFlowInfo->defV);
- dvmIntersectBitVectors(phi2V, entry->dataFlowInfo->defV,
- loopBranch->dataFlowInfo->defV);
- dvmUnifyBitVectors(phiV, phiV, phi2V);
-
- /* Insert the PHI MIRs */
- int i;
- for (i = 0; i < cUnit->method->registersSize; i++) {
- if (!dvmIsBitSet(phiV, i)) {
- continue;
- }
- MIR *phi = (MIR *)dvmCompilerNew(sizeof(MIR), true);
- phi->dalvikInsn.opcode = kMirOpPhi;
- phi->dalvikInsn.vA = i;
- dvmCompilerPrependMIR(loopBody, phi);
- }
-}
-
-static void fillPhiNodeContents(CompilationUnit *cUnit)
-{
- BasicBlock *entry =
- (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 0);
- BasicBlock *loopBody =
- (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 1);
- BasicBlock *loopBranch =
- (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 2);
- MIR *mir;
-
- for (mir = loopBody->firstMIRInsn; mir; mir = mir->next) {
- if (mir->dalvikInsn.opcode != kMirOpPhi) break;
- int dalvikReg = mir->dalvikInsn.vA;
-
- mir->ssaRep->numUses = 2;
- mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * 2, false);
- mir->ssaRep->uses[0] =
- DECODE_REG(entry->dataFlowInfo->dalvikToSSAMap[dalvikReg]);
- mir->ssaRep->uses[1] =
- DECODE_REG(loopBranch->dataFlowInfo->dalvikToSSAMap[dalvikReg]);
- }
-
-
-}
-
#if 0
/* Debugging routines */
static void dumpConstants(CompilationUnit *cUnit)
{
int i;
+ LOGE("LOOP starting offset: %x", cUnit->entryBlock->startOffset);
for (i = 0; i < cUnit->numSSARegs; i++) {
if (dvmIsBitSet(cUnit->isConstantV, i)) {
int subNReg = dvmConvertSSARegToDalvik(cUnit, i);
- LOGE("s%d(v%d_%d) has %d", i,
+ LOGE("CONST: s%d(v%d_%d) has %d", i,
DECODE_REG(subNReg), DECODE_SUB(subNReg),
cUnit->constantValues[i]);
}
@@ -114,24 +41,27 @@
{
unsigned int i;
GrowableList *ivList = cUnit->loopAnalysis->ivList;
- int *ssaToDalvikMap = (int *) cUnit->ssaToDalvikMap->elemList;
for (i = 0; i < ivList->numUsed; i++) {
- InductionVariableInfo *ivInfo = ivList->elemList[i];
+ InductionVariableInfo *ivInfo =
+ (InductionVariableInfo *) ivList->elemList[i];
+ int iv = dvmConvertSSARegToDalvik(cUnit, ivInfo->ssaReg);
/* Basic IV */
if (ivInfo->ssaReg == ivInfo->basicSSAReg) {
- LOGE("BIV %d: s%d(v%d) + %d", i,
+ LOGE("BIV %d: s%d(v%d_%d) + %d", i,
ivInfo->ssaReg,
- ssaToDalvikMap[ivInfo->ssaReg] & 0xffff,
+ DECODE_REG(iv), DECODE_SUB(iv),
ivInfo->inc);
/* Dependent IV */
} else {
- LOGE("DIV %d: s%d(v%d) = %d * s%d(v%d) + %d", i,
+ int biv = dvmConvertSSARegToDalvik(cUnit, ivInfo->basicSSAReg);
+
+ LOGE("DIV %d: s%d(v%d_%d) = %d * s%d(v%d_%d) + %d", i,
ivInfo->ssaReg,
- ssaToDalvikMap[ivInfo->ssaReg] & 0xffff,
+ DECODE_REG(iv), DECODE_SUB(iv),
ivInfo->m,
ivInfo->basicSSAReg,
- ssaToDalvikMap[ivInfo->basicSSAReg] & 0xffff,
+ DECODE_REG(biv), DECODE_SUB(biv),
ivInfo->c);
}
}
@@ -162,6 +92,32 @@
#endif
+static BasicBlock *findPredecessorBlock(const CompilationUnit *cUnit,
+ const BasicBlock *bb)
+{
+ int numPred = dvmCountSetBits(bb->predecessors);
+ BitVectorIterator bvIterator;
+ dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+
+ if (numPred == 1) {
+ int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+ return (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+ predIdx);
+ /* First loop block */
+ } else if ((numPred == 2) &&
+ dvmIsBitSet(bb->predecessors, cUnit->entryBlock->id)) {
+ while (true) {
+ int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+ if (predIdx == cUnit->entryBlock->id) continue;
+ return (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+ predIdx);
+ }
+ /* Doesn't support other shape of control flow yet */
+ } else {
+ return NULL;
+ }
+}
+
/*
* A loop is considered optimizable if:
* 1) It has one basic induction variable
@@ -171,11 +127,11 @@
*
* Return false if the loop is not optimizable.
*/
-static bool isLoopOptimizable(CompilationUnit *cUnit)
+static bool isSimpleCountedLoop(CompilationUnit *cUnit)
{
unsigned int i;
- BasicBlock *loopBranch =
- (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 2);
+ BasicBlock *loopBranch = findPredecessorBlock(cUnit,
+ cUnit->entryBlock->fallThrough);
LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
if (loopAnalysis->numBasicIV != 1) return false;
@@ -310,8 +266,7 @@
/* Returns true if the loop body cannot throw any exceptions */
static bool doLoopBodyCodeMotion(CompilationUnit *cUnit)
{
- BasicBlock *loopBody =
- (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 1);
+ BasicBlock *loopBody = cUnit->entryBlock->fallThrough;
MIR *mir;
bool loopBodyCanThrow = false;
@@ -385,7 +340,7 @@
if (dvmIsBitSet(cUnit->loopAnalysis->isIndVarV,
mir->ssaRep->uses[useIdx])) {
mir->OptimizationFlags |=
- MIR_IGNORE_RANGE_CHECK | MIR_IGNORE_NULL_CHECK;
+ MIR_IGNORE_RANGE_CHECK | MIR_IGNORE_NULL_CHECK;
updateRangeCheckInfo(cUnit, mir->ssaRep->uses[refIdx],
mir->ssaRep->uses[useIdx]);
}
@@ -398,8 +353,7 @@
static void genHoistedChecks(CompilationUnit *cUnit)
{
unsigned int i;
- BasicBlock *entry =
- (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 0);
+ BasicBlock *entry = cUnit->entryBlock;
LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
int globalMaxC = 0;
int globalMinC = 0;
@@ -460,14 +414,16 @@
} else if (loopAnalysis->loopBranchOpcode == OP_IF_LTZ) {
/* Array index will fall below 0 */
if (globalMinC < 0) {
- MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+ MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR),
+ true);
boundCheckMIR->dalvikInsn.opcode = kMirOpPunt;
dvmCompilerAppendMIR(entry, boundCheckMIR);
}
} else if (loopAnalysis->loopBranchOpcode == OP_IF_LEZ) {
/* Array index will fall below 0 */
if (globalMinC < -1) {
- MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+ MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR),
+ true);
boundCheckMIR->dalvikInsn.opcode = kMirOpPunt;
dvmCompilerAppendMIR(entry, boundCheckMIR);
}
@@ -480,79 +436,6 @@
}
}
-/*
- * Main entry point to do loop optimization.
- * Return false if sanity checks for loop formation/optimization failed.
- */
-bool dvmCompilerLoopOpt(CompilationUnit *cUnit)
-{
- LoopAnalysis *loopAnalysis =
- (LoopAnalysis *)dvmCompilerNew(sizeof(LoopAnalysis), true);
-
- assert(((BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 0))
- ->blockType == kTraceEntryBlock);
- assert(((BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 2))
- ->blockType == kDalvikByteCode);
- assert(((BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 3))
- ->blockType == kTraceExitBlock);
-
- cUnit->loopAnalysis = loopAnalysis;
- /*
- * Find live-in variables to the loop body so that we can fake their
- * definitions in the entry block.
- */
- dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindLocalLiveIn,
- kAllNodes,
- false /* isIterative */);
-
- /* Insert phi nodes to the loop body */
- handlePhiPlacement(cUnit);
-
- dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
- kAllNodes,
- false /* isIterative */);
- fillPhiNodeContents(cUnit);
-
- /* Constant propagation */
- cUnit->isConstantV = dvmAllocBitVector(cUnit->numSSARegs, false);
- cUnit->constantValues =
- (int *)dvmCompilerNew(sizeof(int) * cUnit->numSSARegs,
- true);
- dvmCompilerDataFlowAnalysisDispatcher(cUnit,
- dvmCompilerDoConstantPropagation,
- kAllNodes,
- false /* isIterative */);
- DEBUG_LOOP(dumpConstants(cUnit);)
-
- /* Find induction variables - basic and dependent */
- loopAnalysis->ivList =
- (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
- dvmInitGrowableList(loopAnalysis->ivList, 4);
- loopAnalysis->isIndVarV = dvmAllocBitVector(cUnit->numSSARegs, false);
- dvmCompilerDataFlowAnalysisDispatcher(cUnit,
- dvmCompilerFindInductionVariables,
- kAllNodes,
- false /* isIterative */);
- DEBUG_LOOP(dumpIVList(cUnit);)
-
- /* If the loop turns out to be non-optimizable, return early */
- if (!isLoopOptimizable(cUnit))
- return false;
-
- loopAnalysis->arrayAccessInfo =
- (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
- dvmInitGrowableList(loopAnalysis->arrayAccessInfo, 4);
- loopAnalysis->bodyIsClean = doLoopBodyCodeMotion(cUnit);
- DEBUG_LOOP(dumpHoistedChecks(cUnit);)
-
- /*
- * Convert the array access information into extended MIR code in the loop
- * header.
- */
- genHoistedChecks(cUnit);
- return true;
-}
-
void resetBlockEdges(BasicBlock *bb)
{
bb->taken = NULL;
@@ -573,53 +456,72 @@
int numPred = dvmCountSetBits(firstBB->predecessors);
/*
- * A loop body should have at least two incoming edges. Here we go with the
- * simple case and only form loops if numPred == 2.
+ * A loop body should have at least two incoming edges.
*/
- if (numPred != 2) return false;
+ if (numPred < 2) return false;
- BitVectorIterator bvIterator;
GrowableList *blockList = &cUnit->blockList;
- BasicBlock *predBB = NULL;
- dvmBitVectorIteratorInit(firstBB->predecessors, &bvIterator);
- while (true) {
- int predIdx = dvmBitVectorIteratorNext(&bvIterator);
- if (predIdx == -1) break;
- predBB = (BasicBlock *) dvmGrowableListGetElement(blockList, predIdx);
- if (predBB != cUnit->entryBlock) break;
- }
-
- /* Used to record which block is in the loop */
+ /* Record blocks included in the loop */
dvmClearAllBits(cUnit->tempBlockV);
- dvmCompilerSetBit(cUnit->tempBlockV, predBB->id);
+ dvmCompilerSetBit(cUnit->tempBlockV, cUnit->entryBlock->id);
+ dvmCompilerSetBit(cUnit->tempBlockV, firstBB->id);
- /* Form a loop by only including iDom block that is also a predecessor */
- while (predBB != firstBB) {
- BasicBlock *iDom = predBB->iDom;
- if (!dvmIsBitSet(predBB->predecessors, iDom->id)) {
- return false;
- /*
- * And don't form nested loops (ie by detecting if the branch target
- * of iDom dominates iDom).
- */
- } else if (iDom->taken &&
- dvmIsBitSet(iDom->dominators, iDom->taken->id) &&
- iDom != firstBB) {
+ BasicBlock *bodyBB = firstBB;
+
+ /*
+ * First try to include the fall-through block in the loop, then the taken
+ * block. Stop loop formation on the first backward branch that enters the
+ * first block (ie only include the inner-most loop).
+ */
+ while (true) {
+ /* Loop formed */
+ if (bodyBB->taken == firstBB || bodyBB->fallThrough == firstBB) break;
+
+ /* Inner loops formed first - quit */
+ if (bodyBB->fallThrough &&
+ dvmIsBitSet(cUnit->tempBlockV, bodyBB->fallThrough->id)) {
return false;
}
- dvmCompilerSetBit(cUnit->tempBlockV, iDom->id);
- predBB = iDom;
+ if (bodyBB->taken &&
+ dvmIsBitSet(cUnit->tempBlockV, bodyBB->taken->id)) {
+ return false;
+ }
+
+ if (bodyBB->fallThrough) {
+ if (bodyBB->fallThrough->iDom == bodyBB) {
+ bodyBB = bodyBB->fallThrough;
+ dvmCompilerSetBit(cUnit->tempBlockV, bodyBB->id);
+ /*
+ * Loop formation to be detected at the beginning of next
+ * iteration.
+ */
+ continue;
+ }
+ }
+ if (bodyBB->taken) {
+ if (bodyBB->taken->iDom == bodyBB) {
+ bodyBB = bodyBB->taken;
+ dvmCompilerSetBit(cUnit->tempBlockV, bodyBB->id);
+ /*
+ * Loop formation to be detected at the beginning of next
+ * iteration.
+ */
+ continue;
+ }
+ }
+ /*
+ * Current block is not the immediate dominator of either fallthrough
+ * nor taken block - bail out of loop formation.
+ */
+ return false;
}
- /* Add the entry block and first block */
- dvmCompilerSetBit(cUnit->tempBlockV, firstBB->id);
- dvmCompilerSetBit(cUnit->tempBlockV, cUnit->entryBlock->id);
/* Now mark blocks not included in the loop as hidden */
GrowableListIterator iterator;
- dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+ dvmGrowableListIteratorInit(blockList, &iterator);
while (true) {
BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
if (bb == NULL) break;
@@ -634,7 +536,7 @@
dvmCompilerDataFlowAnalysisDispatcher(cUnit, clearPredecessorVector,
kAllNodes, false /* isIterative */);
- dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+ dvmGrowableListIteratorInit(blockList, &iterator);
while (true) {
BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
if (bb == NULL) break;
@@ -671,6 +573,89 @@
assert(bb->successorBlockList.blockListType == kNotUsed);
}
}
-
return true;
}
+
+/*
+ * Main entry point to do loop optimization.
+ * Return false if sanity checks for loop formation/optimization failed.
+ */
+bool dvmCompilerLoopOpt(CompilationUnit *cUnit)
+{
+ LoopAnalysis *loopAnalysis =
+ (LoopAnalysis *)dvmCompilerNew(sizeof(LoopAnalysis), true);
+ cUnit->loopAnalysis = loopAnalysis;
+
+ /* Constant propagation */
+ cUnit->isConstantV = dvmAllocBitVector(cUnit->numSSARegs, false);
+ cUnit->constantValues =
+ (int *)dvmCompilerNew(sizeof(int) * cUnit->numSSARegs,
+ true);
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+ dvmCompilerDoConstantPropagation,
+ kAllNodes,
+ false /* isIterative */);
+ DEBUG_LOOP(dumpConstants(cUnit);)
+
+ /* Find induction variables - basic and dependent */
+ loopAnalysis->ivList =
+ (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
+ dvmInitGrowableList(loopAnalysis->ivList, 4);
+ loopAnalysis->isIndVarV = dvmAllocBitVector(cUnit->numSSARegs, false);
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+ dvmCompilerFindInductionVariables,
+ kAllNodes,
+ false /* isIterative */);
+ DEBUG_LOOP(dumpIVList(cUnit);)
+
+ /* Only optimize array accesses for simple counted loop for now */
+ if (!isSimpleCountedLoop(cUnit))
+ return false;
+
+ loopAnalysis->arrayAccessInfo =
+ (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
+ dvmInitGrowableList(loopAnalysis->arrayAccessInfo, 4);
+ loopAnalysis->bodyIsClean = doLoopBodyCodeMotion(cUnit);
+ DEBUG_LOOP(dumpHoistedChecks(cUnit);)
+
+ /*
+ * Convert the array access information into extended MIR code in the loop
+ * header.
+ */
+ genHoistedChecks(cUnit);
+ return true;
+}
+
+/*
+ * Select the target block of the backward branch.
+ */
+void dvmCompilerInsertBackwardChaining(CompilationUnit *cUnit)
+{
+ /*
+ * If we are not in self-verification or profiling mode, the backward
+ * branch can go to the entryBlock->fallThrough directly. Suspend polling
+ * code will be generated along the backward branch to honor the suspend
+ * requests.
+ */
+#if !defined(WITH_SELF_VERIFICATION)
+ if (gDvmJit.profileMode != kTraceProfilingContinuous &&
+ gDvmJit.profileMode != kTraceProfilingPeriodicOn) {
+ return;
+ }
+#endif
+ /*
+ * In self-verification or profiling mode, the backward branch is altered
+ * to go to the backward chaining cell. Without using the backward chaining
+ * cell we won't be able to do check-pointing on the target PC, or count the
+ * number of iterations accurately.
+ */
+ BasicBlock *firstBB = cUnit->entryBlock->fallThrough;
+ BasicBlock *backBranchBB = findPredecessorBlock(cUnit, firstBB);
+ if (backBranchBB->taken == firstBB) {
+ backBranchBB->taken = cUnit->backChainBlock;
+ } else {
+ assert(backBranchBB->fallThrough == firstBB);
+ backBranchBB->fallThrough = cUnit->backChainBlock;
+ }
+ cUnit->backChainBlock->startOffset = firstBB->startOffset;
+}
diff --git a/vm/compiler/Loop.h b/vm/compiler/Loop.h
index ec87e57..122817d 100644
--- a/vm/compiler/Loop.h
+++ b/vm/compiler/Loop.h
@@ -36,4 +36,13 @@
bool dvmCompilerFilterLoopBlocks(CompilationUnit *cUnit);
+/*
+ * An unexecuted code path may contain unresolved fields or classes. Before we
+ * have a quiet resolver we simply bail out of the loop compilation mode.
+ */
+#define BAIL_LOOP_COMPILATION() if (cUnit->jitMode == kJitLoop) { \
+ cUnit->quitLoopMode = true; \
+ return false; \
+ }
+
#endif /* _DALVIK_VM_LOOP */
diff --git a/vm/compiler/Ralloc.c b/vm/compiler/Ralloc.c
index d772a31..e2752b1 100644
--- a/vm/compiler/Ralloc.c
+++ b/vm/compiler/Ralloc.c
@@ -27,8 +27,7 @@
static void inferTypes(CompilationUnit *cUnit, BasicBlock *bb)
{
MIR *mir;
- if (bb->blockType != kDalvikByteCode &&
- bb->blockType != kTraceEntryBlock)
+ if (bb->blockType != kDalvikByteCode && bb->blockType != kEntryBlock)
return;
for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
diff --git a/vm/compiler/SSATransformation.c b/vm/compiler/SSATransformation.c
index b045a1e..6d7dd23 100644
--- a/vm/compiler/SSATransformation.c
+++ b/vm/compiler/SSATransformation.c
@@ -633,5 +633,9 @@
kReachableNodes,
false /* isIterative */);
+ if (gDvmJit.receivedSIGUSR2 || gDvmJit.printMe) {
+ dvmDumpCFG(cUnit, "/sdcard/cfg/");
+ }
+
return true;
}
diff --git a/vm/compiler/Utility.c b/vm/compiler/Utility.c
index fb16388..2599e0a 100644
--- a/vm/compiler/Utility.c
+++ b/vm/compiler/Utility.c
@@ -165,11 +165,9 @@
"Backward Branch",
"Chaining Cell Gap",
"N/A",
- "Method Entry Block",
- "Trace Entry Block",
+ "Entry Block",
"Code Block",
- "Trace Exit Block",
- "Method Exit Block",
+ "Exit Block",
"PC Reconstruction",
"Exception Handling",
};
@@ -389,10 +387,10 @@
void dvmGetBlockName(BasicBlock *bb, char *name)
{
switch (bb->blockType) {
- case kMethodEntryBlock:
+ case kEntryBlock:
snprintf(name, BLOCK_NAME_LEN, "entry");
break;
- case kMethodExitBlock:
+ case kExitBlock:
snprintf(name, BLOCK_NAME_LEN, "exit");
break;
case kDalvikByteCode:
diff --git a/vm/compiler/codegen/arm/ArchUtility.c b/vm/compiler/codegen/arm/ArchUtility.c
index fb28e26..edcbf86 100644
--- a/vm/compiler/codegen/arm/ArchUtility.c
+++ b/vm/compiler/codegen/arm/ArchUtility.c
@@ -316,20 +316,25 @@
DUMP_SSA_REP(LOGD("-------- %s\n", (char *) dest));
break;
case kArmPseudoChainingCellBackwardBranch:
+ LOGD("L%p:\n", lir);
LOGD("-------- chaining cell (backward branch): 0x%04x\n", dest);
break;
case kArmPseudoChainingCellNormal:
+ LOGD("L%p:\n", lir);
LOGD("-------- chaining cell (normal): 0x%04x\n", dest);
break;
case kArmPseudoChainingCellHot:
+ LOGD("L%p:\n", lir);
LOGD("-------- chaining cell (hot): 0x%04x\n", dest);
break;
case kArmPseudoChainingCellInvokePredicted:
+ LOGD("L%p:\n", lir);
LOGD("-------- chaining cell (predicted): %s%s\n",
dest ? ((Method *) dest)->clazz->descriptor : "",
dest ? ((Method *) dest)->name : "N/A");
break;
case kArmPseudoChainingCellInvokeSingleton:
+ LOGD("L%p:\n", lir);
LOGD("-------- chaining cell (invoke singleton): %s%s/%p\n",
((Method *)dest)->clazz->descriptor,
((Method *)dest)->name,
diff --git a/vm/compiler/codegen/arm/Assemble.c b/vm/compiler/codegen/arm/Assemble.c
index 0e919c4..dfc68b6 100644
--- a/vm/compiler/codegen/arm/Assemble.c
+++ b/vm/compiler/codegen/arm/Assemble.c
@@ -1001,6 +1001,11 @@
lir->operands[1] = 0;
lir->generic.target = 0;
dvmCompilerSetupResourceMasks(lir);
+ if (cUnit->printMe) {
+ LOGD("kThumb2Cbnz/kThumb2Cbz@%x: delta=%d",
+ lir->generic.offset, delta);
+ dvmCompilerCodegenDump(cUnit);
+ }
return kRetryAll;
} else {
lir->operands[1] = delta >> 1;
diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c
index 8d8619b..f020e9c 100644
--- a/vm/compiler/codegen/arm/CodegenDriver.c
+++ b/vm/compiler/codegen/arm/CodegenDriver.c
@@ -1356,6 +1356,12 @@
int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
kInstrCanThrow;
+ // Single stepping is considered loop mode breaker
+ if (cUnit->jitMode == kJitLoop) {
+ cUnit->quitLoopMode = true;
+ return;
+ }
+
//If already optimized out, just ignore
if (mir->dalvikInsn.opcode == OP_NOP)
return;
@@ -1448,7 +1454,8 @@
/* backward branch? */
bool backwardBranch = (bb->taken->startOffset <= mir->offset);
- if (backwardBranch && gDvmJit.genSuspendPoll) {
+ if (backwardBranch &&
+ (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
genSuspendPoll(cUnit, mir);
}
@@ -1579,6 +1586,7 @@
(cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]);
if (strPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
LOGE("Unexpected null string");
dvmAbort();
}
@@ -1595,6 +1603,7 @@
(cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
if (classPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
LOGE("Unexpected null class");
dvmAbort();
}
@@ -1631,6 +1640,7 @@
Opcode opcode = mir->dalvikInsn.opcode;
if (fieldPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
LOGE("Unexpected null static field");
dvmAbort();
}
@@ -1664,6 +1674,7 @@
(method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
if (fieldPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
LOGE("Unexpected null static field");
dvmAbort();
}
@@ -1707,6 +1718,12 @@
(method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
Opcode opcode = mir->dalvikInsn.opcode;
+ if (fieldPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
+ LOGE("Unexpected null static field");
+ dvmAbort();
+ }
+
isVolatile = (opcode == OP_SPUT_VOLATILE) ||
(opcode == OP_SPUT_VOLATILE_JUMBO) ||
(opcode == OP_SPUT_OBJECT_VOLATILE) ||
@@ -1718,11 +1735,6 @@
(opcode == OP_SPUT_OBJECT_VOLATILE) ||
(opcode == OP_SPUT_OBJECT_VOLATILE_JUMBO);
- if (fieldPtr == NULL) {
- LOGE("Unexpected null static field");
- dvmAbort();
- }
-
rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
loadConstant(cUnit, tReg, (int) fieldPtr);
@@ -1755,6 +1767,7 @@
(method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
if (fieldPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
LOGE("Unexpected null static field");
dvmAbort();
}
@@ -1778,6 +1791,7 @@
(cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
if (classPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
LOGE("Unexpected null class");
dvmAbort();
}
@@ -1829,9 +1843,10 @@
* so that we can tell if it happens frequently.
*/
if (classPtr == NULL) {
- LOGVV("null clazz in OP_CHECK_CAST, single-stepping");
- genInterpSingleStep(cUnit, mir);
- return false;
+ BAIL_LOOP_COMPILATION();
+ LOGVV("null clazz in OP_CHECK_CAST, single-stepping");
+ genInterpSingleStep(cUnit, mir);
+ return false;
}
dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */
loadConstant(cUnit, r1, (int) classPtr );
@@ -2093,7 +2108,8 @@
/* backward branch? */
bool backwardBranch = (bb->taken->startOffset <= mir->offset);
- if (backwardBranch && gDvmJit.genSuspendPoll) {
+ if (backwardBranch &&
+ (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
genSuspendPoll(cUnit, mir);
}
@@ -2426,6 +2442,7 @@
method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC];
if (fieldPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
LOGE("Unexpected null instance field");
dvmAbort();
}
@@ -2448,6 +2465,7 @@
(cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
if (classPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
LOGE("Unexpected null class");
dvmAbort();
}
@@ -2499,6 +2517,7 @@
* so that we can tell if it happens frequently.
*/
if (classPtr == NULL) {
+ BAIL_LOOP_COMPILATION();
LOGD("null clazz in OP_INSTANCE_OF, single-stepping");
genInterpSingleStep(cUnit, mir);
break;
@@ -2627,7 +2646,8 @@
/* backward branch? */
bool backwardBranch = (bb->taken->startOffset <= mir->offset);
- if (backwardBranch && gDvmJit.genSuspendPoll) {
+ if (backwardBranch &&
+ (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
genSuspendPoll(cUnit, mir);
}
@@ -4238,7 +4258,7 @@
dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
}
- if (bb->blockType == kTraceEntryBlock) {
+ if (bb->blockType == kEntryBlock) {
labelList[i].opcode = kArmPseudoEntryBlock;
if (bb->firstMIRInsn == NULL) {
continue;
@@ -4246,10 +4266,11 @@
setupLoopEntryBlock(cUnit, bb,
&labelList[bb->fallThrough->id]);
}
- } else if (bb->blockType == kTraceExitBlock) {
+ } else if (bb->blockType == kExitBlock) {
labelList[i].opcode = kArmPseudoExitBlock;
goto gen_fallthrough;
} else if (bb->blockType == kDalvikByteCode) {
+ if (bb->hidden == true) continue;
labelList[i].opcode = kArmPseudoNormalBlockLabel;
/* Reset the register state */
dvmCompilerResetRegPool(cUnit);
@@ -4297,8 +4318,8 @@
/* Make sure exception handling block is next */
labelList[i].opcode =
kArmPseudoPCReconstructionBlockLabel;
- assert (i == cUnit->numBlocks - 2);
- handlePCReconstruction(cUnit, &labelList[i+1]);
+ handlePCReconstruction(cUnit,
+ &labelList[cUnit->puntBlock->id]);
break;
case kExceptionHandling:
labelList[i].opcode = kArmPseudoEHBlockLabel;
@@ -4510,7 +4531,7 @@
}
}
- if (bb->blockType == kTraceEntryBlock) {
+ if (bb->blockType == kEntryBlock) {
dvmCompilerAppendLIR(cUnit,
(LIR *) cUnit->loopAnalysis->branchToBody);
dvmCompilerAppendLIR(cUnit,
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.c b/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.c
index 5a08b60..98d97d8 100644
--- a/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.c
+++ b/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.c
@@ -255,13 +255,13 @@
ArmLIR *headLIR = NULL;
- if (bb->blockType == kMethodEntryBlock) {
+ if (bb->blockType == kEntryBlock) {
/* r0 = callsitePC */
opImm(cUnit, kOpPush, (1 << r0 | 1 << r1 | 1 << r5FP | 1 << r14lr));
opRegImm(cUnit, kOpSub, r5FP,
sizeof(StackSaveArea) + cUnit->method->registersSize * 4);
- } else if (bb->blockType == kMethodExitBlock) {
+ } else if (bb->blockType == kExitBlock) {
/* No need to pop r0 and r1 */
opRegImm(cUnit, kOpAdd, r13sp, 8);
opImm(cUnit, kOpPop, (1 << r5FP | 1 << r15pc));
diff --git a/vm/hprof/Hprof.h b/vm/hprof/Hprof.h
index c79a3df..c62dd9c 100644
--- a/vm/hprof/Hprof.h
+++ b/vm/hprof/Hprof.h
@@ -84,12 +84,12 @@
/* Android */
HPROF_HEAP_DUMP_INFO = 0xfe,
HPROF_ROOT_INTERNED_STRING = 0x89,
- HPROF_ROOT_FINALIZING = 0x8a,
+ HPROF_ROOT_FINALIZING = 0x8a, /* obsolete */
HPROF_ROOT_DEBUGGER = 0x8b,
- HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,
+ HPROF_ROOT_REFERENCE_CLEANUP = 0x8c, /* obsolete */
HPROF_ROOT_VM_INTERNAL = 0x8d,
HPROF_ROOT_JNI_MONITOR = 0x8e,
- HPROF_UNREACHABLE = 0x90, /* deprecated */
+ HPROF_UNREACHABLE = 0x90, /* obsolete */
HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
} hprof_heap_tag_t;