diff options
Diffstat (limited to 'vm')
102 files changed, 3635 insertions, 1639 deletions
diff --git a/vm/CheckJni.c b/vm/CheckJni.c index 3c87eaf99..ae98c4c1b 100644 --- a/vm/CheckJni.c +++ b/vm/CheckJni.c @@ -251,13 +251,18 @@ void dvmCheckCallJNIMethod_staticNoRef(const u4* args, JValue* pResult, checkNonNull(_env, _ptr, __FUNCTION__) #define CHECK_SIG(_env, _methid, _sigbyte, _isstatic) \ checkSig(_env, _methid, _sigbyte, _isstatic, __FUNCTION__) +#define CHECK_VIRTUAL_METHOD(_env, _obj, _methid) \ + checkVirtualMethod(_env, _obj, _methid, __FUNCTION__) +#define CHECK_STATIC_METHOD(_env, _clazz, _methid) \ + checkStaticMethod(_env, _clazz, _methid, __FUNCTION__) #define CHECK_METHOD_ARGS_A(_env, _methid, _args) \ checkMethodArgsA(_env, _methid, _args, __FUNCTION__) #define CHECK_METHOD_ARGS_V(_env, _methid, _args) \ checkMethodArgsV(_env, _methid, _args, __FUNCTION__) /* - * Print trace message when both "checkJNI" and "verbose:jni" are enabled. + * Prints trace messages when a native method calls a JNI function such as + * NewByteArray. Enabled if both "-Xcheck:jni" and "-verbose:jni" are enabled. */ #define JNI_TRACE(_entry, _hasmeth) \ do { \ @@ -857,6 +862,56 @@ bail: } /* + * Verify that "methodID" is appropriate for "jobj". + * + * Make sure the object is an instance of the method's declaring class. + * (Note the methodID might point to a declaration in an interface; this + * will be handled automatically by the instanceof check.) + */ +static void checkVirtualMethod(JNIEnv* env, jobject jobj, jmethodID methodID, + const char* func) +{ + JNI_ENTER(); + + Object* obj = dvmDecodeIndirectRef(env, jobj); + const Method* meth = (const Method*) methodID; + + if (!dvmInstanceof(obj->clazz, meth->clazz)) { + LOGW("JNI WARNING: can't call %s.%s on instance of %s\n", + meth->clazz->descriptor, meth->name, obj->clazz->descriptor); + abortMaybe(); + } + + JNI_EXIT(); +} + +/* + * Verify that "methodID" is appropriate for "clazz". + * + * A mismatch isn't dangerous, because the method defines the class. In + * fact, jclazz is unused in the implementation. It's best if we don't + * allow bad code in the system though. + * + * Instances of "jclazz" must be instances of the method's declaring class. + */ +static void checkStaticMethod(JNIEnv* env, jclass jclazz, jmethodID methodID, + const char* func) +{ + JNI_ENTER(); + + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + const Method* meth = (const Method*) methodID; + + if (!dvmInstanceof(clazz, meth->clazz)) { + LOGW("JNI WARNING: can't call static %s.%s on class %s\n", + meth->clazz->descriptor, meth->name, clazz->descriptor); + // no abort + } + + JNI_EXIT(); +} + +/* * Verify that the reference arguments being passed in are appropriate for * this method. * @@ -1696,6 +1751,7 @@ SET_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE); CHECK_ENTER(env, kFlag_Default); \ CHECK_OBJECT(env, obj); \ CHECK_SIG(env, methodID, _retsig, false); \ + CHECK_VIRTUAL_METHOD(env, obj, methodID); \ _retdecl; \ va_list args, tmpArgs; \ va_start(args, methodID); \ @@ -1714,6 +1770,7 @@ SET_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE); CHECK_ENTER(env, kFlag_Default); \ CHECK_OBJECT(env, obj); \ CHECK_SIG(env, methodID, _retsig, false); \ + CHECK_VIRTUAL_METHOD(env, obj, methodID); \ _retdecl; \ va_list tmpArgs; \ va_copy(tmpArgs, args); \ @@ -1730,6 +1787,7 @@ SET_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE); CHECK_ENTER(env, kFlag_Default); \ CHECK_OBJECT(env, obj); \ CHECK_SIG(env, methodID, _retsig, false); \ + CHECK_VIRTUAL_METHOD(env, obj, methodID); \ _retdecl; \ CHECK_METHOD_ARGS_A(env, methodID, args); \ _retasgn BASE_ENV(env)->Call##_jname##MethodA(env, obj, methodID, \ @@ -1757,6 +1815,7 @@ CALL_VIRTUAL(void, Void, , , , 'V'); CHECK_CLASS(env, clazz); \ CHECK_OBJECT(env, obj); \ CHECK_SIG(env, methodID, _retsig, false); \ + CHECK_VIRTUAL_METHOD(env, obj, methodID); \ _retdecl; \ va_list args, tmpArgs; \ va_start(args, methodID); \ @@ -1776,6 +1835,7 @@ CALL_VIRTUAL(void, Void, , , , 'V'); CHECK_CLASS(env, clazz); \ CHECK_OBJECT(env, obj); \ CHECK_SIG(env, methodID, _retsig, false); \ + CHECK_VIRTUAL_METHOD(env, obj, methodID); \ _retdecl; \ va_list tmpArgs; \ va_copy(tmpArgs, args); \ @@ -1793,6 +1853,7 @@ CALL_VIRTUAL(void, Void, , , , 'V'); CHECK_CLASS(env, clazz); \ CHECK_OBJECT(env, obj); \ CHECK_SIG(env, methodID, _retsig, false); \ + CHECK_VIRTUAL_METHOD(env, obj, methodID); \ _retdecl; \ CHECK_METHOD_ARGS_A(env, methodID, args); \ _retasgn BASE_ENV(env)->CallNonvirtual##_jname##MethodA(env, obj, \ @@ -1819,6 +1880,7 @@ CALL_NONVIRTUAL(void, Void, , , , 'V'); CHECK_ENTER(env, kFlag_Default); \ CHECK_CLASS(env, clazz); \ CHECK_SIG(env, methodID, _retsig, true); \ + CHECK_STATIC_METHOD(env, clazz, methodID); \ _retdecl; \ va_list args, tmpArgs; \ va_start(args, methodID); \ @@ -1837,6 +1899,7 @@ CALL_NONVIRTUAL(void, Void, , , , 'V'); CHECK_ENTER(env, kFlag_Default); \ CHECK_CLASS(env, clazz); \ CHECK_SIG(env, methodID, _retsig, true); \ + CHECK_STATIC_METHOD(env, clazz, methodID); \ _retdecl; \ va_list tmpArgs; \ va_copy(tmpArgs, args); \ @@ -1853,6 +1916,7 @@ CALL_NONVIRTUAL(void, Void, , , , 'V'); CHECK_ENTER(env, kFlag_Default); \ CHECK_CLASS(env, clazz); \ CHECK_SIG(env, methodID, _retsig, true); \ + CHECK_STATIC_METHOD(env, clazz, methodID); \ _retdecl; \ CHECK_METHOD_ARGS_A(env, methodID, args); \ _retasgn BASE_ENV(env)->CallStatic##_jname##MethodA(env, clazz, \ diff --git a/vm/Common.h b/vm/Common.h index d0c021d50..2f151b8fe 100644 --- a/vm/Common.h +++ b/vm/Common.h @@ -34,6 +34,8 @@ __FILE__, __LINE__, #x), *(int*)39=39, 0) ) #endif +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#define MAX(x,y) (((x) > (y)) ? (x) : (y)) /* * If "very verbose" logging is enabled, make it equivalent to LOGV. diff --git a/vm/Dalvik.h b/vm/Dalvik.h index 5d8444047..6418bf2c7 100644 --- a/vm/Dalvik.h +++ b/vm/Dalvik.h @@ -55,6 +55,7 @@ #include "oo/Array.h" #include "Exception.h" #include "alloc/Alloc.h" +#include "alloc/CardTable.h" #include "alloc/HeapDebug.h" #include "alloc/HeapWorker.h" #include "alloc/GC.h" diff --git a/vm/Debugger.c b/vm/Debugger.c index dc54d76b8..94029114b 100644 --- a/vm/Debugger.c +++ b/vm/Debugger.c @@ -1169,6 +1169,20 @@ ObjectId dvmDbgCreateObject(RefTypeId classId) } /* + * Allocate a new array object of the specified type and length. The + * type is the array type, not the element type. + * + * Add it to the registry to prevent it from being GCed. + */ +ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length) +{ + ClassObject* clazz = refTypeIdToClassObject(arrayTypeId); + Object* newObj = (Object*) dvmAllocArrayByClass(clazz, length, ALLOC_DEFAULT); + dvmReleaseTrackedAlloc(newObj, NULL); + return objectToObjectId(newObj); +} + +/* * Determine if "instClassId" is an instance of "classId". */ bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId) diff --git a/vm/Debugger.h b/vm/Debugger.h index 04477fb52..d7221604f 100644 --- a/vm/Debugger.h +++ b/vm/Debugger.h @@ -191,6 +191,7 @@ bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count, ObjectId dvmDbgCreateString(const char* str); ObjectId dvmDbgCreateObject(RefTypeId classId); +ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length); bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId); @@ -32,6 +32,7 @@ LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter # Optional features. These may impact the size or performance of the VM. # LOCAL_CFLAGS += -DWITH_PROFILER -DWITH_DEBUGGER +#LOCAL_CFLAGS += -DWITH_JNI_TRACE # 0=full cache, 1/2=reduced, 3=no cache LOCAL_CFLAGS += -DDVM_RESOLVER_CACHE=0 @@ -130,6 +131,7 @@ LOCAL_SRC_FILES := \ UtfString.c \ alloc/clz.c.arm \ alloc/Alloc.c \ + alloc/CardTable.c \ alloc/HeapBitmap.c.arm \ alloc/HeapDebug.c \ alloc/HeapTable.c \ diff --git a/vm/DvmDex.c b/vm/DvmDex.c index aefdb8124..02dd758a4 100644 --- a/vm/DvmDex.c +++ b/vm/DvmDex.c @@ -227,10 +227,14 @@ void dvmDexFileFree(DvmDex* pDvmDex) * This requires changing the access permissions to read-write, updating * the value, and then resetting the permissions. * - * This does not make any synchronization guarantees. It's important for the - * caller(s) to work out mutual exclusion, at least on a page granularity, - * to avoid a race where one threads sets read-write, another thread sets - * read-only, and then the first thread does a write. + * We need to ensure mutual exclusion at a page granularity to avoid a race + * where one threads sets read-write, another thread sets read-only, and + * then the first thread does a write. Since we don't do a lot of updates, + * and the window is small, we just use a lock across the entire DvmDex. + * We're only trying to make the page state change atomic; it's up to the + * caller to ensure that multiple threads aren't stomping on the same + * location (e.g. breakpoints and verifier/optimizer changes happening + * simultaneously). * * TODO: if we're back to the original state of the page, use * madvise(MADV_DONTNEED) to release the private/dirty copy. @@ -244,6 +248,12 @@ bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal) return true; } + /* + * We're not holding this for long, so we don't bother with switching + * to VMWAIT. + */ + dvmLockMutex(&pDvmDex->modLock); + LOGV("+++ change byte at %p from 0x%02x to 0x%02x\n", addr, *addr, newVal); if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) { LOGD("NOTE: DEX page access change (->RW) failed\n"); @@ -257,6 +267,8 @@ bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal) /* expected on files mounted from FAT; keep going */ } + dvmUnlockMutex(&pDvmDex->modLock); + return true; } @@ -273,6 +285,12 @@ bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal) return true; } + /* + * We're not holding this for long, so we don't bother with switching + * to VMWAIT. + */ + dvmLockMutex(&pDvmDex->modLock); + LOGV("+++ change 2byte at %p from 0x%04x to 0x%04x\n", addr, *addr, newVal); if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) { LOGD("NOTE: DEX page access change (->RW) failed\n"); @@ -286,5 +304,7 @@ bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal) /* expected on files mounted from FAT; keep going */ } + dvmUnlockMutex(&pDvmDex->modLock); + return true; } diff --git a/vm/DvmDex.h b/vm/DvmDex.h index 9f3903aee..803be1f00 100644 --- a/vm/DvmDex.h +++ b/vm/DvmDex.h @@ -59,6 +59,9 @@ typedef struct DvmDex { /* shared memory region with file contents */ MemMapping memMap; + + /* lock ensuring mutual exclusion during updates */ + pthread_mutex_t modLock; } DvmDex; diff --git a/vm/Exception.c b/vm/Exception.c index fb3b9f8c1..3a73420db 100644 --- a/vm/Exception.c +++ b/vm/Exception.c @@ -744,7 +744,7 @@ void dvmPrintExceptionStackTrace(void) } if (self->exception != NULL) { - LOGI("NOTE: exception thrown while printing stack trace: %s\n", + LOGW("NOTE: exception thrown while printing stack trace: %s\n", self->exception->clazz->descriptor); } diff --git a/vm/Globals.h b/vm/Globals.h index 30df209a0..7bed2e3df 100644 --- a/vm/Globals.h +++ b/vm/Globals.h @@ -104,6 +104,7 @@ struct DvmGlobals { void (*abortHook)(void); int jniGrefLimit; // 0 means no limit + char* jniTrace; bool reduceSignals; bool noQuitHandler; bool verifyDexChecksum; @@ -123,6 +124,7 @@ struct DvmGlobals { bool postVerify; bool generateRegisterMaps; bool concurrentMarkSweep; + bool verifyCardTable; int assertionCtrlCount; AssertionControl* assertionCtrl; @@ -176,8 +178,16 @@ struct DvmGlobals { /* * Interned strings. */ + + /* A mutex that guards access to the interned string tables. */ + pthread_mutex_t internLock; + + /* Hash table of strings interned by the user. */ HashTable* internedStrings; + /* Hash table of strings interned by the class loader. */ + HashTable* literalStrings; + /* * Quick lookups for popular classes used internally. */ @@ -277,6 +287,7 @@ struct DvmGlobals { int offJavaLangRefReference_referent; int offJavaLangRefReference_queue; int offJavaLangRefReference_queueNext; + int offJavaLangRefReference_pendingNext; /* method pointers - java.lang.ref.Reference */ Method* methJavaLangRefReference_enqueueInternal; @@ -444,9 +455,18 @@ struct DvmGlobals { */ pthread_mutex_t gcHeapLock; + /* + * Condition variable to queue threads waiting to retry an + * allocation. Signaled after a concurrent GC is completed. + */ + pthread_cond_t gcHeapCond; + /* Opaque pointer representing the heap. */ GcHeap* gcHeap; + /* The card table base, modified as needed for marking cards. */ + u1* biasedCardTableBase; + /* * Pre-allocated throwables. */ @@ -111,6 +111,9 @@ static void dvmUsage(const char* progName) dvmFprintf(stderr, " -Xjnigreflimit:N (must be multiple of 100, >= 200)\n"); dvmFprintf(stderr, " -Xjniopts:{warnonly,forcecopy}\n"); +#if defined(WITH_JNI_TRACE) + dvmFprintf(stderr, " -Xjnitrace:substring (eg NativeClass or nativeMethod)\n"); +#endif dvmFprintf(stderr, " -Xdeadlockpredict:{off,warn,err,abort}\n"); dvmFprintf(stderr, " -Xstacktracefile:<filename>\n"); dvmFprintf(stderr, " -Xgc:[no]precise\n"); @@ -118,6 +121,7 @@ static void dvmUsage(const char* progName) dvmFprintf(stderr, " -Xgc:[no]preverify\n"); dvmFprintf(stderr, " -Xgc:[no]postverify\n"); dvmFprintf(stderr, " -Xgc:[no]concurrent\n"); + dvmFprintf(stderr, " -Xgc:[no]verifycardtable\n"); dvmFprintf(stderr, " -Xgenregmap\n"); dvmFprintf(stderr, " -Xcheckdexsum\n"); #if defined(WITH_JIT) @@ -175,6 +179,9 @@ static void dvmUsage(const char* progName) #ifdef WITH_JNI_STACK_CHECK " jni_stack_check" #endif +#ifdef WITH_JNI_TRACE + " jni_trace" +#endif #ifdef EASY_GDB " easy_gdb" #endif @@ -885,6 +892,11 @@ static int dvmProcessOptions(int argc, const char* const argv[], } gDvm.jniGrefLimit = lim; +#if defined(WITH_JNI_TRACE) + } else if (strncmp(argv[i], "-Xjnitrace:", 11) == 0) { + gDvm.jniTrace = strdup(argv[i] + 11); +#endif + } else if (strcmp(argv[i], "-Xlog-stdio") == 0) { gDvm.logStdio = true; @@ -988,6 +1000,10 @@ static int dvmProcessOptions(int argc, const char* const argv[], gDvm.concurrentMarkSweep = true; else if (strcmp(argv[i] + 5, "noconcurrent") == 0) gDvm.concurrentMarkSweep = false; + else if (strcmp(argv[i] + 5, "verifycardtable") == 0) + gDvm.verifyCardTable = true; + else if (strcmp(argv[i] + 5, "noverifycardtable") == 0) + gDvm.verifyCardTable = false; else { dvmFprintf(stderr, "Bad value for -Xgc"); return -1; @@ -1040,6 +1056,8 @@ static void setCommandLineDefaults() gDvm.heapSizeMax = 16 * 1024 * 1024; // Spec says 75% physical mem gDvm.stackSize = kDefaultStackSize; + gDvm.concurrentMarkSweep = true; + /* gDvm.jdwpSuspend = true; */ /* allowed unless zygote config doesn't allow it */ @@ -1602,6 +1620,8 @@ void dvmShutdown(void) dvmJdwpShutdown(gDvm.jdwpState); free(gDvm.jdwpHost); gDvm.jdwpHost = NULL; + free(gDvm.jniTrace); + gDvm.jniTrace = NULL; free(gDvm.stackTraceFile); gDvm.stackTraceFile = NULL; diff --git a/vm/Intern.c b/vm/Intern.c index 85843335b..15bf8ab60 100644 --- a/vm/Intern.c +++ b/vm/Intern.c @@ -18,26 +18,20 @@ */ #include "Dalvik.h" -#include <stdlib.h> - -#define INTERN_STRING_IMMORTAL_BIT (1<<0) -#define SET_IMMORTAL_BIT(strObj) \ - ((uintptr_t)(strObj) | INTERN_STRING_IMMORTAL_BIT) -#define STRIP_IMMORTAL_BIT(strObj) \ - ((uintptr_t)(strObj) & ~INTERN_STRING_IMMORTAL_BIT) -#define IS_IMMORTAL(strObj) \ - ((uintptr_t)(strObj) & INTERN_STRING_IMMORTAL_BIT) - +#include <stddef.h> /* * Prep string interning. */ bool dvmStringInternStartup(void) { + dvmInitMutex(&gDvm.internLock); gDvm.internedStrings = dvmHashTableCreate(256, NULL); if (gDvm.internedStrings == NULL) return false; - + gDvm.literalStrings = dvmHashTableCreate(256, NULL); + if (gDvm.literalStrings == NULL) + return false; return true; } @@ -48,70 +42,47 @@ bool dvmStringInternStartup(void) */ void dvmStringInternShutdown(void) { + if (gDvm.internedStrings != NULL || gDvm.literalStrings != NULL) { + dvmDestroyMutex(&gDvm.internLock); + } dvmHashTableFree(gDvm.internedStrings); gDvm.internedStrings = NULL; + dvmHashTableFree(gDvm.literalStrings); + gDvm.literalStrings = NULL; } - -/* - * Compare two string objects that may have INTERN_STRING_IMMORTAL_BIT - * set in their pointer values. - */ -static int hashcmpImmortalStrings(const void* vstrObj1, const void* vstrObj2) -{ - return dvmHashcmpStrings((const void*) STRIP_IMMORTAL_BIT(vstrObj1), - (const void*) STRIP_IMMORTAL_BIT(vstrObj2)); -} - -static StringObject* lookupInternedString(StringObject* strObj, bool immortal) +static StringObject* lookupInternedString(StringObject* strObj, bool isLiteral) { StringObject* found; u4 hash; assert(strObj != NULL); hash = dvmComputeStringHash(strObj); - - if (false) { - char* debugStr = dvmCreateCstrFromString(strObj); - LOGV("+++ dvmLookupInternedString searching for '%s'\n", debugStr); - free(debugStr); - } - - if (immortal) { - strObj = (StringObject*) SET_IMMORTAL_BIT(strObj); - } - - dvmHashTableLock(gDvm.internedStrings); - - found = (StringObject*) dvmHashTableLookup(gDvm.internedStrings, - hash, strObj, hashcmpImmortalStrings, true); - if (immortal && !IS_IMMORTAL(found)) { - /* Make this entry immortal. We have to use the existing object - * because, as an interned string, it's not allowed to change. - * - * There's no way to get a pointer to the actual hash table entry, - * so the only way to modify the existing entry is to remove, - * modify, and re-add it. - */ - dvmHashTableRemove(gDvm.internedStrings, hash, found); - found = (StringObject*) SET_IMMORTAL_BIT(found); - found = (StringObject*) dvmHashTableLookup(gDvm.internedStrings, - hash, found, hashcmpImmortalStrings, true); - assert(IS_IMMORTAL(found)); + dvmLockMutex(&gDvm.internLock); + if (isLiteral) { + found = dvmHashTableLookup(gDvm.literalStrings, hash, strObj, + dvmHashcmpStrings, true); + if (found == strObj) { + dvmHashTableRemove(gDvm.internedStrings, hash, strObj); + } + } else { + found = dvmHashTableLookup(gDvm.literalStrings, hash, strObj, + dvmHashcmpStrings, false); + if (found == NULL) { + found = dvmHashTableLookup(gDvm.internedStrings, hash, strObj, + dvmHashcmpStrings, true); + } } - - dvmHashTableUnlock(gDvm.internedStrings); - - //if (found == strObj) - // LOGVV("+++ added string\n"); - return (StringObject*) STRIP_IMMORTAL_BIT(found); + assert(found != NULL); + dvmUnlockMutex(&gDvm.internLock); + return found; } /* - * Find an entry in the interned string list. + * Find an entry in the interned string table. * * If the string doesn't already exist, the StringObject is added to - * the list. Otherwise, the existing entry is returned. + * the table. Otherwise, the existing entry is returned. */ StringObject* dvmLookupInternedString(StringObject* strObj) { @@ -120,45 +91,36 @@ StringObject* dvmLookupInternedString(StringObject* strObj) /* * Same as dvmLookupInternedString(), but guarantees that the - * returned string is immortal. + * returned string is a literal. */ StringObject* dvmLookupImmortalInternedString(StringObject* strObj) { return lookupInternedString(strObj, true); } -/* - * Mark all immortal interned string objects so that they don't - * get collected by the GC. Non-immortal strings may or may not - * get marked by other references. - */ static int markStringObject(void* strObj, void* arg) { UNUSED_PARAMETER(arg); - - if (IS_IMMORTAL(strObj)) { - dvmMarkObjectNonNull((Object*) STRIP_IMMORTAL_BIT(strObj)); - } + dvmMarkObjectNonNull(strObj); return 0; } +/* + * Blacken string references from the literal string table. The + * literal table is a root. + */ void dvmGcScanInternedStrings() { /* It's possible for a GC to happen before dvmStringInternStartup() * is called. */ - if (gDvm.internedStrings != NULL) { - dvmHashTableLock(gDvm.internedStrings); - dvmHashForeach(gDvm.internedStrings, markStringObject, NULL); - dvmHashTableUnlock(gDvm.internedStrings); + if (gDvm.literalStrings != NULL) { + dvmHashForeach(gDvm.literalStrings, markStringObject, NULL); } } /* - * Called by the GC after all reachable objects have been - * marked. isUnmarkedObject is a function suitable for passing - * to dvmHashForeachRemove(); it must strip the low bits from - * its pointer argument to deal with the immortal bit, though. + * Clear white references from the intern table. */ void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *)) { @@ -166,8 +128,8 @@ void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *)) * is called. */ if (gDvm.internedStrings != NULL) { - dvmHashTableLock(gDvm.internedStrings); + dvmLockMutex(&gDvm.internLock); dvmHashForeachRemove(gDvm.internedStrings, isUnmarkedObject); - dvmHashTableUnlock(gDvm.internedStrings); + dvmUnlockMutex(&gDvm.internLock); } } @@ -1296,26 +1296,26 @@ static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName, if (method == NULL) method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature); if (method == NULL) { - LOGW("ERROR: Unable to find decl for native %s.%s %s\n", + LOGW("ERROR: Unable to find decl for native %s.%s:%s\n", clazz->descriptor, methodName, signature); goto bail; } if (!dvmIsNativeMethod(method)) { - LOGW("Unable to register: not native: %s.%s %s\n", + LOGW("Unable to register: not native: %s.%s:%s\n", clazz->descriptor, methodName, signature); goto bail; } if (method->nativeFunc != dvmResolveNativeMethod) { - LOGW("Warning: %s.%s %s was already registered/resolved?\n", + /* this is allowed, but unusual */ + LOGV("Note: %s.%s:%s was already registered\n", clazz->descriptor, methodName, signature); - /* keep going, I guess */ } dvmUseJNIBridge(method, fnPtr); - LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName, + LOGV("JNI-registered %s.%s:%s\n", clazz->descriptor, methodName, signature); result = true; @@ -1485,6 +1485,31 @@ void dvmReleaseJniMonitors(Thread* self) pRefTable->nextEntry = pRefTable->table; } +/* + * Determine if the specified class can be instantiated from JNI. This + * is used by AllocObject / NewObject, which are documented as throwing + * an exception for abstract and interface classes, and not accepting + * array classes. We also want to reject attempts to create new Class + * objects, since only DefineClass should do that. + */ +static bool canAllocClass(ClassObject* clazz) +{ + if (dvmIsAbstractClass(clazz) || dvmIsInterfaceClass(clazz)) { + /* JNI spec defines what this throws */ + dvmThrowExceptionFmt("Ljava/lang/InstantiationException;", + "Can't instantiate %s (abstract or interface)", clazz->descriptor); + return false; + } else if (dvmIsArrayClass(clazz) || clazz == gDvm.classJavaLangClass) { + /* spec says "must not" for arrays, ignores Class */ + dvmThrowExceptionFmt("Ljava/lang/IllegalArgumentException;", + "Can't instantiate %s (array or Class) with this JNI function", + clazz->descriptor); + return false; + } + + return true; +} + #ifdef WITH_JNI_STACK_CHECK /* * Compute a CRC on the entire interpreted stack. @@ -1607,8 +1632,6 @@ void dvmCallJNIMethod_general(const u4* args, JValue* pResult, jclass staticMethodClass; JNIEnv* env = self->jniEnv; - assert(method->insns != NULL); - //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns, // method->clazz->descriptor, method->name, method->shorty); @@ -1671,6 +1694,9 @@ void dvmCallJNIMethod_general(const u4* args, JValue* pResult, oldStatus = dvmChangeStatus(self, THREAD_NATIVE); + ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */ + assert(method->insns != NULL); + COMPUTE_STACK_SUM(self); dvmPlatformInvoke(env, staticMethodClass, method->jniArgInfo, method->insSize, modArgs, method->shorty, @@ -1730,6 +1756,8 @@ void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult, oldStatus = dvmChangeStatus(self, THREAD_NATIVE); + ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */ + COMPUTE_STACK_SUM(self); dvmPlatformInvoke(self->jniEnv, NULL, method->jniArgInfo, method->insSize, modArgs, method->shorty, @@ -1764,6 +1792,8 @@ void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult, oldStatus = dvmChangeStatus(self, THREAD_NATIVE); + ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */ + COMPUTE_STACK_SUM(self); dvmPlatformInvoke(self->jniEnv, staticMethodClass, method->jniArgInfo, method->insSize, args, method->shorty, @@ -2227,7 +2257,9 @@ static jobject AllocObject(JNIEnv* env, jclass jclazz) ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); jobject result; - if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { + if (!canAllocClass(clazz) || + (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) + { assert(dvmCheckException(_self)); result = NULL; } else { @@ -2248,7 +2280,9 @@ static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); jobject result; - if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { + if (!canAllocClass(clazz) || + (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) + { assert(dvmCheckException(_self)); result = NULL; } else { @@ -2274,11 +2308,19 @@ static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); jobject result; - Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); - result = addLocalReference(env, newObj); - if (newObj != NULL) { - JValue unused; - dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args); + if (!canAllocClass(clazz) || + (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) + { + assert(dvmCheckException(_self)); + result = NULL; + } else { + Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); + result = addLocalReference(env, newObj); + if (newObj != NULL) { + JValue unused; + dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, + args); + } } JNI_EXIT(); @@ -2291,11 +2333,19 @@ static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); jobject result; - Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); - result = addLocalReference(env, newObj); - if (newObj != NULL) { - JValue unused; - dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args); + if (!canAllocClass(clazz) || + (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) + { + assert(dvmCheckException(_self)); + result = NULL; + } else { + Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); + result = addLocalReference(env, newObj); + if (newObj != NULL) { + JValue unused; + dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, + args); + } } JNI_EXIT(); @@ -3236,6 +3286,9 @@ PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double); /* * Register one or more native functions in one class. + * + * This can be called multiple times on the same method, allowing the + * caller to redefine the method implementation at will. */ static jint RegisterNatives(JNIEnv* env, jclass jclazz, const JNINativeMethod* methods, jint nMethods) @@ -3264,19 +3317,44 @@ static jint RegisterNatives(JNIEnv* env, jclass jclazz, } /* - * Un-register a native function. + * Un-register all native methods associated with the class. + * + * The JNI docs refer to this as a way to reload/relink native libraries, + * and say it "should not be used in normal native code". In particular, + * there is no need to do this during shutdown, and you do not need to do + * this before redefining a method implementation with RegisterNatives. + * + * It's chiefly useful for a native "plugin"-style library that wasn't + * loaded with System.loadLibrary() (since there's no way to unload those). + * For example, the library could upgrade itself by: + * + * 1. call UnregisterNatives to unbind the old methods + * 2. ensure that no code is still executing inside it (somehow) + * 3. dlclose() the library + * 4. dlopen() the new library + * 5. use RegisterNatives to bind the methods from the new library + * + * The above can work correctly without the UnregisterNatives call, but + * creates a window of opportunity in which somebody might try to call a + * method that is pointing at unmapped memory, crashing the VM. In theory + * the same guards that prevent dlclose() from unmapping executing code could + * prevent that anyway, but with this we can be more thorough and also deal + * with methods that only exist in the old or new form of the library (maybe + * the lib wants to try the call and catch the UnsatisfiedLinkError). */ static jint UnregisterNatives(JNIEnv* env, jclass jclazz) { JNI_ENTER(); - /* - * The JNI docs refer to this as a way to reload/relink native libraries, - * and say it "should not be used in normal native code". - * - * We can implement it if we decide we need it. - */ + + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + if (gDvm.verboseJni) { + LOGI("[Unregistering JNI native methods for class %s]\n", + clazz->descriptor); + } + dvmUnregisterJNINativeMethods(clazz); + JNI_EXIT(); - return JNI_ERR; + return JNI_OK; } /* @@ -26,7 +26,11 @@ #include <time.h> #include <sys/time.h> #include <fcntl.h> +#include <cutils/ashmem.h> +#include <sys/mman.h> +#define ALIGN_UP_TO_PAGE_SIZE(p) \ + (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1)) /* * Print a hex dump in this format: @@ -686,3 +690,28 @@ size_t strlcpy(char *dst, const char *src, size_t size) { return srcLength; } #endif + +/* + * Allocates a memory region using ashmem and mmap, initialized to + * zero. Actual allocation rounded up to page multiple. Returns + * NULL on failure. + */ +void *dvmAllocRegion(size_t size, int prot, const char *name) { + void *base; + int fd, ret; + + size = ALIGN_UP_TO_PAGE_SIZE(size); + fd = ashmem_create_region(name, size); + if (fd == -1) { + return NULL; + } + base = mmap(NULL, size, prot, MAP_PRIVATE, fd, 0); + ret = close(fd); + if (base == MAP_FAILED) { + return NULL; + } + if (ret == -1) { + return NULL; + } + return base; +} @@ -231,6 +231,18 @@ INLINE u8 dvmGetRelativeTimeUsec(void) { } /* + * Get the current time, in milliseconds. This is "relative" time, + * meaning it could be wall-clock time or a monotonic counter, and is + * only suitable for computing time deltas. The value returned from + * this function is a u4 and should only be used for debugging + * messages. TODO: make this value relative to the start-up time of + * the VM. + */ +INLINE u4 dvmGetRelativeTimeMsec(void) { + return (u4)(dvmGetRelativeTimeUsec() / 1000); +} + +/* * Get the current per-thread CPU time. This clock increases monotonically * when the thread is running, but not when it's sleeping or blocked on a * synchronization object. @@ -287,4 +299,11 @@ void dvmAbort(void); size_t strlcpy(char *dst, const char *src, size_t size); #endif +/* + * Allocates a memory region using ashmem and mmap, initialized to + * zero. Actual allocation rounded up to page multiple. Returns + * NULL on failure. + */ +void *dvmAllocRegion(size_t size, int prot, const char *name); + #endif /*_DALVIK_MISC*/ diff --git a/vm/Native.c b/vm/Native.c index 0b10e232e..a641c2505 100644 --- a/vm/Native.c +++ b/vm/Native.c @@ -57,7 +57,7 @@ void dvmNativeShutdown(void) * * This is executed as if it were a native bridge or function. If the * resolution succeeds, method->insns is replaced, and we don't go through - * here again. + * here again unless the method is unregistered. * * Initializes method's class if necessary. * @@ -103,8 +103,7 @@ void dvmResolveNativeMethod(const u4* args, JValue* pResult, dvmAbort(); // harsh, but this is VM-internal problem } DalvikBridgeFunc dfunc = (DalvikBridgeFunc) func; - dvmSetNativeFunc(method, dfunc, NULL); - assert(method->insns == NULL); + dvmSetNativeFunc((Method*) method, dfunc, NULL); dfunc(args, pResult, method, self); return; } @@ -407,7 +406,7 @@ static bool checkOnLoadResult(SharedLib* pEntry) while (pEntry->onLoadResult == kOnLoadPending) { LOGD("threadid=%d: waiting for %s OnLoad status\n", self->threadId, pEntry->pathName); - int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); + ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); pthread_cond_wait(&pEntry->onLoadCond, &pEntry->onLoadLock); dvmChangeStatus(self, oldStatus); } @@ -499,7 +498,7 @@ bool dvmLoadNativeCode(const char* pathName, Object* classLoader) * the GC to ignore us. */ Thread* self = dvmThreadSelf(); - int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); + ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); handle = dlopen(pathName, RTLD_LAZY); dvmChangeStatus(self, oldStatus); @@ -594,6 +593,71 @@ bool dvmLoadNativeCode(const char* pathName, Object* classLoader) /* + * Un-register JNI native methods. + * + * There are two relevant fields in struct Method, "nativeFunc" and + * "insns". The former holds a function pointer to a "bridge" function + * (or, for internal native, the actual implementation). The latter holds + * a pointer to the actual JNI method. + * + * The obvious approach is to reset both fields to their initial state + * (nativeFunc points at dvmResolveNativeMethod, insns holds NULL), but + * that creates some unpleasant race conditions. In particular, if another + * thread is executing inside the call bridge for the method in question, + * and we reset insns to NULL, the VM will crash. (See the comments above + * dvmSetNativeFunc() for additional commentary.) + * + * We can't rely on being able to update two 32-bit fields in one atomic + * operation (e.g. no 64-bit atomic ops on ARMv5TE), so we want to change + * only one field. It turns out we can simply reset nativeFunc to its + * initial state, leaving insns alone, because dvmResolveNativeMethod + * ignores "insns" entirely. + * + * When the method is re-registered, both fields will be updated, but + * dvmSetNativeFunc guarantees that "insns" is updated first. This means + * we shouldn't be in a situation where we have a "live" call bridge and + * a stale implementation pointer. + */ +static void unregisterJNINativeMethods(Method* methods, size_t count) +{ + while (count != 0) { + count--; + + Method* meth = &methods[count]; + if (!dvmIsNativeMethod(meth)) + continue; + if (dvmIsAbstractMethod(meth)) /* avoid abstract method stubs */ + continue; + + /* + * Strictly speaking this ought to test the function pointer against + * the various JNI bridge functions to ensure that we only undo + * methods that were registered through JNI. In practice, any + * native method with a non-NULL "insns" is a registered JNI method. + * + * If we inadvertently unregister an internal-native, it'll get + * re-resolved on the next call; unregistering an unregistered + * JNI method is a no-op. So we don't really need to test for + * anything. + */ + + LOGD("Unregistering JNI method %s.%s:%s\n", + meth->clazz->descriptor, meth->name, meth->shorty); + dvmSetNativeFunc(meth, dvmResolveNativeMethod, NULL); + } +} + +/* + * Un-register all JNI native methods from a class. + */ +void dvmUnregisterJNINativeMethods(ClassObject* clazz) +{ + unregisterJNINativeMethods(clazz->directMethods, clazz->directMethodCount); + unregisterJNINativeMethods(clazz->virtualMethods, clazz->virtualMethodCount); +} + + +/* * =========================================================================== * Signature-based method lookup * =========================================================================== @@ -815,3 +879,114 @@ static void* lookupSharedLibMethod(const Method* method) return (void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib, (void*) method); } + + +static void appendValue(char type, const JValue value, char* buf, size_t n, + bool appendComma) +{ + size_t len = strlen(buf); + if (len >= n - 32) { // 32 should be longer than anything we could append. + buf[len - 1] = '.'; + buf[len - 2] = '.'; + buf[len - 3] = '.'; + return; + } + char* p = buf + len; + switch (type) { + case 'B': + if (value.b >= 0 && value.b < 10) { + sprintf(p, "%d", value.b); + } else { + sprintf(p, "0x%x (%d)", value.b, value.b); + } + break; + case 'C': + if (value.c < 0x7f && value.c >= ' ') { + sprintf(p, "U+%x ('%c')", value.c, value.c); + } else { + sprintf(p, "U+%x", value.c); + } + break; + case 'D': + sprintf(p, "%g", value.d); + break; + case 'F': + sprintf(p, "%g", value.f); + break; + case 'I': + sprintf(p, "%d", value.i); + break; + case 'L': + sprintf(p, "0x%x", value.i); + break; + case 'J': + sprintf(p, "%lld", value.j); + break; + case 'S': + sprintf(p, "%d", value.s); + break; + case 'V': + strcpy(p, "void"); + break; + case 'Z': + strcpy(p, value.z ? "true" : "false"); + break; + default: + sprintf(p, "unknown type '%c'", type); + break; + } + + if (appendComma) { + strcat(p, ", "); + } +} + +#define LOGI_NATIVE(...) LOG(LOG_INFO, LOG_TAG "-native", __VA_ARGS__) + +void dvmLogNativeMethodEntry(const Method* method, u4* fp) +{ + char thisString[32] = { 0 }; + u4* sp = &fp[method->registersSize - method->insSize]; + if (!dvmIsStaticMethod(method)) { + sprintf(thisString, "this=0x%08x ", *sp++); + } + + char argsString[128]= { 0 }; + const char* desc = &method->shorty[1]; + while (*desc != '\0') { + char argType = *desc++; + JValue value; + if (argType == 'D' || argType == 'J') { + value.j = dvmGetArgLong(sp, 0); + sp += 2; + } else { + value.i = *sp++; + } + appendValue(argType, value, argsString, sizeof(argsString), + *desc != '\0'); + } + + char* signature = dexProtoCopyMethodDescriptor(&method->prototype); + LOGI_NATIVE("-> %s.%s%s %s(%s)", method->clazz->descriptor, method->name, + signature, thisString, argsString); + free(signature); +} + +void dvmLogNativeMethodExit(const Method* method, Thread* self, + const JValue returnValue) +{ + char* signature = dexProtoCopyMethodDescriptor(&method->prototype); + if (dvmCheckException(self)) { + Object* exception = dvmGetException(self); + LOGI_NATIVE("<- %s.%s%s threw %s", method->clazz->descriptor, + method->name, signature, exception->clazz->descriptor); + } else { + char returnValueString[128] = { 0 }; + char returnType = method->shorty[0]; + appendValue(returnType, returnValue, + returnValueString, sizeof(returnValueString), false); + LOGI_NATIVE("<- %s.%s%s returned %s", method->clazz->descriptor, + method->name, signature, returnValueString); + } + free(signature); +} diff --git a/vm/Native.h b/vm/Native.h index 5b54fc7cb..57396477b 100644 --- a/vm/Native.h +++ b/vm/Native.h @@ -78,6 +78,11 @@ bool dvmLoadNativeCode(const char* fileName, Object* classLoader); void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, struct Thread* self); +/* + * Unregister all JNI native methods associated with a class. + */ +void dvmUnregisterJNINativeMethods(ClassObject* clazz); + //#define GET_ARG_LONG(_args, _elem) (*(s8*)(&(_args)[_elem])) #define GET_ARG_LONG(_args, _elem) dvmGetArgLong(_args, _elem) @@ -105,4 +110,12 @@ INLINE s8 dvmGetArgLong(const u4* args, int elem) #endif } +/* + * Used to implement -Xjnitrace. + */ +struct Thread; +void dvmLogNativeMethodEntry(const Method* method, u4* newFp); +void dvmLogNativeMethodExit(const Method* method, struct Thread* self, + const JValue retval); + #endif /*_DALVIK_NATIVE*/ diff --git a/vm/StdioConverter.c b/vm/StdioConverter.c index 987fdf0f1..6a4d84584 100644 --- a/vm/StdioConverter.c +++ b/vm/StdioConverter.c @@ -143,7 +143,6 @@ void dvmStdioConverterShutdown(void) */ static void* stdioConverterThreadStart(void* arg) { -#define MAX(a,b) ((a) > (b) ? (a) : (b)) StdPipes* pipeStorage = (StdPipes*) arg; BufferedData* stdoutData; BufferedData* stderrData; @@ -217,7 +216,6 @@ static void* stdioConverterThreadStart(void* arg) /* change back for shutdown sequence */ dvmChangeStatus(NULL, THREAD_RUNNING); return NULL; -#undef MAX } /* @@ -928,6 +928,14 @@ retry: * value of the recursion count field. */ obj->lock += 1 << LW_LOCK_COUNT_SHIFT; + if (LW_LOCK_COUNT(obj->lock) == LW_LOCK_COUNT_MASK) { + /* + * The reacquisition limit has been reached. Inflate + * the lock so the next acquire will not overflow the + * recursion count field. + */ + inflateMonitor(self, obj); + } } else if (LW_LOCK_OWNER(thin) == 0) { /* * The lock is unowned. Install the thread id of the @@ -2013,26 +2021,6 @@ static void removeCollectedObject(Object* obj) LOGVV("+++ collecting %p\n", obj); -#if 0 - /* - * We're currently running through the entire set of known monitors. - * This can be somewhat slow. We may want to keep lists of parents - * in each child to speed up GC. - */ - mon = gDvm.monitorList; - while (mon != NULL) { - Object* parent = mon->obj; - if (parent != NULL) { /* value nulled for deleted entries */ - if (objectInChildList(parent, obj)) { - LOGVV("removing child %p from parent %p\n", obj, parent); - unlinkParentFromChild(parent, obj); - mergeChildren(parent, obj); - } - } - mon = mon->next; - } -#endif - /* * For every parent of this object: * - merge all of our children into the parent's child list (creates diff --git a/vm/Thread.c b/vm/Thread.c index b3b171ed3..40c79c40c 100644 --- a/vm/Thread.c +++ b/vm/Thread.c @@ -1472,9 +1472,8 @@ bool dvmCreateInterpThread(Object* threadObj, int reqStackSize) */ dvmUnlockThreadList(); - int cc, oldStatus; - oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); - cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, + ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); + int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread); oldStatus = dvmChangeStatus(self, oldStatus); @@ -1850,7 +1849,7 @@ bool dvmCreateInternalThread(pthread_t* pHandle, const char* name, * need to initiate a GC. We switch to VMWAIT while we pause. */ Thread* self = dvmThreadSelf(); - int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); + ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); dvmLockThreadList(self); while (createStatus == 0) pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock); diff --git a/vm/alloc/CardTable.c b/vm/alloc/CardTable.c new file mode 100644 index 000000000..bd8de9aa7 --- /dev/null +++ b/vm/alloc/CardTable.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010 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. + */ + +/* + * Needed for PROT_* definitions. + */ +#include <sys/mman.h> + +#include "Dalvik.h" +#include "alloc/HeapSource.h" +#include "alloc/Visit.h" + +/* + * Maintain a card table from the the write barrier. All writes of + * non-NULL values to heap addresses should go through an entry in + * WriteBarrier, and from there to here. + * + * The heap is divided into "cards" of GC_CARD_SIZE bytes, as + * determined by GC_CARD_SHIFT. The card table contains one byte of + * data per card, to be used by the GC. The value of the byte will be + * one of GC_CARD_CLEAN or GC_CARD_DIRTY. + * + * After any store of a non-NULL object pointer into a heap object, + * code is obliged to mark the card dirty. The setters in + * ObjectInlines.h [such as dvmSetFieldObject] do this for you. The + * JIT and fast interpreters also contain code to mark cards as dirty. + * + * The card table's base [the "biased card table"] gets set to a + * rather strange value. In order to keep the JIT from having to + * fabricate or load GC_DIRTY_CARD to store into the card table, + * biased base is within the mmap allocation at a point where it's low + * byte is equal to GC_DIRTY_CARD. See dvmCardTableStartup for details. + */ + +/* + * Initializes the card table; must be called before any other + * dvmCardTable*() functions. + */ +bool dvmCardTableStartup(void) +{ + size_t length; + void *allocBase; + u1 *biasedBase; + GcHeap *gcHeap = gDvm.gcHeap; + void *heapBase = dvmHeapSourceGetBase(); + assert(gcHeap != NULL); + assert(heapBase != NULL); + + /* Set up the card table */ + length = gDvm.heapSizeMax / GC_CARD_SIZE; + /* Allocate an extra 256 bytes to allow fixed low-byte of base */ + allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE, + "dalvik-card-table"); + if (allocBase == NULL) { + return false; + } + gcHeap->cardTableBase = allocBase; + gcHeap->cardTableLength = length; + /* All zeros is the correct initial value; all clean. */ + assert(GC_CARD_CLEAN == 0); + + biasedBase = (u1 *)((uintptr_t)allocBase - + ((uintptr_t)heapBase >> GC_CARD_SHIFT)); + if (((uintptr_t)biasedBase & 0xff) != GC_CARD_DIRTY) { + int offset; + offset = GC_CARD_DIRTY - ((uintptr_t)biasedBase & 0xff); + biasedBase += offset + (offset < 0 ? 0x100 : 0); + } + assert(((uintptr_t)biasedBase & 0xff) == GC_CARD_DIRTY); + gDvm.biasedCardTableBase = biasedBase; + + return true; +} + +/* + * Tears down the entire CardTable. + */ +void dvmCardTableShutdown() +{ + gDvm.biasedCardTableBase = NULL; + munmap(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength); +} + +void dvmClearCardTable(void) +{ + assert(gDvm.gcHeap->cardTableBase != NULL); + memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, gDvm.gcHeap->cardTableLength); +} + +/* + * Returns true iff the address is within the bounds of the card table. + */ +bool dvmIsValidCard(const u1 *cardAddr) +{ + GcHeap *h = gDvm.gcHeap; + return cardAddr >= h->cardTableBase && + cardAddr < &h->cardTableBase[h->cardTableLength]; +} + +/* + * Returns the address of the relevent byte in the card table, given + * an address on the heap. + */ +u1 *dvmCardFromAddr(const void *addr) +{ + u1 *biasedBase = gDvm.biasedCardTableBase; + u1 *cardAddr = biasedBase + ((uintptr_t)addr >> GC_CARD_SHIFT); + assert(dvmIsValidCard(cardAddr)); + return cardAddr; +} + +/* + * Returns the first address in the heap which maps to this card. + */ +void *dvmAddrFromCard(const u1 *cardAddr) +{ + assert(dvmIsValidCard(cardAddr)); + uintptr_t offset = cardAddr - gDvm.biasedCardTableBase; + return (void *)(offset << GC_CARD_SHIFT); +} + +/* + * Dirties the card for the given address. + */ +void dvmMarkCard(const void *addr) +{ + u1 *cardAddr = dvmCardFromAddr(addr); + *cardAddr = GC_CARD_DIRTY; +} diff --git a/vm/alloc/CardTable.h b/vm/alloc/CardTable.h new file mode 100644 index 000000000..7a86bd049 --- /dev/null +++ b/vm/alloc/CardTable.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 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. + */ + +/* + * Maintain a card table from the the write barrier. All writes of + * non-NULL values to heap addresses should go through an entry in + * WriteBarrier, and from there to here. + */ + +#ifndef _DALVIK_ALLOC_CARDTABLE +#define _DALVIK_ALLOC_CARDTABLE + +/* + * TODO: Better documentation of these values pending integration of + * concurrent collections and the card table. + */ +#define GC_CARD_SHIFT 7 +#define GC_CARD_SIZE (1 << GC_CARD_SHIFT) +#define GC_CARD_CLEAN 0 +#define GC_CARD_DIRTY 0x70 + +/* + * Initializes the card table; must be called before any other + * dvmCardTable*() functions. + */ +bool dvmCardTableStartup(void); + +/* + * Tears down the entire CardTable structure. + */ +void dvmCardTableShutdown(void); + +/* + * Resets all of the bytes in the card table to clean. + */ +void dvmClearCardTable(void); + +/* + * Returns the address of the relevent byte in the card table, given + * an address on the heap. + */ +u1 *dvmCardFromAddr(const void *addr); + +/* + * Returns the first address in the heap which maps to this card. + */ +void *dvmAddrFromCard(const u1 *card); + +/* + * Set the card associated with the given address to GC_CARD_DIRTY. + */ +void dvmMarkCard(const void *addr); + +/* + * dvmAbort if any clean object in the Zygote heap contains a + * reference to the application heap, or if the immune limit is not as + * expected. + */ +void dvmVerifyCardTable(void); + +/* TODO: Clearing, querying, and iterating over the card table. */ + +#endif /*_DALVIK_ALLOC_CARDTABLE*/ diff --git a/vm/alloc/Copying.c b/vm/alloc/Copying.c index 2dd812791..622f48955 100644 --- a/vm/alloc/Copying.c +++ b/vm/alloc/Copying.c @@ -2355,3 +2355,13 @@ void dvmMarkObjectNonNull(const Object *obj) { assert(!"implemented"); } + +void dvmMarkDirtyObjects(void) +{ + assert(!"implemented"); +} + +void dvmHeapSourceThreadShutdown(void) +{ + /* do nothing */ +} diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c index 474818cc1..f063b45c4 100644 --- a/vm/alloc/Heap.c +++ b/vm/alloc/Heap.c @@ -25,6 +25,7 @@ #include "alloc/DdmHeap.h" #include "alloc/HeapSource.h" #include "alloc/MarkSweep.h" +#include "alloc/Visit.h" #include "utils/threads.h" // need Android thread priorities #define kInvalidPriority 10000 @@ -84,6 +85,11 @@ bool dvmHeapStartup() gcHeap->pendingFinalizationRefs = NULL; gcHeap->referenceOperations = NULL; + if (!dvmCardTableStartup()) { + LOGE_HEAP("card table startup failed."); + return false; + } + /* Initialize the HeapWorker locks and other state * that the GC uses. */ @@ -94,10 +100,6 @@ bool dvmHeapStartup() bool dvmHeapStartupAfterZygote(void) { - /* Update our idea of the last GC start time so that we - * don't use the last time that Zygote happened to GC. - */ - gDvm.gcHeap->gcStartTime = dvmGetRelativeTimeUsec(); return dvmHeapSourceStartupAfterZygote(); } @@ -105,10 +107,10 @@ void dvmHeapShutdown() { //TODO: make sure we're locked if (gDvm.gcHeap != NULL) { - /* 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. + 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->finalizableRefs); gDvm.gcHeap->finalizableRefs = NULL; @@ -119,10 +121,9 @@ void dvmHeapShutdown() 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. + /* 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. */ dvmHeapSourceShutdown(&gDvm.gcHeap); } @@ -166,16 +167,9 @@ bool dvmLockHeap() ThreadStatus oldStatus; self = dvmThreadSelf(); - if (self != NULL) { - oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); - } else { - LOGI("ODD: waiting on heap lock, no self\n"); - oldStatus = -1; // shut up gcc - } + oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); dvmLockMutex(&gDvm.gcHeapLock); - if (self != NULL) { - dvmChangeStatus(self, oldStatus); - } + dvmChangeStatus(self, oldStatus); } return true; @@ -285,9 +279,25 @@ static void *tryMalloc(size_t size) return ptr; } - /* The allocation failed. Free up some space by doing - * a full garbage collection. This may grow the heap - * if the live set is sufficiently large. + /* + * The allocation failed. If the GC is running, block until it + * completes and retry. + */ + if (gDvm.gcHeap->gcRunning) { + /* + * The GC is concurrently tracing the heap. Release the heap + * lock, wait for the GC to complete, and retrying allocating. + */ + dvmWaitForConcurrentGcToComplete(); + ptr = dvmHeapSourceAlloc(size); + if (ptr != NULL) { + return ptr; + } + } + /* + * Another failure. Our thread was starved or there may be too + * many live objects. Try a foreground GC. This will have no + * effect if the concurrent GC is already running. */ gcForMalloc(false); ptr = dvmHeapSourceAlloc(size); @@ -573,11 +583,10 @@ size_t dvmObjectSizeInHeap(const Object *obj) /* * Scan every live object in the heap, holding the locks. */ -static void verifyHeap() +static void verifyHeap(void) { - // TODO: check the locks. - HeapBitmap *liveBits = dvmHeapSourceGetLiveBits(); - dvmVerifyBitmap(liveBits); + dvmVerifyRoots(); + dvmVerifyBitmap(dvmHeapSourceGetLiveBits()); } /* @@ -597,9 +606,9 @@ static void verifyHeap() void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason) { GcHeap *gcHeap = gDvm.gcHeap; - u8 now; - s8 timeSinceLastGc; - s8 gcElapsedTime; + u4 suspendStart, totalTime; + u4 rootStart, rootEnd, rootTime, rootSuspendTime; + u4 dirtyStart, dirtyEnd, dirtyTime, dirtySuspendTime; int numFreed; size_t sizeFreed; GcMode gcMode; @@ -612,19 +621,13 @@ void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason) LOGW_HEAP("Attempted recursive GC\n"); return; } + gcMode = (reason == GC_FOR_MALLOC) ? GC_PARTIAL : GC_FULL; gcHeap->gcRunning = true; - now = dvmGetRelativeTimeUsec(); - if (gcHeap->gcStartTime != 0) { - timeSinceLastGc = (now - gcHeap->gcStartTime) / 1000; - } else { - timeSinceLastGc = 0; - } - gcHeap->gcStartTime = now; - - LOGV_HEAP("%s starting -- suspending threads\n", GcReasonStr[reason]); + suspendStart = dvmGetRelativeTimeMsec(); dvmSuspendAllThreads(SUSPEND_FOR_GC); + rootStart = dvmGetRelativeTimeMsec(); /* * If we are not marking concurrently raise the priority of the @@ -729,14 +732,6 @@ void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason) } #endif - if (timeSinceLastGc < 10000) { - LOGD_HEAP("GC! (%dms since last GC)\n", - (int)timeSinceLastGc); - } else { - LOGD_HEAP("GC! (%d sec since last GC)\n", - (int)(timeSinceLastGc / 1000)); - } - /* Set up the marking context. */ if (!dvmHeapBeginMarkStep(gcMode)) { @@ -758,10 +753,15 @@ void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason) if (reason == GC_CONCURRENT) { /* - * We are performing a concurrent collection. Resume all - * threads for the duration of the recursive mark. + * Resume threads while tracing from the roots. We unlock the + * heap to allow mutator threads to allocate from free space. */ + rootEnd = dvmGetRelativeTimeMsec(); + dvmClearCardTable(); + dvmUnlockHeap(); dvmResumeAllThreads(SUSPEND_FOR_GC); + rootSuspendTime = rootStart - suspendStart; + rootTime = rootEnd - rootStart; } /* Recursively mark any objects that marked objects point to strongly. @@ -773,20 +773,23 @@ void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason) if (reason == GC_CONCURRENT) { /* - * We are performing a concurrent collection. Perform the - * final thread suspension. + * Re-acquire the heap lock and perform the final thread + * suspension. */ + dvmLockHeap(); + suspendStart = dvmGetRelativeTimeMsec(); dvmSuspendAllThreads(SUSPEND_FOR_GC); + dirtyStart = dvmGetRelativeTimeMsec(); /* * As no barrier intercepts root updates, we conservatively * assume all roots may be gray and re-mark them. */ - dvmHeapMarkRootSet(); + dvmHeapReMarkRootSet(); /* * Recursively mark gray objects pointed to by the roots or by * heap objects dirtied during the concurrent mark. */ - dvmMarkDirtyObjects(); + dvmHeapReScanMarkedObjects(); } /* All strongly-reachable objects have now been marked. @@ -891,7 +894,23 @@ void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason) dvmCompilerPerformSafePointChecks(); #endif + dirtyEnd = dvmGetRelativeTimeMsec(); + + if (reason == GC_CONCURRENT) { + dirtySuspendTime = dirtyStart - suspendStart; + dirtyTime = dirtyEnd - dirtyStart; + } + dvmResumeAllThreads(SUSPEND_FOR_GC); + + if (reason == GC_CONCURRENT) { + /* + * Wake-up any threads that blocked after a failed allocation + * request. + */ + dvmBroadcastCond(&gDvm.gcHeapCond); + } + if (reason != GC_CONCURRENT) { if (oldThreadPriority != kInvalidPriority) { if (setpriority(PRIO_PROCESS, 0, oldThreadPriority) != 0) { @@ -906,11 +925,22 @@ void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason) } } } - gcElapsedTime = (dvmGetRelativeTimeUsec() - gcHeap->gcStartTime) / 1000; - LOGD("%s freed %d objects / %zd bytes in %dms\n", - GcReasonStr[reason], numFreed, sizeFreed, (int)gcElapsedTime); - dvmLogGcStats(numFreed, sizeFreed, gcElapsedTime); + if (reason != GC_CONCURRENT) { + u4 suspendTime = rootStart - suspendStart; + u4 markSweepTime = dirtyEnd - rootStart; + totalTime = suspendTime + markSweepTime; + LOGD("%s freed %d objects / %zd bytes in (%ums) %ums", + GcReasonStr[reason], numFreed, sizeFreed, + suspendTime, markSweepTime); + } else { + totalTime = rootSuspendTime + rootTime + dirtySuspendTime + dirtyTime; + LOGD("%s freed %d objects / %zd bytes in (%ums) %ums (%ums) %ums", + GcReasonStr[reason], numFreed, sizeFreed, + rootSuspendTime, rootTime, + dirtySuspendTime, dirtyTime); + } + dvmLogGcStats(numFreed, sizeFreed, totalTime); if (gcHeap->ddmHpifWhen != 0) { LOGD_HEAP("Sending VM heap info to DDM\n"); dvmDdmSendHeapInfo(gcHeap->ddmHpifWhen, false); @@ -925,6 +955,16 @@ void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason) } } +void dvmWaitForConcurrentGcToComplete(void) +{ + Thread *self = dvmThreadSelf(); + ThreadStatus oldStatus; + assert(self != NULL); + oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); + dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock); + dvmChangeStatus(self, oldStatus); +} + #if WITH_HPROF /* * Perform garbage collection, writing heap information to the specified file. diff --git a/vm/alloc/Heap.h b/vm/alloc/Heap.h index 95740c103..ea0510f91 100644 --- a/vm/alloc/Heap.h +++ b/vm/alloc/Heap.h @@ -83,4 +83,10 @@ typedef enum { */ void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason); +/* + * Blocks the until the GC thread signals the completion of a + * concurrent GC. + */ +void dvmWaitForConcurrentGcToComplete(void); + #endif // _DALVIK_ALLOC_HEAP diff --git a/vm/alloc/HeapBitmap.c b/vm/alloc/HeapBitmap.c index 4b5c0a3ce..2f412dc5c 100644 --- a/vm/alloc/HeapBitmap.c +++ b/vm/alloc/HeapBitmap.c @@ -19,12 +19,6 @@ #include "clz.h" #include <limits.h> // for ULONG_MAX #include <sys/mman.h> // for madvise(), mmap() -#include <cutils/ashmem.h> - -#define HB_ASHMEM_NAME "dalvik-heap-bitmap" - -#define ALIGN_UP_TO_PAGE_SIZE(p) \ - (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1)) #define LIKELY(exp) (__builtin_expect((exp) != 0, true)) #define UNLIKELY(exp) (__builtin_expect((exp) != 0, false)) @@ -40,40 +34,19 @@ dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize, { void *bits; size_t bitsLen; - size_t allocLen; - int fd; - char nameBuf[ASHMEM_NAME_LEN] = HB_ASHMEM_NAME; assert(hb != NULL); - + assert(name != NULL); bitsLen = HB_OFFSET_TO_INDEX(maxSize) * sizeof(*hb->bits); - allocLen = ALIGN_UP_TO_PAGE_SIZE(bitsLen); // required by ashmem - - if (name != NULL) { - snprintf(nameBuf, sizeof(nameBuf), HB_ASHMEM_NAME "/%s", name); - } - fd = ashmem_create_region(nameBuf, allocLen); - if (fd < 0) { - LOGE("Could not create %zu-byte ashmem region \"%s\" to cover " - "%zu-byte heap (%d)\n", - allocLen, nameBuf, maxSize, fd); + bits = dvmAllocRegion(bitsLen, PROT_READ | PROT_WRITE, name); + if (bits == NULL) { + LOGE("Could not mmap %zd-byte ashmem region '%s'", bitsLen, name); return false; } - - bits = mmap(NULL, bitsLen, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - close(fd); - if (bits == MAP_FAILED) { - LOGE("Could not mmap %d-byte ashmem region \"%s\"\n", - bitsLen, nameBuf); - return false; - } - - memset(hb, 0, sizeof(*hb)); hb->bits = bits; hb->bitsLen = hb->allocLen = bitsLen; hb->base = (uintptr_t)base; hb->max = hb->base - 1; - return true; } @@ -195,7 +168,7 @@ dvmHeapBitmapXorWalk(const HeapBitmap *hb1, const HeapBitmap *hb2, /* First, walk along the section of the bitmaps that may be the same. */ if (hb1->max >= hb1->base && hb2->max >= hb2->base) { - unsigned long int *p1, *p2; + unsigned long *p1, *p2; uintptr_t offset; offset = ((hb1->max < hb2->max) ? hb1->max : hb2->max) - hb1->base; @@ -206,7 +179,7 @@ dvmHeapBitmapXorWalk(const HeapBitmap *hb1, const HeapBitmap *hb2, p2 = hb2->bits; for (i = 0; i <= index; i++) { //TODO: unroll this. pile up a few in locals? - unsigned long int diff = *p1++ ^ *p2++; + unsigned long diff = *p1++ ^ *p2++; DECODE_BITS(hb1, diff, false); //BUG: if the callback was called, either max could have changed. } @@ -223,7 +196,7 @@ dvmHeapBitmapXorWalk(const HeapBitmap *hb1, const HeapBitmap *hb2, * set bits. */ const HeapBitmap *longHb; -unsigned long int *p; +unsigned long *p; //TODO: may be the same size, in which case this is wasted work longHb = (hb1->max > hb2->max) ? hb1 : hb2; i = index; @@ -236,11 +209,13 @@ unsigned long int *p; } if (pb > pointerBuf) { - /* Set the finger to the end of the heap (rather than longHb->max) - * so that the callback doesn't expect to be called again - * if it happens to change the current max. + /* Set the finger to the end of the heap (rather than + * longHb->max) so that the callback doesn't expect to be + * called again if it happens to change the current max. */ - FLUSH_POINTERBUF(longHb->base + HB_MAX_OFFSET(longHb)); + uintptr_t finalFinger = longHb->base + HB_MAX_OFFSET(longHb); + FLUSH_POINTERBUF(finalFinger); + assert(finalFinger > longHb->max); } return true; diff --git a/vm/alloc/HeapBitmap.h b/vm/alloc/HeapBitmap.h index fd3585829..2a4fe2c42 100644 --- a/vm/alloc/HeapBitmap.h +++ b/vm/alloc/HeapBitmap.h @@ -19,7 +19,7 @@ #include <stdint.h> #define HB_OBJECT_ALIGNMENT 8 -#define HB_BITS_PER_WORD (sizeof (unsigned long int) * 8) +#define HB_BITS_PER_WORD (sizeof (unsigned long) * 8) /* <offset> is the difference from .base to a pointer address. * <index> is the index of .bits that contains the bit representing @@ -50,11 +50,11 @@ static inline p -typedef struct { +struct HeapBitmap { /* The bitmap data, which points to an mmap()ed area of zeroed * anonymous memory. */ - unsigned long int *bits; + unsigned long *bits; /* The size of the used memory pointed to by bits, in bytes. This * value changes when the bitmap is shrunk. @@ -77,7 +77,8 @@ typedef struct { * to a set bit. If there are no bits set, (max < base). */ uintptr_t max; -} HeapBitmap; +}; +typedef struct HeapBitmap HeapBitmap; /* @@ -148,28 +149,26 @@ HB_INLINE_PROTO( * Internal function; do not call directly. */ HB_INLINE_PROTO( - unsigned long int + unsigned long _heapBitmapModifyObjectBit(HeapBitmap *hb, const void *obj, bool setBit, bool returnOld) ) { const uintptr_t offset = (uintptr_t)obj - hb->base; const size_t index = HB_OFFSET_TO_INDEX(offset); - const unsigned long int mask = HB_OFFSET_TO_MASK(offset); + const unsigned long mask = HB_OFFSET_TO_MASK(offset); -#ifndef NDEBUG assert(hb->bits != NULL); assert((uintptr_t)obj >= hb->base); assert(index < hb->bitsLen / sizeof(*hb->bits)); -#endif if (setBit) { if ((uintptr_t)obj > hb->max) { hb->max = (uintptr_t)obj; } if (returnOld) { - unsigned long int *p = hb->bits + index; - const unsigned long int word = *p; + unsigned long *p = hb->bits + index; + const unsigned long word = *p; *p |= mask; return word & mask; } else { @@ -190,7 +189,7 @@ HB_INLINE_PROTO( * set bits will be lost. */ HB_INLINE_PROTO( - unsigned long int + unsigned long dvmHeapBitmapSetAndReturnObjectBit(HeapBitmap *hb, const void *obj) ) { @@ -228,7 +227,7 @@ HB_INLINE_PROTO( * set bits will be lost. */ HB_INLINE_PROTO( - unsigned long int + unsigned long dvmHeapBitmapIsObjectBitSet(const HeapBitmap *hb, const void *obj) ) { diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h index 112fc914f..773ebc389 100644 --- a/vm/alloc/HeapInternal.h +++ b/vm/alloc/HeapInternal.h @@ -93,12 +93,9 @@ struct GcHeap { */ GcMarkContext markContext; - /* Set to dvmGetRelativeTimeUsec() whenever a GC begins. - * The value is preserved between GCs, so it can be used - * to determine the time between successive GCs. - * Initialized to zero before the first GC. - */ - u8 gcStartTime; + /* GC's card table */ + u1* cardTableBase; + size_t cardTableLength; /* Is the GC running? Used to avoid recursive calls to GC. */ diff --git a/vm/alloc/HeapSource.c b/vm/alloc/HeapSource.c index 544230d72..62c0740eb 100644 --- a/vm/alloc/HeapSource.c +++ b/vm/alloc/HeapSource.c @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <cutils/ashmem.h> #include <cutils/mspace.h> #include <limits.h> // for INT_MAX #include <sys/mman.h> @@ -285,8 +284,7 @@ ptr2heap(const HeapSource *hs, const void *ptr) * * These aren't exact, and should not be treated as such. */ -static inline void -countAllocation(Heap *heap, const void *ptr, bool isObj) +static void countAllocation(Heap *heap, const void *ptr, bool isObj) { HeapSource *hs; @@ -474,7 +472,6 @@ dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize) mspace msp; size_t length; void *base; - int fd, ret; assert(gHs == NULL); @@ -489,18 +486,10 @@ dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize) * among the heaps managed by the garbage collector. */ length = ALIGN_UP_TO_PAGE_SIZE(absoluteMaxSize); - fd = ashmem_create_region("dalvik-heap", length); - if (fd == -1) { + base = dvmAllocRegion(length, PROT_NONE, "dalvik-heap"); + if (base == NULL) { return NULL; } - base = mmap(NULL, length, PROT_NONE, MAP_PRIVATE, fd, 0); - if (base == MAP_FAILED) { - return NULL; - } - ret = close(fd); - if (ret == -1) { - goto fail; - } /* Create an unlocked dlmalloc mspace to use as * the small object heap source. @@ -599,7 +588,9 @@ dvmHeapSourceStartupBeforeFork() void dvmHeapSourceThreadShutdown(void) { - gcDaemonShutdown(); + if (gDvm.gcHeap != NULL) { + gcDaemonShutdown(); + } } /* @@ -628,6 +619,14 @@ dvmHeapSourceShutdown(GcHeap **gcHeap) } /* + * Gets the begining of the allocation for the HeapSource. + */ +void *dvmHeapSourceGetBase(void) +{ + return gHs->heapBase; +} + +/* * Returns the requested value. If the per-heap stats are requested, fill * them as well. * @@ -689,7 +688,12 @@ static void aliasBitmap(HeapBitmap *dst, HeapBitmap *src, dst->base = base; dst->max = max; - dst->bitsLen = HB_OFFSET_TO_BYTE_INDEX(max - base); + dst->bitsLen = HB_OFFSET_TO_BYTE_INDEX(max - base) + sizeof(dst->bits); + /* The exclusive limit from bitsLen is greater than the inclusive max. */ + assert(base + HB_MAX_OFFSET(dst) > max); + /* The exclusive limit is at most one word of bits beyond max. */ + assert((base + HB_MAX_OFFSET(dst)) - max <= + HB_OBJECT_ALIGNMENT * HB_BITS_PER_WORD); dst->allocLen = dst->bitsLen; offset = base - src->base; assert(HB_OFFSET_TO_MASK(offset) == 1 << 31); @@ -714,7 +718,10 @@ void dvmHeapSourceGetObjectBitmaps(HeapBitmap liveBits[], HeapBitmap markBits[], assert(numHeaps == hs->numHeaps); for (i = 0; i < hs->numHeaps; ++i) { base = (uintptr_t)hs->heaps[i].base; - max = (uintptr_t)hs->heaps[i].limit - 1; + /* Using liveBits.max will include all the markBits as well. */ + assert(hs->liveBits.max >= hs->markBits.max); + /* -1 because limit is exclusive but max is inclusive. */ + max = MIN((uintptr_t)hs->heaps[i].limit - 1, hs->liveBits.max); aliasBitmap(&liveBits[i], &hs->liveBits, base, max); aliasBitmap(&markBits[i], &hs->markBits, base, max); } @@ -760,7 +767,6 @@ void dvmMarkImmuneObjects(const char *immuneLimit) for (i = 1; i < gHs->numHeaps; ++i) { if (gHs->heaps[i].base < immuneLimit) { assert(gHs->heaps[i].limit <= immuneLimit); - LOGV("Copying markBits for immune heap %d", i); /* Compute the number of words to copy in the bitmap. */ index = HB_OFFSET_TO_INDEX( (uintptr_t)gHs->heaps[i].base - gHs->liveBits.base); @@ -789,28 +795,41 @@ dvmHeapSourceAlloc(size_t n) HeapSource *hs = gHs; Heap *heap; void *ptr; + size_t allocated; HS_BOILERPLATE(); heap = hs2heap(hs); - - if (heap->bytesAllocated + n <= hs->softLimit) { - ptr = mspace_calloc(heap->msp, 1, n); - if (ptr != NULL) { - countAllocation(heap, ptr, true); - size_t allocated = heap->bytesAllocated - heap->prevBytesAllocated; - if (allocated > OCCUPANCY_THRESHOLD) { - if (hs->hasGcThread == true) { - dvmSignalCond(&gHs->gcThreadCond); - } - } - } - } else { - /* This allocation would push us over the soft limit; - * act as if the heap is full. + if (heap->bytesAllocated + n > hs->softLimit) { + /* + * This allocation would push us over the soft limit; act as + * if the heap is full. */ LOGV_HEAP("softLimit of %zd.%03zdMB hit for %zd-byte allocation\n", - FRACTIONAL_MB(hs->softLimit), n); - ptr = NULL; + FRACTIONAL_MB(hs->softLimit), n); + return NULL; + } + ptr = mspace_calloc(heap->msp, 1, n); + if (ptr == NULL) { + return NULL; + } + countAllocation(heap, ptr, true); + /* + * Check to see if a concurrent GC should be initiated. + */ + if (gDvm.gcHeap->gcRunning || !hs->hasGcThread) { + /* + * The garbage collector thread is already running or has yet + * to be started. Do nothing. + */ + return ptr; + } + allocated = heap->bytesAllocated - heap->prevBytesAllocated; + if (allocated > OCCUPANCY_THRESHOLD) { + /* + * We have exceeded the occupancy threshold. Wake up the + * garbage collector. + */ + dvmSignalCond(&gHs->gcThreadCond); } return ptr; } @@ -1653,6 +1672,18 @@ dvmTrackExternalAllocation(size_t n) goto out; } + if (gDvm.gcHeap->gcRunning) { + /* + * The GC is concurrently tracing the heap. Release the heap + * lock, wait for the GC to complete, and try again. + */ + dvmWaitForConcurrentGcToComplete(); + if (externalAlloc(hs, n, false)) { + ret = true; + goto out; + } + } + /* The "allocation" failed. Free up some space by doing * a full garbage collection. This may grow the heap source * if the live set is sufficiently large. diff --git a/vm/alloc/HeapSource.h b/vm/alloc/HeapSource.h index fb8fdf1e3..be8122023 100644 --- a/vm/alloc/HeapSource.h +++ b/vm/alloc/HeapSource.h @@ -74,6 +74,11 @@ void dvmHeapSourceGetObjectBitmaps(HeapBitmap objBits[], HeapBitmap markBits[], HeapBitmap *dvmHeapSourceGetLiveBits(void); /* + * Gets the begining of the allocation for the HeapSource. + */ +void *dvmHeapSourceGetBase(void); + +/* * Returns the requested value. If the per-heap stats are requested, fill * them as well. */ diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c index 1abdffa7e..94179e4aa 100644 --- a/vm/alloc/MarkSweep.c +++ b/vm/alloc/MarkSweep.c @@ -23,7 +23,6 @@ #include "alloc/Visit.h" #include <limits.h> // for ULONG_MAX #include <sys/mman.h> // for madvise(), mmap() -#include <cutils/ashmem.h> #include <errno.h> #define GC_LOG_TAG LOG_TAG "-gc" @@ -57,8 +56,8 @@ static bool createMarkStack(GcMarkStack *stack) { const Object **limit; + const char *name; size_t size; - int fd, err; /* Create a stack big enough for the worst possible case, * where the heap is perfectly full of the smallest object. @@ -68,27 +67,15 @@ createMarkStack(GcMarkStack *stack) size = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD); size = ALIGN_UP_TO_PAGE_SIZE(size); - fd = ashmem_create_region("dalvik-heap-markstack", size); - if (fd < 0) { - LOGE_GC("Could not create %d-byte ashmem mark stack: %s\n", - size, strerror(errno)); + name = "dalvik-mark-stack"; + limit = dvmAllocRegion(size, PROT_READ | PROT_WRITE, name); + if (limit == NULL) { + LOGE_GC("Could not mmap %zd-byte ashmem region '%s'", size, name); return false; } - limit = (const Object **)mmap(NULL, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE, fd, 0); - err = errno; - close(fd); - if (limit == MAP_FAILED) { - LOGE_GC("Could not mmap %d-byte ashmem mark stack: %s\n", - size, strerror(err)); - return false; - } - - memset(stack, 0, sizeof(*stack)); stack->limit = limit; stack->base = (const Object **)((uintptr_t)limit + size); stack->top = stack->base; - return true; } @@ -125,8 +112,7 @@ setAndReturnMarkBit(GcMarkContext *ctx, const void *obj) } static void -markObjectNonNull(const Object *obj, GcMarkContext *ctx, - bool checkFinger, bool forceStack) +markObjectNonNull(const Object *obj, GcMarkContext *ctx, bool checkFinger) { assert(ctx != NULL); assert(obj != NULL); @@ -139,7 +125,7 @@ markObjectNonNull(const Object *obj, GcMarkContext *ctx, if (!setAndReturnMarkBit(ctx, obj)) { /* This object was not previously marked. */ - if (forceStack || (checkFinger && (void *)obj < ctx->finger)) { + if (checkFinger && (void *)obj < ctx->finger) { /* This object will need to go on the mark stack. */ MARK_STACK_PUSH(ctx->stack, obj); @@ -162,7 +148,7 @@ markObjectNonNull(const Object *obj, GcMarkContext *ctx, static void markObject(const Object *obj, GcMarkContext *ctx) { if (obj != NULL) { - markObjectNonNull(obj, ctx, true, false); + markObjectNonNull(obj, ctx, true); } } @@ -179,7 +165,7 @@ void dvmMarkObjectNonNull(const Object *obj) { assert(obj != NULL); - markObjectNonNull(obj, &gDvm.gcHeap->markContext, false, false); + markObjectNonNull(obj, &gDvm.gcHeap->markContext, false); } /* Mark the set of root objects. @@ -269,6 +255,32 @@ void dvmHeapMarkRootSet() } /* + * Callback applied to root references. If the root location contains + * a white reference it is pushed on the mark stack and grayed. + */ +static void markObjectVisitor(void *addr, void *arg) +{ + Object *obj; + + assert(addr != NULL); + assert(arg != NULL); + obj = *(Object **)addr; + if (obj != NULL) { + markObjectNonNull(obj, arg, true); + } +} + +/* + * Grays all references in the roots. + */ +void dvmHeapReMarkRootSet(void) +{ + GcMarkContext *ctx = &gDvm.gcHeap->markContext; + assert(ctx->finger == (void *)ULONG_MAX); + dvmVisitRoots(markObjectVisitor, ctx); +} + +/* * Nothing past this point is allowed to use dvmMarkObject() or * dvmMarkObjectNonNull(), which are for root-marking only. * Scanning/recursion must use markObject(), which takes the finger @@ -369,76 +381,115 @@ static void scanArrayObject(const ArrayObject *obj, GcMarkContext *ctx) } /* + * Returns class flags relating to Reference subclasses. + */ +static int referenceClassFlags(const Object *obj) +{ + int flags = CLASS_ISREFERENCE | + CLASS_ISWEAKREFERENCE | + CLASS_ISPHANTOMREFERENCE; + return GET_CLASS_FLAG_GROUP(obj->clazz, flags); +} + +/* + * Returns true if the object derives from SoftReference. + */ +static bool isSoftReference(const Object *obj) +{ + return referenceClassFlags(obj) == CLASS_ISREFERENCE; +} + +/* + * Returns true if the object derives from WeakReference. + */ +static bool isWeakReference(const Object *obj) +{ + return referenceClassFlags(obj) & CLASS_ISWEAKREFERENCE; +} + +/* + * Returns true if the object derives from PhantomReference. + */ +static bool isPhantomReference(const Object *obj) +{ + return referenceClassFlags(obj) & CLASS_ISPHANTOMREFERENCE; +} + +/* + * Adds a reference to the tail of a circular queue of references. + */ +static void enqueuePendingReference(Object *ref, Object **list) +{ + size_t offset; + + assert(ref != NULL); + assert(list != NULL); + offset = gDvm.offJavaLangRefReference_pendingNext; + if (*list == NULL) { + dvmSetFieldObject(ref, offset, ref); + *list = ref; + } else { + Object *head = dvmGetFieldObject(*list, offset); + dvmSetFieldObject(ref, offset, head); + dvmSetFieldObject(*list, offset, ref); + } +} + +/* + * Removes the reference at the head of a circular queue of + * references. + */ +static Object *dequeuePendingReference(Object **list) +{ + Object *ref, *head; + size_t offset; + + assert(list != NULL); + assert(*list != NULL); + offset = gDvm.offJavaLangRefReference_pendingNext; + head = dvmGetFieldObject(*list, offset); + if (*list == head) { + ref = *list; + *list = NULL; + } else { + Object *next = dvmGetFieldObject(head, offset); + dvmSetFieldObject(*list, offset, next); + ref = head; + } + dvmSetFieldObject(ref, offset, NULL); + return ref; +} + +/* * Process the "referent" field in a java.lang.ref.Reference. If the * referent has not yet been marked, put it on the appropriate list in * the gcHeap for later processing. */ static void delayReferenceReferent(Object *obj, GcMarkContext *ctx) { + GcHeap *gcHeap = gDvm.gcHeap; + Object *pending, *referent; + size_t pendingNextOffset, referentOffset; + assert(obj != NULL); assert(obj->clazz != NULL); + assert(IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)); assert(ctx != NULL); - - GcHeap *gcHeap = gDvm.gcHeap; - Object *referent; - - /* It's a subclass of java/lang/ref/Reference. - * The fields in this class have been arranged - * such that scanInstanceFields() did not actually - * mark the "referent" field; we need to handle - * it specially. - * - * If the referent already has a strong mark (isMarked(referent)), - * we don't care about its reference status. - */ - referent = dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent); - if (referent != NULL && !isMarked(referent, ctx)) - { - u4 refFlags; - - /* Find out what kind of reference is pointing - * to referent. - */ - refFlags = GET_CLASS_FLAG_GROUP(obj->clazz, - CLASS_ISREFERENCE | - CLASS_ISWEAKREFERENCE | - CLASS_ISPHANTOMREFERENCE); - -#define ADD_REF_TO_LIST(list, ref) \ - do { \ - Object *ARTL_ref_ = ref; \ - assert(dvmGetFieldObject(ARTL_ref_, \ - gDvm.offJavaLangRefReference_queueNext) == NULL); \ - dvmSetFieldObject(ARTL_ref_, \ - gDvm.offJavaLangRefReference_queueNext, list); \ - list = ARTL_ref_; \ - } while (false) - - /* At this stage, we just keep track of all of - * the live references that we've seen. Later, - * we'll walk through each of these lists and - * deal with the referents. - */ - if (refFlags == CLASS_ISREFERENCE) { - /* It's a soft reference. Depending on the state, - * we'll attempt to collect all of them, some of - * them, or none of them. - */ - ADD_REF_TO_LIST(gcHeap->softReferences, obj); - } else { - /* It's a weak or phantom reference. - * Clearing CLASS_ISREFERENCE will reveal which. - */ - refFlags &= ~CLASS_ISREFERENCE; - if (refFlags == CLASS_ISWEAKREFERENCE) { - ADD_REF_TO_LIST(gcHeap->weakReferences, obj); - } else if (refFlags == CLASS_ISPHANTOMREFERENCE) { - ADD_REF_TO_LIST(gcHeap->phantomReferences, obj); - } else { - assert(!"Unknown reference type"); - } + pendingNextOffset = gDvm.offJavaLangRefReference_pendingNext; + referentOffset = gDvm.offJavaLangRefReference_referent; + pending = dvmGetFieldObject(obj, pendingNextOffset); + referent = dvmGetFieldObject(obj, referentOffset); + if (pending == NULL && referent != NULL && !isMarked(referent, ctx)) { + Object **list = NULL; + if (isSoftReference(obj)) { + list = &gcHeap->softReferences; + } else if (isWeakReference(obj)) { + list = &gcHeap->weakReferences; + } else if (isPhantomReference(obj)) { + list = &gcHeap->phantomReferences; } -#undef ADD_REF_TO_LIST + assert(list != NULL); + enqueuePendingReference(obj, list); } } @@ -498,6 +549,107 @@ processMarkStack(GcMarkContext *ctx) } } +static size_t objectSize(const Object *obj) +{ + assert(dvmIsValidObject(obj)); + assert(dvmIsValidObject((Object *)obj->clazz)); + if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) { + return dvmArrayObjectSize((ArrayObject *)obj); + } else if (obj->clazz == gDvm.classJavaLangClass) { + return dvmClassObjectSize((ClassObject *)obj); + } else { + return obj->clazz->objectSize; + } +} + +/* + * Scans backward to the header of a marked object that spans the + * given address. Returns NULL if there is no spanning marked object. + */ +static Object *previousGrayObject(u1 *start, GcMarkContext *ctx) +{ + u1 *end = (u1 *)ctx->bitmap->base; + u1 *ptr; + + assert(start >= end); + for (ptr = start; ptr >= end; ptr -= HB_OBJECT_ALIGNMENT) { + if (dvmIsValidObject((Object *)ptr)) + break; + } + if (ptr < end || !isMarked(ptr, ctx)) { + return NULL; + } else { + Object *obj = (Object *)ptr; + size_t size = objectSize(obj); + if (ptr + size < start) { + return NULL; + } + return obj; + } +} + +/* + * Scans forward to the header of the next marked object between start + * and limit. Returns NULL if no marked objects are in that region. + */ +static Object *nextGrayObject(u1 *base, u1 *limit, GcMarkContext *ctx) +{ + u1 *ptr; + + assert(base < limit); + assert(limit - base <= GC_CARD_SIZE); + for (ptr = base; ptr < limit; ptr += HB_OBJECT_ALIGNMENT) { + if (isMarked(ptr, ctx)) + return (Object *)ptr; + } + return NULL; +} + +/* + * Scan the card table looking for objects that have been grayed by + * the mutator. + */ +static void scanGrayObjects(GcMarkContext *ctx) +{ + GcHeap *h = gDvm.gcHeap; + u1 *card, *baseCard, *limitCard; + + baseCard = &h->cardTableBase[0]; + limitCard = &h->cardTableBase[h->cardTableLength]; + for (card = baseCard; card != limitCard; ++card) { + if (*card == GC_CARD_DIRTY) { + /* + * The card is dirty. Scan all of the objects that + * intersect with the card address. + */ + u1 *addr = dvmAddrFromCard(card); + /* + * If the last object on the previous card terminates on + * the current card it is gray and must be scanned. + */ + if (!dvmIsValidObject((Object *)addr)) { + Object *prev = previousGrayObject(addr, ctx); + if (prev != NULL) { + scanObject(prev, ctx); + } + } + /* + * Scan through all black objects that start on the + * current card. + */ + u1 *limit = addr + GC_CARD_SIZE; + u1 *next = addr; + while (next < limit) { + Object *obj = nextGrayObject(next, limit, ctx); + if (obj == NULL) + break; + scanObject(obj, ctx); + next = (u1*)obj + HB_OBJECT_ALIGNMENT; + } + } + } +} + #ifndef NDEBUG static uintptr_t gLastFinger = 0; #endif @@ -547,67 +699,26 @@ void dvmHeapScanMarkedObjects(void) LOG_SCAN("done with marked objects\n"); } -static void dirtyObjectVisitor(void *ptr, void *arg) +void dvmHeapReScanMarkedObjects(void) { - markObject(*(Object **)ptr, (GcMarkContext *)arg); -} - -/* - * Callback applied to each gray object to blacken it. - */ -static bool dirtyObjectCallback(size_t numPtrs, void **ptrs, - const void *finger, void *arg) -{ - GcMarkContext *ctx; - size_t i; - - ctx = (GcMarkContext *)arg; - for (i = 0; i < numPtrs; ++i) { - dvmVisitObject(dirtyObjectVisitor, ptrs[i], ctx); - } - return true; -} - -/* - * Re-mark dirtied objects. Iterates through all blackened objects - * looking for references to white objects. - */ -void dvmMarkDirtyObjects(void) -{ - HeapBitmap markBits[HEAP_SOURCE_MAX_HEAP_COUNT]; - HeapBitmap liveBits[HEAP_SOURCE_MAX_HEAP_COUNT]; - GcMarkContext *ctx; - size_t numBitmaps; - size_t i; + GcMarkContext *ctx = &gDvm.gcHeap->markContext; - ctx = &gDvm.gcHeap->markContext; /* * The finger must have been set to the maximum value to ensure * that gray objects will be pushed onto the mark stack. */ assert(ctx->finger == (void *)ULONG_MAX); - numBitmaps = dvmHeapSourceGetNumHeaps(); - dvmHeapSourceGetObjectBitmaps(liveBits, markBits, numBitmaps); - for (i = 0; i < numBitmaps; i++) { - dvmHeapBitmapWalk(&markBits[i], dirtyObjectCallback, ctx); - } + scanGrayObjects(ctx); processMarkStack(ctx); } -/** Clear the referent field. +/* + * Clear the referent field. */ static void clearReference(Object *reference) { - /* This is what the default implementation of Reference.clear() - * does. We're required to clear all references to a given - * referent atomically, so we can't pop in and out of interp - * code each time. - * - * We don't ever actaully call overriding implementations of - * Reference.clear(). - */ - dvmSetFieldObject(reference, - gDvm.offJavaLangRefReference_referent, NULL); + size_t offset = gDvm.offJavaLangRefReference_referent; + dvmSetFieldObject(reference, offset, NULL); } /* @@ -620,18 +731,7 @@ static bool isEnqueuable(const Object *reference) gDvm.offJavaLangRefReference_queue); Object *queueNext = dvmGetFieldObject(reference, gDvm.offJavaLangRefReference_queueNext); - if (queue == NULL || queueNext != NULL) { - /* There is no queue, or the reference has already - * been enqueued. The Reference.enqueue() method - * will do nothing even if we call it. - */ - return false; - } - - /* We need to call enqueue(), but if we called it from - * here we'd probably deadlock. Schedule a call. - */ - return true; + return queue != NULL && queueNext == NULL; } /* @@ -659,40 +759,31 @@ void dvmHandleSoftRefs(Object **list) { GcMarkContext *markContext; Object *ref, *referent; - Object *prev, *next; - size_t referentOffset, queueNextOffset; - unsigned counter; + Object *clear; + size_t referentOffset; + size_t counter; bool marked; markContext = &gDvm.gcHeap->markContext; - queueNextOffset = gDvm.offJavaLangRefReference_queueNext; referentOffset = gDvm.offJavaLangRefReference_referent; + clear = NULL; counter = 0; - prev = next = NULL; - ref = *list; - while (ref != NULL) { + while (*list != NULL) { + ref = dequeuePendingReference(list); referent = dvmGetFieldObject(ref, referentOffset); - next = dvmGetFieldObject(ref, queueNextOffset); assert(referent != NULL); marked = isMarked(referent, markContext); if (!marked && ((++counter) & 1)) { /* Referent is white and biased toward saving, mark it. */ - assert(referent != NULL); markObject(referent, markContext); marked = true; } - if (marked) { - /* Referent is black, unlink it. */ - if (prev != NULL) { - dvmSetFieldObject(ref, queueNextOffset, NULL); - dvmSetFieldObject(prev, queueNextOffset, next); - } - } else { - /* Referent is white, skip over it. */ - prev = ref; + if (!marked) { + /* Referent is white, queue it for clearing. */ + enqueuePendingReference(ref, &clear); } - ref = next; } + *list = clear; /* * Restart the mark with the newly black references added to the * root set. @@ -701,29 +792,26 @@ void dvmHandleSoftRefs(Object **list) } /* - * Walks the reference list and clears references with an unmarked - * (white) referents. Cleared references registered to a reference - * queue are scheduled for appending by the heap worker thread. + * Unlink the reference list clearing references objects with white + * referents. Cleared references registered to a reference queue are + * scheduled for appending by the heap worker thread. */ void dvmClearWhiteRefs(Object **list) { GcMarkContext *markContext; Object *ref, *referent; - size_t referentOffset, queueNextOffset; + size_t referentOffset; bool doSignal; markContext = &gDvm.gcHeap->markContext; - queueNextOffset = gDvm.offJavaLangRefReference_queueNext; referentOffset = gDvm.offJavaLangRefReference_referent; doSignal = false; while (*list != NULL) { - ref = *list; + ref = dequeuePendingReference(list); referent = dvmGetFieldObject(ref, referentOffset); - *list = dvmGetFieldObject(ref, queueNextOffset); - dvmSetFieldObject(ref, queueNextOffset, NULL); assert(referent != NULL); if (!isMarked(referent, markContext)) { - /* Referent is "white", clear it. */ + /* Referent is white, clear it. */ clearReference(ref); if (isEnqueuable(ref)) { enqueueReference(ref); @@ -877,12 +965,11 @@ sweepBitmapCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg) const ClassObject *const classJavaLangClass = gDvm.classJavaLangClass; const bool overwriteFree = gDvm.overwriteFree; size_t i; - void **origPtrs = ptrs; for (i = 0; i < numPtrs; i++) { Object *obj; - obj = (Object *)*ptrs++; + obj = (Object *)ptrs[i]; /* This assumes that java.lang.Class will never go away. * If it can, and we were the last reference to it, it @@ -910,7 +997,7 @@ sweepBitmapCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg) } // TODO: dvmHeapSourceFreeList has a loop, just like the above // does. Consider collapsing the two loops to save overhead. - dvmHeapSourceFreeList(numPtrs, origPtrs); + dvmHeapSourceFreeList(numPtrs, ptrs); return true; } diff --git a/vm/alloc/MarkSweep.h b/vm/alloc/MarkSweep.h index d344aaef2..46066c646 100644 --- a/vm/alloc/MarkSweep.h +++ b/vm/alloc/MarkSweep.h @@ -42,12 +42,14 @@ typedef struct { GcMarkStack stack; const char *immuneLimit; const void *finger; // only used while scanning/recursing. + bool crossGen; // used when scanning immune objects. } GcMarkContext; bool dvmHeapBeginMarkStep(GcMode mode); void dvmHeapMarkRootSet(void); +void dvmHeapReMarkRootSet(void); void dvmHeapScanMarkedObjects(void); -void dvmMarkDirtyObjects(void); +void dvmHeapReScanMarkedObjects(void); void dvmHandleSoftRefs(Object **list); void dvmClearWhiteRefs(Object **list); void dvmHeapScheduleFinalizations(void); diff --git a/vm/alloc/Verify.c b/vm/alloc/Verify.c index 2bb88088a..f71b0f1f0 100644 --- a/vm/alloc/Verify.c +++ b/vm/alloc/Verify.c @@ -16,23 +16,19 @@ #include "Dalvik.h" #include "alloc/HeapBitmap.h" -#include "alloc/HeapSource.h" #include "alloc/Verify.h" #include "alloc/Visit.h" /* - * Helper routine for verifyRefernce that masks low-tag bits before - * applying verification checks. TODO: eliminate the use of low-tag - * bits and move this code into verifyReference. + * Checks that the given reference points to a valid object. */ -static void verifyReferenceUnmask(const void *addr, uintptr_t mask) +static void verifyReference(void *addr, void *arg) { const Object *obj; - uintptr_t tmp; bool isValid; - tmp = (uintptr_t)*(const Object **)addr; - obj = (const Object *)(tmp & ~mask); + assert(addr != NULL); + obj = *(const Object **)addr; if (obj == NULL) { isValid = true; } else { @@ -44,28 +40,17 @@ static void verifyReferenceUnmask(const void *addr, uintptr_t mask) } } -/* - * Assertion that the given reference points to a valid object. - */ -static void verifyReference(const void *addr) -{ - verifyReferenceUnmask(addr, 0); -} - static void visitorCallback(void *addr, void *arg) { - verifyReference(addr); + verifyReference(addr, arg); } /* - * Verifies an object reference. Determines the type of the reference - * and dispatches to a specialized verification routine. + * Verifies an object reference. */ void dvmVerifyObject(const Object *obj) { - LOGV("Entering dvmVerifyObject(obj=%p)", obj); dvmVisitObject(visitorCallback, (Object *)obj, NULL); - LOGV("Exiting dvmVerifyObject(obj=%p)", obj); } /* @@ -77,7 +62,7 @@ static bool verifyBitmapCallback(size_t numPtrs, void **ptrs, size_t i; for (i = 0; i < numPtrs; i++) { - dvmVerifyObject(*ptrs++); + dvmVerifyObject(ptrs[i]); } return true; } @@ -93,177 +78,9 @@ void dvmVerifyBitmap(const HeapBitmap *bitmap) } /* - * Applies a verification function to all present values in the hash table. - */ -static void verifyHashTable(HashTable *table, - void (*callback)(const void *arg)) -{ - int i; - - assert(table != NULL); - assert(callback != NULL); - dvmHashTableLock(table); - for (i = 0; i < table->tableSize; ++i) { - const HashEntry *entry = &table->pEntries[i]; - if (entry->data != NULL && entry->data != HASH_TOMBSTONE) { - (*callback)(&entry->data); - } - } - dvmHashTableUnlock(table); -} - -/* - * Applies the verify routine to the given object. - */ -static void verifyStringReference(const void *arg) -{ - assert(arg != NULL); - verifyReferenceUnmask(arg, 0x1); -} - -/* - * Verifies all entries in the reference table. - */ -static void verifyReferenceTable(const ReferenceTable *table) -{ - Object **entry; - - assert(table != NULL); - for (entry = table->table; entry < table->nextEntry; ++entry) { - assert(entry != NULL); - verifyReference(entry); - } -} - -/* - * Verifies a large heap reference table. These objects are list - * heads. As such, it is valid for table to be NULL. - */ -static void verifyLargeHeapRefTable(const LargeHeapRefTable *table) -{ - for (; table != NULL; table = table->next) { - verifyReferenceTable(&table->refs); - } -} - -/* - * Verifies all stack slots. TODO: verify native methods. - */ -static void verifyThreadStack(const Thread *thread) -{ - const StackSaveArea *saveArea; - const u4 *framePtr; - - assert(thread != NULL); - framePtr = (const u4 *)thread->curFrame; - for (; framePtr != NULL; framePtr = saveArea->prevFrame) { - Method *method; - saveArea = SAVEAREA_FROM_FP(framePtr); - method = (Method *)saveArea->method; - if (method != NULL && !dvmIsNativeMethod(method)) { - const RegisterMap* pMap = dvmGetExpandedRegisterMap(method); - const u1* regVector = NULL; - int i; - - if (pMap != NULL) { - /* found map, get registers for this address */ - int addr = saveArea->xtra.currentPc - method->insns; - regVector = dvmRegisterMapGetLine(pMap, addr); - } - if (regVector == NULL) { - /* - * Either there was no register map or there is no - * info for the current PC. Perform a conservative - * scan. - */ - for (i = 0; i < method->registersSize; ++i) { - if (dvmIsValidObject((Object *)framePtr[i])) { - verifyReference(&framePtr[i]); - } - } - } else { - /* - * Precise scan. v0 is at the lowest address on the - * interpreted stack, and is the first bit in the - * register vector, so we can walk through the - * register map and memory in the same direction. - * - * A '1' bit indicates a live reference. - */ - u2 bits = 1 << 1; - for (i = 0; i < method->registersSize; ++i) { - bits >>= 1; - if (bits == 1) { - /* set bit 9 so we can tell when we're empty */ - bits = *regVector++ | 0x0100; - } - if ((bits & 0x1) != 0) { - /* - * Register is marked as live, it's a valid root. - */ - verifyReference(&framePtr[i]); - } - } - dvmReleaseRegisterMapLine(pMap, regVector); - } - } - /* - * Don't fall into an infinite loop if things get corrupted. - */ - assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr || - saveArea->prevFrame == NULL); - } -} - -/* - * Verifies all roots associated with a thread. - */ -static void verifyThread(const Thread *thread) -{ - assert(thread != NULL); - assert(thread->status != THREAD_RUNNING || - thread->isSuspended || - thread == dvmThreadSelf()); - LOGV("Entering verifyThread(thread=%p)", thread); - verifyReference(&thread->threadObj); - verifyReference(&thread->exception); - verifyReferenceTable(&thread->internalLocalRefTable); - verifyReferenceTable(&thread->jniLocalRefTable); - if (thread->jniMonitorRefTable.table) { - verifyReferenceTable(&thread->jniMonitorRefTable); - } - verifyThreadStack(thread); - LOGV("Exiting verifyThread(thread=%p)", thread); -} - -/* - * Verifies all threads on the thread list. - */ -static void verifyThreads(void) -{ - Thread *thread; - - dvmLockThreadList(dvmThreadSelf()); - thread = gDvm.threadList; - while (thread) { - verifyThread(thread); - thread = thread->next; - } - dvmUnlockThreadList(); -} - -/* - * Verifies roots. TODO: verify all roots. + * Verifies references in the roots. */ void dvmVerifyRoots(void) { - verifyHashTable(gDvm.loadedClasses, verifyReference); - verifyHashTable(gDvm.dbgRegistry, verifyReference); - verifyHashTable(gDvm.internedStrings, verifyStringReference); - verifyReferenceTable(&gDvm.jniGlobalRefTable); - verifyReferenceTable(&gDvm.jniPinRefTable); - verifyLargeHeapRefTable(gDvm.gcHeap->referenceOperations); - verifyLargeHeapRefTable(gDvm.gcHeap->pendingFinalizationRefs); - verifyThreads(); - /* TODO: verify cached global references. */ + dvmVisitRoots(verifyReference, NULL); } diff --git a/vm/alloc/Visit.c b/vm/alloc/Visit.c index 8d4722058..9a799f2de 100644 --- a/vm/alloc/Visit.c +++ b/vm/alloc/Visit.c @@ -16,154 +16,193 @@ #include "Dalvik.h" #include "alloc/clz.h" +#include "alloc/HeapInternal.h" #include "alloc/Visit.h" +#include "alloc/VisitInlines.h" /* - * Visits the instance fields of a class or data object. + * Visits all of the reference locations in an object. */ -static void visitInstanceFields(Visitor *visitor, Object *obj, void *arg) +void dvmVisitObject(Visitor *visitor, Object *obj, void *arg) { assert(visitor != NULL); assert(obj != NULL); assert(obj->clazz != NULL); - LOGV("Entering visitInstanceFields(visitor=%p,obj=%p)", visitor, obj); - if (obj->clazz->refOffsets != CLASS_WALK_SUPER) { - size_t refOffsets = obj->clazz->refOffsets; - while (refOffsets != 0) { - size_t rshift = CLZ(refOffsets); - size_t offset = CLASS_OFFSET_FROM_CLZ(rshift); - Object **ref = BYTE_OFFSET(obj, offset); - (*visitor)(ref, arg); - refOffsets &= ~(CLASS_HIGH_BIT >> rshift); - } - } else { - ClassObject *clazz; - for (clazz = obj->clazz; clazz != NULL; clazz = clazz->super) { - InstField *field = clazz->ifields; - int i; - for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) { - size_t offset = field->byteOffset; - Object **ref = BYTE_OFFSET(obj, offset); - (*visitor)(ref, arg); - } - } - } - LOGV("Exiting visitInstanceFields(visitor=%p,obj=%p)", visitor, obj); + visitObject(visitor, obj, arg); } /* - * Visits the static fields of a class object. + * Applies a verification function to all present values in the hash table. */ -static void visitStaticFields(Visitor *visitor, ClassObject *clazz, void *arg) +static void visitHashTable(Visitor *visitor, HashTable *table, void *arg) { int i; assert(visitor != NULL); - assert(clazz != NULL); - for (i = 0; i < clazz->sfieldCount; ++i) { - char ch = clazz->sfields[i].field.signature[0]; - if (ch == '[' || ch == 'L') { - (*visitor)(&clazz->sfields[i].value.l, arg); + assert(table != NULL); + dvmHashTableLock(table); + for (i = 0; i < table->tableSize; ++i) { + HashEntry *entry = &table->pEntries[i]; + if (entry->data != NULL && entry->data != HASH_TOMBSTONE) { + (*visitor)(&entry->data, arg); } } + dvmHashTableUnlock(table); } /* - * Visit the interfaces of a class object. + * Visits all entries in the reference table. */ -static void visitInterfaces(Visitor *visitor, ClassObject *clazz, void *arg) +static void visitReferenceTable(Visitor *visitor, const ReferenceTable *table, + void *arg) { - int i; + Object **entry; assert(visitor != NULL); - assert(clazz != NULL); - for (i = 0; i < clazz->interfaceCount; ++i) { - (*visitor)(&clazz->interfaces[i], arg); + assert(table != NULL); + for (entry = table->table; entry < table->nextEntry; ++entry) { + assert(entry != NULL); + (*visitor)(entry, arg); } } /* - * Visits all the references stored in a class object instance. + * Visits a large heap reference table. These objects are list heads. + * As such, it is valid for table to be NULL. */ -static void visitClassObject(Visitor *visitor, ClassObject *obj, void *arg) +static void visitLargeHeapRefTable(Visitor *visitor, LargeHeapRefTable *table, + void *arg) { assert(visitor != NULL); - assert(obj != NULL); - LOGV("Entering visitClassObject(visitor=%p,obj=%p)", visitor, obj); - assert(!strcmp(obj->obj.clazz->descriptor, "Ljava/lang/Class;")); - (*visitor)(&obj->obj.clazz, arg); - if (IS_CLASS_FLAG_SET(obj, CLASS_ISARRAY)) { - (*visitor)(&obj->elementClass, arg); + for (; table != NULL; table = table->next) { + visitReferenceTable(visitor, &table->refs, arg); } - if (obj->status > CLASS_IDX) { - (*visitor)(&obj->super, arg); - } - (*visitor)(&obj->classLoader, arg); - visitInstanceFields(visitor, (Object *)obj, arg); - visitStaticFields(visitor, obj, arg); - if (obj->status > CLASS_IDX) { - visitInterfaces(visitor, obj, arg); - } - LOGV("Exiting visitClassObject(visitor=%p,obj=%p)", visitor, obj); } /* - * Visits the class object and, if the array is typed as an object - * array, all of the array elements. + * Visits all stack slots. TODO: visit native methods. */ -static void visitArrayObject(Visitor *visitor, Object *obj, void *arg) +static void visitThreadStack(Visitor *visitor, Thread *thread, void *arg) { + const StackSaveArea *saveArea; + u4 *framePtr; + assert(visitor != NULL); - assert(obj != NULL); - assert(obj->clazz != NULL); - LOGV("Entering visitArrayObject(visitor=%p,obj=%p)", visitor, obj); - (*visitor)(&obj->clazz, arg); - if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISOBJECTARRAY)) { - ArrayObject *array = (ArrayObject *)obj; - Object **contents = (Object **)array->contents; - size_t i; - for (i = 0; i < array->length; ++i) { - (*visitor)(&contents[i], arg); + assert(thread != NULL); + framePtr = (u4 *)thread->curFrame; + for (; framePtr != NULL; framePtr = saveArea->prevFrame) { + Method *method; + saveArea = SAVEAREA_FROM_FP(framePtr); + method = (Method *)saveArea->method; + if (method != NULL && !dvmIsNativeMethod(method)) { + const RegisterMap* pMap = dvmGetExpandedRegisterMap(method); + const u1* regVector = NULL; + size_t i; + + if (pMap != NULL) { + /* found map, get registers for this address */ + int addr = saveArea->xtra.currentPc - method->insns; + regVector = dvmRegisterMapGetLine(pMap, addr); + } + if (regVector == NULL) { + /* + * Either there was no register map or there is no + * info for the current PC. Perform a conservative + * scan. + */ + for (i = 0; i < method->registersSize; ++i) { + if (dvmIsValidObject((Object *)framePtr[i])) { + (*visitor)(&framePtr[i], arg); + } + } + } else { + /* + * Precise scan. v0 is at the lowest address on the + * interpreted stack, and is the first bit in the + * register vector, so we can walk through the + * register map and memory in the same direction. + * + * A '1' bit indicates a live reference. + */ + u2 bits = 1 << 1; + for (i = 0; i < method->registersSize; ++i) { + bits >>= 1; + if (bits == 1) { + /* set bit 9 so we can tell when we're empty */ + bits = *regVector++ | 0x0100; + } + if ((bits & 0x1) != 0) { + /* + * Register is marked as live, it's a valid root. + */ + (*visitor)(&framePtr[i], arg); + } + } + dvmReleaseRegisterMapLine(pMap, regVector); + } } + /* + * Don't fall into an infinite loop if things get corrupted. + */ + assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr || + saveArea->prevFrame == NULL); } - LOGV("Exiting visitArrayObject(visitor=%p,obj=%p)", visitor, obj); } /* - * Visits the class object and reference typed instance fields of a - * data object. + * Visits all roots associated with a thread. */ -static void visitDataObject(Visitor *visitor, Object *obj, void *arg) +static void visitThread(Visitor *visitor, Thread *thread, void *arg) { assert(visitor != NULL); - assert(obj != NULL); - assert(obj->clazz != NULL); - LOGV("Entering visitDataObject(visitor=%p,obj=%p)", visitor, obj); - (*visitor)(&obj->clazz, arg); - visitInstanceFields(visitor, obj, arg); - if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) { - size_t offset = gDvm.offJavaLangRefReference_referent; - Object **ref = BYTE_OFFSET(obj, offset); - (*visitor)(ref, arg); + assert(thread != NULL); + assert(thread->status != THREAD_RUNNING || + thread->isSuspended || + thread == dvmThreadSelf()); + (*visitor)(&thread->threadObj, arg); + (*visitor)(&thread->exception, arg); + visitReferenceTable(visitor, &thread->internalLocalRefTable, arg); + visitReferenceTable(visitor, &thread->jniLocalRefTable, arg); + if (thread->jniMonitorRefTable.table) { + visitReferenceTable(visitor, &thread->jniMonitorRefTable, arg); } - LOGV("Exiting visitDataObject(visitor=%p,obj=%p)", visitor, obj); + visitThreadStack(visitor, thread, arg); } /* - * Visits all of the reference stored in an object. + * Visits all threads on the thread list. */ -void dvmVisitObject(Visitor *visitor, Object *obj, void *arg) +static void visitThreads(Visitor *visitor, void *arg) { + Thread *thread; + assert(visitor != NULL); - assert(obj != NULL); - assert(obj->clazz != NULL); - LOGV("Entering dvmVisitObject(visitor=%p,obj=%p)", visitor, obj); - if (obj->clazz == gDvm.classJavaLangClass) { - visitClassObject(visitor, (ClassObject *)obj, arg); - } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) { - visitArrayObject(visitor, obj, arg); - } else { - visitDataObject(visitor, obj, arg); + dvmLockThreadList(dvmThreadSelf()); + thread = gDvm.threadList; + while (thread) { + visitThread(visitor, thread, arg); + thread = thread->next; } - LOGV("Exiting dvmVisitObject(visitor=%p,obj=%p)", visitor, obj); + dvmUnlockThreadList(); +} + +/* + * Visits roots. TODO: visit all roots. + */ +void dvmVisitRoots(Visitor *visitor, void *arg) +{ + assert(visitor != NULL); + visitHashTable(visitor, gDvm.loadedClasses, arg); + visitHashTable(visitor, gDvm.dbgRegistry, arg); + visitHashTable(visitor, gDvm.internedStrings, arg); + visitHashTable(visitor, gDvm.literalStrings, arg); + visitReferenceTable(visitor, &gDvm.jniGlobalRefTable, arg); + visitReferenceTable(visitor, &gDvm.jniPinRefTable, arg); + visitLargeHeapRefTable(visitor, gDvm.gcHeap->referenceOperations, arg); + visitLargeHeapRefTable(visitor, gDvm.gcHeap->pendingFinalizationRefs, arg); + visitThreads(visitor, arg); + (*visitor)(&gDvm.outOfMemoryObj, arg); + (*visitor)(&gDvm.internalErrorObj, arg); + (*visitor)(&gDvm.noClassDefFoundErrorObj, arg); + /* TODO: visit cached global references. */ } diff --git a/vm/alloc/Visit.h b/vm/alloc/Visit.h index 343d5675d..488c7212e 100644 --- a/vm/alloc/Visit.h +++ b/vm/alloc/Visit.h @@ -19,8 +19,20 @@ #include "Dalvik.h" -typedef void Visitor(void *obj, void *arg); +/* + * Callback invoked with the address of a reference and a user + * supplied context argument. + */ +typedef void Visitor(void *addr, void *arg); +/* + * Visits references in an object. + */ void dvmVisitObject(Visitor *visitor, Object *obj, void *arg); +/* + * Visits references in the root set. + */ +void dvmVisitRoots(Visitor *visitor, void *arg); + #endif /* _DALVIK_ALLOC_VISIT */ diff --git a/vm/alloc/VisitInlines.h b/vm/alloc/VisitInlines.h new file mode 100644 index 000000000..84a456a6e --- /dev/null +++ b/vm/alloc/VisitInlines.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2010 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_VISITINLINES +#define _DALVIK_ALLOC_VISITINLINES + +/* + * Visits the instance fields of a class or data object. + */ +static void visitFields(Visitor *visitor, Object *obj, void *arg) +{ + assert(visitor != NULL); + assert(obj != NULL); + assert(obj->clazz != NULL); + if (obj->clazz->refOffsets != CLASS_WALK_SUPER) { + size_t refOffsets = obj->clazz->refOffsets; + while (refOffsets != 0) { + size_t rshift = CLZ(refOffsets); + size_t offset = CLASS_OFFSET_FROM_CLZ(rshift); + Object **ref = BYTE_OFFSET(obj, offset); + (*visitor)(ref, arg); + refOffsets &= ~(CLASS_HIGH_BIT >> rshift); + } + } else { + ClassObject *clazz; + for (clazz = obj->clazz; clazz != NULL; clazz = clazz->super) { + InstField *field = clazz->ifields; + int i; + for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) { + size_t offset = field->byteOffset; + Object **ref = BYTE_OFFSET(obj, offset); + (*visitor)(ref, arg); + } + } + } +} + +/* + * Visits the static fields of a class object. + */ +static void visitStaticFields(Visitor *visitor, ClassObject *clazz, + void *arg) +{ + int i; + + assert(visitor != NULL); + assert(clazz != NULL); + for (i = 0; i < clazz->sfieldCount; ++i) { + char ch = clazz->sfields[i].field.signature[0]; + if (ch == '[' || ch == 'L') { + (*visitor)(&clazz->sfields[i].value.l, arg); + } + } +} + +/* + * Visit the interfaces of a class object. + */ +static void visitInterfaces(Visitor *visitor, ClassObject *clazz, + void *arg) +{ + int i; + + assert(visitor != NULL); + assert(clazz != NULL); + for (i = 0; i < clazz->interfaceCount; ++i) { + (*visitor)(&clazz->interfaces[i], arg); + } +} + +/* + * Visits all the references stored in a class object instance. + */ +static void visitClassObject(Visitor *visitor, Object *obj, void *arg) +{ + ClassObject *classObj; + ClassStatus status; + + assert(visitor != NULL); + assert(obj != NULL); + assert(obj->clazz != NULL); + assert(!strcmp(obj->clazz->descriptor, "Ljava/lang/Class;")); + classObj = (ClassObject *)obj; + (*visitor)(&obj->clazz, arg); + if (IS_CLASS_FLAG_SET(classObj, CLASS_ISARRAY)) { + (*visitor)(&classObj->elementClass, arg); + } + status = classObj->status; + if (status > CLASS_IDX) { + (*visitor)(&classObj->super, arg); + } + (*visitor)(&classObj->classLoader, arg); + visitFields(visitor, obj, arg); + visitStaticFields(visitor, classObj, arg); + if (status > CLASS_IDX) { + visitInterfaces(visitor, classObj, arg); + } +} + +/* + * Visits the class object and, if the array is typed as an object + * array, all of the array elements. + */ +static void visitArrayObject(Visitor *visitor, Object *obj, void *arg) +{ + assert(visitor != NULL); + assert(obj != NULL); + assert(obj->clazz != NULL); + (*visitor)(&obj->clazz, arg); + if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISOBJECTARRAY)) { + ArrayObject *array = (ArrayObject *)obj; + Object **contents = (Object **)array->contents; + size_t i; + for (i = 0; i < array->length; ++i) { + (*visitor)(&contents[i], arg); + } + } +} + +/* + * Visits the class object and reference typed instance fields of a + * data object. + */ +static void visitDataObject(Visitor *visitor, Object *obj, void *arg) +{ + assert(visitor != NULL); + assert(obj != NULL); + assert(obj->clazz != NULL); + (*visitor)(&obj->clazz, arg); + visitFields(visitor, obj, arg); +} + +/* + * Like visitDataObject, but visits the hidden referent field that + * belongings to the subclasses of java.lang.Reference. + */ +static void visitReferenceObject(Visitor *visitor, Object *obj, void *arg) +{ + assert(visitor != NULL); + assert(obj != NULL); + assert(obj->clazz != NULL); + visitDataObject(visitor, obj, arg); + size_t offset = gDvm.offJavaLangRefReference_referent; + Object **ref = BYTE_OFFSET(obj, offset); + (*visitor)(ref, arg); +} + +/* + * Visits all of the reference stored in an object. + */ +static void visitObject(Visitor *visitor, Object *obj, void *arg) +{ + assert(visitor != NULL); + assert(obj != NULL); + assert(obj->clazz != NULL); + if (obj->clazz == gDvm.classJavaLangClass) { + visitClassObject(visitor, obj, arg); + } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) { + visitArrayObject(visitor, obj, arg); + } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) { + visitReferenceObject(visitor, obj, arg); + } else { + visitDataObject(visitor, obj, arg); + } +} + +#endif /* _DALVIK_ALLOC_VISITINLINES */ diff --git a/vm/alloc/WriteBarrier.h b/vm/alloc/WriteBarrier.h index fef993e7e..34b3b196c 100644 --- a/vm/alloc/WriteBarrier.h +++ b/vm/alloc/WriteBarrier.h @@ -23,13 +23,17 @@ /* * The address within the Object has been written, and perhaps changed. */ -INLINE void dvmWriteBarrierField(const Object *obj, void *addr) { +INLINE void dvmWriteBarrierField(const Object *obj, void *addr) +{ + dvmMarkCard(obj); } /* * All of the Object may have changed. */ -INLINE void dvmWriteBarrierObject(const Object *obj) { +INLINE void dvmWriteBarrierObject(const Object *obj) +{ + dvmMarkCard(obj); } /* @@ -37,6 +41,8 @@ INLINE void dvmWriteBarrierObject(const Object *obj) { * or equal to start and strictly less than end, have been written, * and perhaps changed. */ -INLINE void dvmWriteBarrierArray(const ArrayObject* obj, - size_t start, size_t end) { +INLINE void dvmWriteBarrierArray(const ArrayObject *obj, + size_t start, size_t end) +{ + dvmMarkCard((Object *)obj); } diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c index 4310a067f..3c7be1162 100644 --- a/vm/analysis/CodeVerify.c +++ b/vm/analysis/CodeVerify.c @@ -2946,6 +2946,9 @@ static bool replaceFailingInstruction(const Method* meth, InsnFlags* insnFlags, u2 oldInsn = *oldInsns; bool result = false; + if (gDvm.optimizing) + LOGD("Weird: RFI during dexopt?"); + //LOGD(" was 0x%04x\n", oldInsn); u2* newInsns = (u2*) meth->insns + insnIdx; @@ -3045,50 +3048,6 @@ bail: return result; } -/* - * Replace {iget,iput,sget,sput}-wide with the -wide-volatile form. - * - * If this is called during dexopt, we can modify the instruction in - * place. If this happens during just-in-time verification, we need to - * use the DEX read/write page feature. - * - * NOTE: - * This shouldn't really be tied to verification. It ought to be a - * separate pass that is run before or after the verifier. However, that - * requires a bunch of extra code, and the only advantage of doing so is - * that the feature isn't disabled when verification is turned off. At - * some point we may need to revisit this choice. - */ -static void replaceVolatileInstruction(const Method* meth, InsnFlags* insnFlags, - int insnIdx) -{ - u2* oldInsns = (u2*)meth->insns + insnIdx; - u2 oldInsn = *oldInsns; - u2 newVal; - - switch (oldInsn & 0xff) { - case OP_IGET_WIDE: newVal = OP_IGET_WIDE_VOLATILE; break; - case OP_IPUT_WIDE: newVal = OP_IPUT_WIDE_VOLATILE; break; - case OP_SGET_WIDE: newVal = OP_SGET_WIDE_VOLATILE; break; - case OP_SPUT_WIDE: newVal = OP_SPUT_WIDE_VOLATILE; break; - default: - LOGE("wide-volatile op mismatch (0x%x)\n", oldInsn); - dvmAbort(); - return; // in-lieu-of noreturn attribute - } - - /* merge new opcode into 16-bit code unit */ - newVal |= (oldInsn & 0xff00); - - if (gDvm.optimizing) { - /* dexopt time, alter the output */ - *oldInsns = newVal; - } else { - /* runtime, make the page read/write */ - dvmDexChangeDex2(meth->clazz->pDvmDex, oldInsns, newVal); - } -} - /* * =========================================================================== @@ -4539,13 +4498,6 @@ iget_1nr_common: setRegisterType(workRegs, insnRegCount, decInsn.vA, dstType, &failure); } - if (VERIFY_OK(failure)) { - if (decInsn.opCode != OP_IGET_WIDE_VOLATILE && - dvmIsVolatileField(&instField->field)) - { - replaceVolatileInstruction(meth, insnFlags, insnIdx); - } - } } break; case OP_IGET_OBJECT: @@ -4680,13 +4632,6 @@ iput_1nr_common: failure = VERIFY_ERROR_GENERIC; break; } - if (VERIFY_OK(failure)) { - if (decInsn.opCode != OP_IPUT_WIDE_VOLATILE && - dvmIsVolatileField(&instField->field)) - { - replaceVolatileInstruction(meth, insnFlags, insnIdx); - } - } } break; case OP_IPUT_OBJECT: @@ -4826,13 +4771,6 @@ sget_1nr_common: setRegisterType(workRegs, insnRegCount, decInsn.vA, dstType, &failure); } - if (VERIFY_OK(failure)) { - if (decInsn.opCode != OP_SGET_WIDE_VOLATILE && - dvmIsVolatileField(&staticField->field)) - { - replaceVolatileInstruction(meth, insnFlags, insnIdx); - } - } } break; case OP_SGET_OBJECT: @@ -4955,13 +4893,6 @@ sput_1nr_common: failure = VERIFY_ERROR_GENERIC; break; } - if (VERIFY_OK(failure)) { - if (decInsn.opCode != OP_SPUT_WIDE_VOLATILE && - dvmIsVolatileField(&staticField->field)) - { - replaceVolatileInstruction(meth, insnFlags, insnIdx); - } - } } break; case OP_SPUT_OBJECT: diff --git a/vm/analysis/DexPrepare.c b/vm/analysis/DexPrepare.c index 2bf33a350..d9871c99b 100644 --- a/vm/analysis/DexPrepare.c +++ b/vm/analysis/DexPrepare.c @@ -965,7 +965,7 @@ static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz, LOGV("DexOpt: not optimizing '%s': not verified\n", classDescriptor); } else { - dvmOptimizeClass(clazz); + dvmOptimizeClass(clazz, false); /* set the flag whether or not we actually changed anything */ ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISOPTIMIZED; diff --git a/vm/analysis/Optimize.c b/vm/analysis/Optimize.c index 670355379..4ac19f3f2 100644 --- a/vm/analysis/Optimize.c +++ b/vm/analysis/Optimize.c @@ -36,8 +36,10 @@ struct InlineSub { /* fwd */ -static bool optimizeMethod(Method* method); -static void rewriteInstField(Method* method, u2* insns, OpCode newOpc); +static void optimizeMethod(Method* method, bool essentialOnly); +static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc, + OpCode volatileOpc); +static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc); static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc); static bool rewriteEmptyDirectInvoke(Method* method, u2* insns); static bool rewriteExecuteInline(Method* method, u2* insns, @@ -132,25 +134,23 @@ void dvmFreeInlineSubsTable(InlineSub* inlineSubs) /* * Optimize the specified class. + * + * If "essentialOnly" is true, we only do essential optimizations. For + * example, accesses to volatile 64-bit fields must be replaced with + * "-wide-volatile" instructions or the program could behave incorrectly. + * (Skipping non-essential optimizations makes us a little bit faster, and + * more importantly avoids dirtying DEX pages.) */ -void dvmOptimizeClass(ClassObject* clazz) +void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly) { int i; for (i = 0; i < clazz->directMethodCount; i++) { - if (!optimizeMethod(&clazz->directMethods[i] )) - goto fail; + optimizeMethod(&clazz->directMethods[i], essentialOnly); } for (i = 0; i < clazz->virtualMethodCount; i++) { - if (!optimizeMethod(&clazz->virtualMethods[i])) - goto fail; + optimizeMethod(&clazz->virtualMethods[i], essentialOnly); } - - return; - -fail: - // TODO: show when in "verbose" mode - LOGV("DexOpt: ceasing optimization attempts on %s\n", clazz->descriptor); } /* @@ -158,101 +158,159 @@ fail: * * This does a single pass through the code, examining each instruction. * - * Returns "true" if all went well, "false" if we bailed out early when - * something failed. + * This is not expected to fail if the class was successfully verified. + * The only significant failure modes occur when an "essential" update fails, + * but we can't generally identify those: if we can't look up a field, + * we can't know if the field access was supposed to be handled as volatile. + * + * Instead, we give it our best effort, and hope for the best. For 100% + * reliability, only optimize a class after verification succeeds. */ -static bool optimizeMethod(Method* method) +static void optimizeMethod(Method* method, bool essentialOnly) { u4 insnsSize; u2* insns; u2 inst; + if (!gDvm.optimizing && !essentialOnly) { + /* unexpected; will force copy-on-write of a lot of pages */ + LOGD("NOTE: doing full bytecode optimization outside dexopt\n"); + } + if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method)) - return true; + return; insns = (u2*) method->insns; assert(insns != NULL); insnsSize = dvmGetMethodInsnsSize(method); while (insnsSize > 0) { + OpCode quickOpc, volatileOpc = OP_NOP; int width; + bool notMatched = false; inst = *insns & 0xff; switch (inst) { -#ifndef PROFILE_FIELD_ACCESS /* quickened instructions not instrumented */ case OP_IGET: case OP_IGET_BOOLEAN: case OP_IGET_BYTE: case OP_IGET_CHAR: case OP_IGET_SHORT: - rewriteInstField(method, insns, OP_IGET_QUICK); - break; + quickOpc = OP_IGET_QUICK; + if (ANDROID_SMP != 0) + volatileOpc = OP_IGET_VOLATILE; + goto rewrite_inst_field; case OP_IGET_WIDE: - rewriteInstField(method, insns, OP_IGET_WIDE_QUICK); - break; + quickOpc = OP_IGET_WIDE_QUICK; + volatileOpc = OP_IGET_WIDE_VOLATILE; + goto rewrite_inst_field; case OP_IGET_OBJECT: - rewriteInstField(method, insns, OP_IGET_OBJECT_QUICK); - break; + quickOpc = OP_IGET_OBJECT_QUICK; + if (ANDROID_SMP != 0) + volatileOpc = OP_IGET_OBJECT_VOLATILE; + goto rewrite_inst_field; case OP_IPUT: case OP_IPUT_BOOLEAN: case OP_IPUT_BYTE: case OP_IPUT_CHAR: case OP_IPUT_SHORT: - rewriteInstField(method, insns, OP_IPUT_QUICK); - break; + quickOpc = OP_IPUT_QUICK; + if (ANDROID_SMP != 0) + volatileOpc = OP_IPUT_VOLATILE; + goto rewrite_inst_field; case OP_IPUT_WIDE: - rewriteInstField(method, insns, OP_IPUT_WIDE_QUICK); - break; + quickOpc = OP_IPUT_WIDE_QUICK; + volatileOpc = OP_IPUT_WIDE_VOLATILE; + goto rewrite_inst_field; case OP_IPUT_OBJECT: - rewriteInstField(method, insns, OP_IPUT_OBJECT_QUICK); - break; -#endif - - case OP_INVOKE_VIRTUAL: - if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) { - if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_VIRTUAL_QUICK)) - return false; - } - break; - case OP_INVOKE_VIRTUAL_RANGE: - if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) { - if (!rewriteVirtualInvoke(method, insns, - OP_INVOKE_VIRTUAL_QUICK_RANGE)) - { - return false; - } - } - break; - case OP_INVOKE_SUPER: - if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK)) - return false; - break; - case OP_INVOKE_SUPER_RANGE: - if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE)) - return false; - break; - - case OP_INVOKE_DIRECT: - if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) { - if (!rewriteEmptyDirectInvoke(method, insns)) - return false; - } - break; - case OP_INVOKE_DIRECT_RANGE: - rewriteExecuteInlineRange(method, insns, METHOD_DIRECT); + quickOpc = OP_IPUT_OBJECT_QUICK; + if (ANDROID_SMP != 0) + volatileOpc = OP_IPUT_OBJECT_VOLATILE; +rewrite_inst_field: + if (essentialOnly) + quickOpc = OP_NOP; + if (quickOpc != OP_NOP || volatileOpc != OP_NOP) + rewriteInstField(method, insns, quickOpc, volatileOpc); break; - case OP_INVOKE_STATIC: - rewriteExecuteInline(method, insns, METHOD_STATIC); - break; - case OP_INVOKE_STATIC_RANGE: - rewriteExecuteInlineRange(method, insns, METHOD_STATIC); +#if ANDROID_SMP != 0 + case OP_SGET: + case OP_SGET_BOOLEAN: + case OP_SGET_BYTE: + case OP_SGET_CHAR: + case OP_SGET_SHORT: + volatileOpc = OP_SGET_VOLATILE; + goto rewrite_static_field; + case OP_SGET_OBJECT: + volatileOpc = OP_SGET_OBJECT_VOLATILE; + goto rewrite_static_field; + case OP_SPUT: + case OP_SPUT_BOOLEAN: + case OP_SPUT_BYTE: + case OP_SPUT_CHAR: + case OP_SPUT_SHORT: + volatileOpc = OP_SPUT_VOLATILE; + goto rewrite_static_field; + case OP_SPUT_OBJECT: + volatileOpc = OP_SPUT_OBJECT_VOLATILE; + goto rewrite_static_field; +#endif + case OP_SGET_WIDE: + volatileOpc = OP_SGET_WIDE_VOLATILE; + goto rewrite_static_field; + case OP_SPUT_WIDE: + volatileOpc = OP_SPUT_WIDE_VOLATILE; +rewrite_static_field: + rewriteStaticField(method, insns, volatileOpc); break; default: - // ignore this instruction - ; + /* not one of the "essential" replacements; check for more */ + notMatched = true; + } + + if (notMatched && !essentialOnly) { + switch (inst) { + case OP_INVOKE_VIRTUAL: + if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) { + rewriteVirtualInvoke(method, insns, + OP_INVOKE_VIRTUAL_QUICK); + } + break; + case OP_INVOKE_VIRTUAL_RANGE: + if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) { + rewriteVirtualInvoke(method, insns, + OP_INVOKE_VIRTUAL_QUICK_RANGE); + } + break; + case OP_INVOKE_SUPER: + rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK); + break; + case OP_INVOKE_SUPER_RANGE: + rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE); + break; + + case OP_INVOKE_DIRECT: + if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) { + rewriteEmptyDirectInvoke(method, insns); + } + break; + case OP_INVOKE_DIRECT_RANGE: + rewriteExecuteInlineRange(method, insns, METHOD_DIRECT); + break; + + case OP_INVOKE_STATIC: + rewriteExecuteInline(method, insns, METHOD_STATIC); + break; + case OP_INVOKE_STATIC_RANGE: + rewriteExecuteInlineRange(method, insns, METHOD_STATIC); + break; + + default: + /* nothing to do for this instruction */ + ; + } } width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns); @@ -263,9 +321,21 @@ static bool optimizeMethod(Method* method) } assert(insnsSize == 0); - return true; } +/* + * Update a 16-bit code unit in "meth". + */ +static inline void updateCode(const Method* meth, u2* ptr, u2 newVal) +{ + if (gDvm.optimizing) { + /* dexopt time, alter the output directly */ + *ptr = newVal; + } else { + /* runtime, toggle the page read/write status */ + dvmDexChangeDex2(meth->clazz->pDvmDex, ptr, newVal); + } +} /* * If "referrer" and "resClass" don't come from the same DEX file, and @@ -541,36 +611,90 @@ StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx, * op vA, vB, field@CCCC * * Where vA holds the value, vB holds the object reference, and CCCC is - * the field reference constant pool offset. We want to replace CCCC - * with the byte offset from the start of the object. + * the field reference constant pool offset. For a non-volatile field, + * we want to replace the opcode with "quickOpc" and replace CCCC with + * the byte offset from the start of the object. For a volatile field, + * we just want to replace the opcode with "volatileOpc". + * + * If "volatileOpc" is OP_NOP we don't check to see if it's a volatile + * field. If "quickOpc" is OP_NOP, and this is a non-volatile field, + * we don't do anything. * - * "clazz" is the referring class. We need this because we verify - * access rights here. + * "method" is the referring method. */ -static void rewriteInstField(Method* method, u2* insns, OpCode newOpc) +static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc, + OpCode volatileOpc) { ClassObject* clazz = method->clazz; u2 fieldIdx = insns[1]; - InstField* field; + InstField* instField; - field = dvmOptResolveInstField(clazz, fieldIdx, NULL); - if (field == NULL) { - LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n", + instField = dvmOptResolveInstField(clazz, fieldIdx, NULL); + if (instField == NULL) { + LOGI("DexOpt: unable to optimize instance field ref " + "0x%04x at 0x%02x in %s.%s\n", fieldIdx, (int) (insns - method->insns), clazz->descriptor, method->name); - return; + return false; } - if (field->byteOffset >= 65536) { - LOGI("DexOpt: field offset exceeds 64K (%d)\n", field->byteOffset); - return; + if (instField->byteOffset >= 65536) { + LOGI("DexOpt: field offset exceeds 64K (%d)\n", instField->byteOffset); + return false; + } + + if (volatileOpc != OP_NOP && dvmIsVolatileField(&instField->field)) { + updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc); + LOGV("DexOpt: rewrote ifield access %s.%s --> volatile\n", + instField->field.clazz->descriptor, instField->field.name); + } else if (quickOpc != OP_NOP) { + updateCode(method, insns, (insns[0] & 0xff00) | (u2) quickOpc); + updateCode(method, insns+1, (u2) instField->byteOffset); + LOGV("DexOpt: rewrote ifield access %s.%s --> %d\n", + instField->field.clazz->descriptor, instField->field.name, + instField->byteOffset); + } else { + LOGV("DexOpt: no rewrite of ifield access %s.%s\n", + instField->field.clazz->descriptor, instField->field.name); } - insns[0] = (insns[0] & 0xff00) | (u2) newOpc; - insns[1] = (u2) field->byteOffset; - LOGVV("DexOpt: rewrote access to %s.%s --> %d\n", - field->field.clazz->descriptor, field->field.name, - field->byteOffset); + return true; +} + +/* + * Rewrite an sget/sput instruction. These all have the form: + * op vAA, field@BBBB + * + * Where vAA holds the value, and BBBB is the field reference constant + * pool offset. There is no "quick" form of static field accesses, so + * this is only useful for volatile fields. + * + * "method" is the referring method. + */ +static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc) +{ + ClassObject* clazz = method->clazz; + u2 fieldIdx = insns[1]; + StaticField* staticField; + + assert(volatileOpc != OP_NOP); + + staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL); + if (staticField == NULL) { + LOGI("DexOpt: unable to optimize static field ref " + "0x%04x at 0x%02x in %s.%s\n", + fieldIdx, (int) (insns - method->insns), clazz->descriptor, + method->name); + return false; + } + + if (dvmIsVolatileField(&staticField->field)) { + updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc); + LOGV("DexOpt: rewrote sfield access %s.%s --> volatile\n", + staticField->field.clazz->descriptor, staticField->field.name); + } + + return true; } /* @@ -738,8 +862,8 @@ static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc) * Note: Method->methodIndex is a u2 and is range checked during the * initial load. */ - insns[0] = (insns[0] & 0xff00) | (u2) newOpc; - insns[1] = baseMethod->methodIndex; + updateCode(method, insns, (insns[0] & 0xff00) | (u2) newOpc); + updateCode(method, insns+1, baseMethod->methodIndex); //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n", // method->clazz->descriptor, method->name, @@ -783,7 +907,8 @@ static bool rewriteEmptyDirectInvoke(Method* method, u2* insns) * OP_INVOKE_DIRECT when debugging is enabled. */ assert((insns[0] & 0xff) == OP_INVOKE_DIRECT); - insns[0] = (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY; + updateCode(method, insns, + (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY); //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n", // method->clazz->descriptor, method->name, @@ -914,8 +1039,9 @@ static bool rewriteExecuteInline(Method* method, u2* insns, assert((insns[0] & 0xff) == OP_INVOKE_DIRECT || (insns[0] & 0xff) == OP_INVOKE_STATIC || (insns[0] & 0xff) == OP_INVOKE_VIRTUAL); - insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE; - insns[1] = (u2) inlineSubs->inlineIdx; + updateCode(method, insns, + (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE); + updateCode(method, insns+1, (u2) inlineSubs->inlineIdx); //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n", // method->clazz->descriptor, method->name, @@ -954,8 +1080,9 @@ static bool rewriteExecuteInlineRange(Method* method, u2* insns, assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE || (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE || (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE); - insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE; - insns[1] = (u2) inlineSubs->inlineIdx; + updateCode(method, insns, + (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE); + updateCode(method, insns+1, (u2) inlineSubs->inlineIdx); //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n", // method->clazz->descriptor, method->name, diff --git a/vm/analysis/Optimize.h b/vm/analysis/Optimize.h index 13876639c..30f7eefcf 100644 --- a/vm/analysis/Optimize.h +++ b/vm/analysis/Optimize.h @@ -29,7 +29,7 @@ void dvmFreeInlineSubsTable(InlineSub* inlineSubs); /* * Entry point from DEX preparation. */ -void dvmOptimizeClass(ClassObject* clazz); +void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly); /* * Abbreviated resolution functions, for use by optimization and verification diff --git a/vm/arch/x86-atom/Call386ABI.S b/vm/arch/x86-atom/Call386ABI.S index 1146c2d10..db1541e25 100644 --- a/vm/arch/x86-atom/Call386ABI.S +++ b/vm/arch/x86-atom/Call386ABI.S @@ -16,7 +16,7 @@ /* * File: CallABI.S * - * Code: facitliates call to native code C and C++ routines. + * Code: facilitates call to native code C and C++ routines. * */ diff --git a/vm/compiler/CompilerUtility.h b/vm/compiler/CompilerUtility.h index 4ab650d72..8bb5b4bbe 100644 --- a/vm/compiler/CompilerUtility.h +++ b/vm/compiler/CompilerUtility.h @@ -42,8 +42,6 @@ typedef struct GrowableList { } GrowableList; #define GET_ELEM_N(LIST, TYPE, N) (((TYPE*) LIST->elemList)[N]) -#define MIN(x,y) (((x) < (y)) ? (x) : (y)) -#define MAX(x,y) (((x) > (y)) ? (x) : (y)) struct LIR; diff --git a/vm/compiler/Frontend.c b/vm/compiler/Frontend.c index db03b1b47..4db75ad09 100644 --- a/vm/compiler/Frontend.c +++ b/vm/compiler/Frontend.c @@ -682,15 +682,18 @@ bool dvmCompileTrace(JitTraceDescription *desc, int numMaxInsts, lastBB->id = numBlocks++; if (cUnit.printMe) { - LOGD("TRACEINFO (%d): 0x%08x %s%s 0x%x %d of %d, %d blocks", + char* signature = dexProtoCopyMethodDescriptor(&desc->method->prototype); + LOGD("TRACEINFO (%d): 0x%08x %s%s.%s 0x%x %d of %d, %d blocks", compilationId, (intptr_t) desc->method->insns, desc->method->clazz->descriptor, desc->method->name, + signature, desc->trace[0].frag.startOffset, traceSize, dexCode->insnsSize, numBlocks); + free(signature); } BasicBlock **blockList; diff --git a/vm/compiler/codegen/CompilerCodegen.h b/vm/compiler/codegen/CompilerCodegen.h index 06fd41090..7379d502e 100644 --- a/vm/compiler/codegen/CompilerCodegen.h +++ b/vm/compiler/codegen/CompilerCodegen.h @@ -61,4 +61,7 @@ bool dvmCompilerArchVariantInit(void); /* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */ int dvmCompilerTargetOptHint(int key); +/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */ +void dvmCompilerGenMemBarrier(CompilationUnit *cUnit); + #endif /* _DALVIK_VM_COMPILERCODEGEN_H_ */ diff --git a/vm/compiler/codegen/arm/ArchUtility.c b/vm/compiler/codegen/arm/ArchUtility.c index 4e0df2884..2daa871ca 100644 --- a/vm/compiler/codegen/arm/ArchUtility.c +++ b/vm/compiler/codegen/arm/ArchUtility.c @@ -68,6 +68,7 @@ static void buildInsnString(char *fmt, ArmLIR *lir, char* buf, char *bufEnd = &buf[size-1]; char *fmtEnd = &fmt[strlen(fmt)]; char tbuf[256]; + char *name; char nc; while (fmt < fmtEnd) { int operand; @@ -82,6 +83,32 @@ static void buildInsnString(char *fmt, ArmLIR *lir, char* buf, assert((unsigned)(nc-'0') < 4); operand = lir->operands[nc-'0']; switch(*fmt++) { + case 'B': + switch (operand) { + case kSY: + name = "sy"; + break; + case kST: + name = "st"; + break; + case kISH: + name = "ish"; + break; + case kISHST: + name = "ishst"; + break; + case kNSH: + name = "nsh"; + break; + case kNSHST: + name = "shst"; + break; + default: + name = "DecodeError"; + break; + } + strcpy(tbuf, name); + break; case 'b': strcpy(tbuf,"0000"); for (i=3; i>= 0; i--) { diff --git a/vm/compiler/codegen/arm/ArmLIR.h b/vm/compiler/codegen/arm/ArmLIR.h index c397a391e..24f5240bd 100644 --- a/vm/compiler/codegen/arm/ArmLIR.h +++ b/vm/compiler/codegen/arm/ArmLIR.h @@ -622,10 +622,21 @@ typedef enum ArmOpCode { rd[11-8] imm2[7-6] [0] msb[4-0] */ kThumb2Bfc, /* bfc [11110011011011110] [0] imm3[14-12] rd[11-8] imm2[7-6] [0] msb[4-0] */ + kThumb2Dmb, /* dmb [1111001110111111100011110101] option[3-0] */ kArmLast, } ArmOpCode; +/* DMB option encodings */ +typedef enum ArmOpDmbOptions { + kSY = 0xf, + kST = 0xe, + kISH = 0xb, + kISHST = 0xa, + kNSH = 0x7, + kNSHST = 0x6 +} ArmOpDmbOptions; + /* Bit flags describing the behavior of each native opcode */ typedef enum ArmOpFeatureFlags { kIsBranch = 0, diff --git a/vm/compiler/codegen/arm/Assemble.c b/vm/compiler/codegen/arm/Assemble.c index 05c4a0ee2..832ee0fea 100644 --- a/vm/compiler/codegen/arm/Assemble.c +++ b/vm/compiler/codegen/arm/Assemble.c @@ -70,6 +70,7 @@ * n -> complimented Thumb2 modified immediate * M -> Thumb2 16-bit zero-extended immediate * b -> 4-digit binary + * B -> dmb option string (sy, st, ish, ishst, nsh, hshst) * * [!] escape. To insert "!", use "!!" */ @@ -869,6 +870,10 @@ ArmEncodingMap EncodingMap[kArmLast] = { kFmtBitBlt, 11, 8, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0, "bfc", "r!0d,#!1d,#!2d", 2), + ENCODING_MAP(kThumb2Dmb, 0xf3bf8f50, + kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP, + "dmb","#!0B",2), }; /* diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c index 03fc2af0f..0bcdd0e96 100644 --- a/vm/compiler/codegen/arm/CodegenDriver.c +++ b/vm/compiler/codegen/arm/CodegenDriver.c @@ -24,6 +24,25 @@ * applicable directory below this one. */ +/* + * Mark garbage collection card. Skip if the value we're storing is null. + */ +static void markCard(CompilationUnit *cUnit, int valReg, int tgtAddrReg) +{ + int regCardBase = dvmCompilerAllocTemp(cUnit); + int regCardNo = dvmCompilerAllocTemp(cUnit); + opRegImm(cUnit, kOpCmp, valReg, 0); /* storing null? */ + ArmLIR *branchOver = opCondBranch(cUnit, kArmCondEq); + loadWordDisp(cUnit, rGLUE, offsetof(InterpState, cardTable), + regCardBase); + opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT); + storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0, + kUnsignedByte); + ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); + target->defMask = ENCODE_ALL; + branchOver->generic.target = (LIR *)target; +} + static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct, int srcSize, int tgtSize) { @@ -58,7 +77,6 @@ static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct, return false; } - static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest, RegLocation rlSrc1, RegLocation rlSrc2) @@ -282,7 +300,7 @@ static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) * */ static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size, - int fieldOffset) + int fieldOffset, bool isVolatile) { RegLocation rlResult; RegisterClass regClass = dvmCompilerRegClassBySize(size); @@ -297,6 +315,9 @@ static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size, loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg, size, rlObj.sRegLow); HEAP_ACCESS_SHADOW(false); + if (isVolatile) { + dvmCompilerGenMemBarrier(cUnit); + } storeValue(cUnit, rlDest, rlResult); } @@ -306,7 +327,7 @@ static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size, * */ static void genIPut(CompilationUnit *cUnit, MIR *mir, OpSize size, - int fieldOffset) + int fieldOffset, bool isObject, bool isVolatile) { RegisterClass regClass = dvmCompilerRegClassBySize(size); RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); @@ -316,9 +337,16 @@ static void genIPut(CompilationUnit *cUnit, MIR *mir, OpSize size, genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, NULL);/* null object? */ + if (isVolatile) { + dvmCompilerGenMemBarrier(cUnit); + } HEAP_ACCESS_SHADOW(true); storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size); HEAP_ACCESS_SHADOW(false); + if (isObject) { + /* NOTE: marking card based on object head */ + markCard(cUnit, rlSrc.lowReg, rlObj.lowReg); + } } @@ -528,12 +556,14 @@ static void genArrayObjectPut(CompilationUnit *cUnit, MIR *mir, dvmCompilerLockTemp(cUnit, regPtr); // r4PC dvmCompilerLockTemp(cUnit, regIndex); // r7 dvmCompilerLockTemp(cUnit, r0); + dvmCompilerLockTemp(cUnit, r1); /* Bad? - roll back and re-execute if so */ genRegImmCheck(cUnit, kArmCondEq, r0, 0, mir->offset, pcrLabel); - /* Resume here - must reload element, regPtr & index preserved */ + /* Resume here - must reload element & array, regPtr & index preserved */ loadValueDirectFixed(cUnit, rlSrc, r0); + loadValueDirectFixed(cUnit, rlArray, r1); ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); target->defMask = ENCODE_ALL; @@ -543,6 +573,9 @@ static void genArrayObjectPut(CompilationUnit *cUnit, MIR *mir, storeBaseIndexed(cUnit, regPtr, regIndex, r0, scale, kWord); HEAP_ACCESS_SHADOW(false); + + /* NOTE: marking card here based on object head */ + markCard(cUnit, r0, r1); } static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir, @@ -1416,6 +1449,8 @@ static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) storeValue(cUnit, rlDest, rlResult); break; } + case OP_SGET_VOLATILE: + case OP_SGET_OBJECT_VOLATILE: case OP_SGET_OBJECT: case OP_SGET_BOOLEAN: case OP_SGET_CHAR: @@ -1424,6 +1459,7 @@ static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) case OP_SGET: { int valOffset = offsetof(StaticField, value); int tReg = dvmCompilerAllocTemp(cUnit); + bool isVolatile; void *fieldPtr = (void*) (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); @@ -1432,10 +1468,17 @@ static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) dvmAbort(); } + isVolatile = (mir->dalvikInsn.opCode == OP_SGET_VOLATILE) || + (mir->dalvikInsn.opCode == OP_SGET_OBJECT_VOLATILE) || + dvmIsVolatileField(fieldPtr); + rlDest = dvmCompilerGetDest(cUnit, mir, 0); rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); loadConstant(cUnit, tReg, (int) fieldPtr + valOffset); + if (isVolatile) { + dvmCompilerGenMemBarrier(cUnit); + } HEAP_ACCESS_SHADOW(true); loadWordDisp(cUnit, tReg, 0, rlResult.lowReg); HEAP_ACCESS_SHADOW(false); @@ -1473,9 +1516,14 @@ static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) case OP_SPUT: { int valOffset = offsetof(StaticField, value); int tReg = dvmCompilerAllocTemp(cUnit); - void *fieldPtr = (void*) + bool isVolatile; + Field *fieldPtr = (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + isVolatile = (mir->dalvikInsn.opCode == OP_SPUT_VOLATILE) || + (mir->dalvikInsn.opCode == OP_SPUT_OBJECT_VOLATILE) || + dvmIsVolatileField(fieldPtr); + if (fieldPtr == NULL) { LOGE("Unexpected null static field"); dvmAbort(); @@ -1488,6 +1536,13 @@ static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) HEAP_ACCESS_SHADOW(true); storeWordDisp(cUnit, tReg, 0 ,rlSrc.lowReg); HEAP_ACCESS_SHADOW(false); + if (isVolatile) { + dvmCompilerGenMemBarrier(cUnit); + } + if (mir->dalvikInsn.opCode == OP_SPUT_OBJECT) { + /* NOTE: marking card based on field address */ + markCard(cUnit, rlSrc.lowReg, tReg); + } break; } @@ -2088,17 +2143,18 @@ static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir) { OpCode dalvikOpCode = mir->dalvikInsn.opCode; int fieldOffset; + bool isVolatile = false; if (dalvikOpCode >= OP_IGET && dalvikOpCode <= OP_IPUT_SHORT) { - InstField *pInstField = (InstField *) + Field *fieldPtr = cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC]; - if (pInstField == NULL) { + if (fieldPtr == NULL) { LOGE("Unexpected null instance field"); dvmAbort(); } - - fieldOffset = pInstField->byteOffset; + isVolatile = dvmIsVolatileField(fieldPtr); + fieldOffset = ((InstField *)fieldPtr)->byteOffset; } else { /* Deliberately break the code while make the compiler happy */ fieldOffset = -1; @@ -2198,36 +2254,47 @@ static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir) case OP_IGET_WIDE: genIGetWide(cUnit, mir, fieldOffset); break; + case OP_IGET_VOLATILE: + case OP_IGET_OBJECT_VOLATILE: + isVolatile = true; + // NOTE: intentional fallthrough case OP_IGET: case OP_IGET_OBJECT: - genIGet(cUnit, mir, kWord, fieldOffset); + genIGet(cUnit, mir, kWord, fieldOffset, isVolatile); break; case OP_IGET_BOOLEAN: - genIGet(cUnit, mir, kUnsignedByte, fieldOffset); + genIGet(cUnit, mir, kUnsignedByte, fieldOffset, isVolatile); break; case OP_IGET_BYTE: - genIGet(cUnit, mir, kSignedByte, fieldOffset); + genIGet(cUnit, mir, kSignedByte, fieldOffset, isVolatile); break; case OP_IGET_CHAR: - genIGet(cUnit, mir, kUnsignedHalf, fieldOffset); + genIGet(cUnit, mir, kUnsignedHalf, fieldOffset, isVolatile); break; case OP_IGET_SHORT: - genIGet(cUnit, mir, kSignedHalf, fieldOffset); + genIGet(cUnit, mir, kSignedHalf, fieldOffset, isVolatile); break; case OP_IPUT_WIDE: genIPutWide(cUnit, mir, fieldOffset); break; case OP_IPUT: + genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile); + break; + case OP_IPUT_OBJECT_VOLATILE: + isVolatile = true; + // NOTE: intentional fallthrough case OP_IPUT_OBJECT: - genIPut(cUnit, mir, kWord, fieldOffset); + genIPut(cUnit, mir, kWord, fieldOffset, true, isVolatile); break; case OP_IPUT_SHORT: case OP_IPUT_CHAR: - genIPut(cUnit, mir, kUnsignedHalf, fieldOffset); + genIPut(cUnit, mir, kUnsignedHalf, fieldOffset, false, isVolatile); break; case OP_IPUT_BYTE: + genIPut(cUnit, mir, kSignedByte, fieldOffset, false, isVolatile); + break; case OP_IPUT_BOOLEAN: - genIPut(cUnit, mir, kUnsignedByte, fieldOffset); + genIPut(cUnit, mir, kUnsignedByte, fieldOffset, false, isVolatile); break; case OP_IGET_WIDE_VOLATILE: case OP_IPUT_WIDE_VOLATILE: @@ -2248,11 +2315,13 @@ static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir) switch (dalvikOpCode) { case OP_IGET_QUICK: case OP_IGET_OBJECT_QUICK: - genIGet(cUnit, mir, kWord, fieldOffset); + genIGet(cUnit, mir, kWord, fieldOffset, false); break; case OP_IPUT_QUICK: + genIPut(cUnit, mir, kWord, fieldOffset, false, false); + break; case OP_IPUT_OBJECT_QUICK: - genIPut(cUnit, mir, kWord, fieldOffset); + genIPut(cUnit, mir, kWord, fieldOffset, true, false); break; case OP_IGET_WIDE_QUICK: genIGetWide(cUnit, mir, fieldOffset); diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c index 7f9fa3ba9..6511eac22 100644 --- a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c +++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c @@ -92,3 +92,10 @@ int dvmCompilerTargetOptHint(int key) } return res; } + +void dvmCompilerGenMemBarrier(CompilationUnit *cUnit) +{ +#if ANDROID_SMP != 0 +#error armv5+smp not supported +#endif +} diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.c b/vm/compiler/codegen/arm/armv5te/ArchVariant.c index e018ea11b..814f410ff 100644 --- a/vm/compiler/codegen/arm/armv5te/ArchVariant.c +++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.c @@ -92,3 +92,10 @@ int dvmCompilerTargetOptHint(int key) } return res; } + +void dvmCompilerGenMemBarrier(CompilationUnit *cUnit) +{ +#if ANDROID_SMP != 0 +#error armv5+smp not supported +#endif +} diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c index 5a14774c0..f1727c6f1 100644 --- a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c +++ b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c @@ -87,3 +87,11 @@ int dvmCompilerTargetOptHint(int key) } return res; } + +void dvmCompilerGenMemBarrier(CompilationUnit *cUnit) +{ +#if ANDROID_SMP != 0 + ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, kSY); // Full system DMB + dmb->defMask = ENCODE_ALL; +#endif +} diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.c b/vm/compiler/codegen/arm/armv7-a/ArchVariant.c index 5a14774c0..f1727c6f1 100644 --- a/vm/compiler/codegen/arm/armv7-a/ArchVariant.c +++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.c @@ -87,3 +87,11 @@ int dvmCompilerTargetOptHint(int key) } return res; } + +void dvmCompilerGenMemBarrier(CompilationUnit *cUnit) +{ +#if ANDROID_SMP != 0 + ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, kSY); // Full system DMB + dmb->defMask = ENCODE_ALL; +#endif +} diff --git a/vm/interp/Interp.c b/vm/interp/Interp.c index da8fa79a0..daaf0c0da 100644 --- a/vm/interp/Interp.c +++ b/vm/interp/Interp.c @@ -127,7 +127,7 @@ static void dvmBreakpointSetLock(BreakpointSet* pSet) { if (dvmTryLockMutex(&pSet->lock) != 0) { Thread* self = dvmThreadSelf(); - int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); + ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); dvmLockMutex(&pSet->lock); dvmChangeStatus(self, oldStatus); } @@ -706,8 +706,8 @@ void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly) break; } const char* name = ""; - int j; #if 0 // "locals" structure has changed -- need to rewrite this + int j; DexFile* pDexFile = method->clazz->pDexFile; const DexCode* pDexCode = dvmGetMethodCode(method); int localsSize = dexGetLocalsSize(pDexFile, pDexCode); diff --git a/vm/interp/InterpDefs.h b/vm/interp/InterpDefs.h index c794db2b3..6ba9e12c1 100644 --- a/vm/interp/InterpDefs.h +++ b/vm/interp/InterpDefs.h @@ -128,6 +128,8 @@ typedef struct InterpState { const u1* interpStackEnd; /* points at self->suspendCount */ volatile int* pSelfSuspendCount; + /* Biased base of GC's card table */ + u1* cardTable; #if defined(WITH_DEBUGGER) /* points at gDvm.debuggerActive, or NULL if debugger not enabled */ volatile u1* pDebuggerActive; @@ -151,8 +153,8 @@ typedef struct InterpState { */ unsigned char* pJitProfTable; JitState jitState; - const void* jitResumeNPC; // Native PC of compiled code - const u2* jitResumeDPC; // Dalvik PC corresponding to NPC + const void* jitResumeNPC; // Native PC of compiled code + const u2* jitResumeDPC; // Dalvik PC corresponding to NPC int jitThreshold; /* * ppJitProfTable holds the address of gDvmJit.pJitProfTable, which diff --git a/vm/jdwp/JdwpHandler.c b/vm/jdwp/JdwpHandler.c index 69fe51077..d2a657dea 100644 --- a/vm/jdwp/JdwpHandler.c +++ b/vm/jdwp/JdwpHandler.c @@ -896,6 +896,30 @@ static JdwpError handleCT_NewInstance(JdwpState* state, } /* + * Create a new array object of the requested type and length. + */ +static JdwpError handleAT_newInstance(JdwpState* state, + const u1* buf, int dataLen, ExpandBuf* pReply) +{ + RefTypeId arrayTypeId; + u4 length; + ObjectId objectId; + + arrayTypeId = dvmReadRefTypeId(&buf); + length = read4BE(&buf); + + LOGV("Creating array %s[%u]\n", + dvmDbgGetClassDescriptor(arrayTypeId), length); + objectId = dvmDbgCreateArrayObject(arrayTypeId, length); + if (objectId == 0) + return ERR_OUT_OF_MEMORY; + + expandBufAdd1(pReply, JT_ARRAY); + expandBufAddObjectId(pReply, objectId); + return ERR_NONE; +} + +/* * Return line number information for the method, if present. */ static JdwpError handleM_LineTable(JdwpState* state, @@ -2012,7 +2036,7 @@ static const JdwpHandlerMap gHandlerMap[] = { { 3, 4, handleCT_NewInstance, "ClassType.NewInstance" }, /* ArrayType command set (4) */ - //4, 1, NewInstance + { 4, 1, handleAT_newInstance, "ArrayType.NewInstance" }, /* InterfaceType command set (5) */ diff --git a/vm/mterp/Mterp.c b/vm/mterp/Mterp.c index 51cef047f..4b0ceb80f 100644 --- a/vm/mterp/Mterp.c +++ b/vm/mterp/Mterp.c @@ -76,6 +76,7 @@ bool dvmMterpStd(Thread* self, InterpState* glue) glue->interpStackEnd = self->interpStackEnd; glue->pSelfSuspendCount = &self->suspendCount; + glue->cardTable = gDvm.biasedCardTableBase; #if defined(WITH_JIT) glue->pJitProfTable = gDvmJit.pProfTable; glue->ppJitProfTable = &gDvmJit.pProfTable; diff --git a/vm/mterp/armv5te/OP_APUT_OBJECT.S b/vm/mterp/armv5te/OP_APUT_OBJECT.S index 03477f520..e926db66d 100644 --- a/vm/mterp/armv5te/OP_APUT_OBJECT.S +++ b/vm/mterp/armv5te/OP_APUT_OBJECT.S @@ -36,6 +36,13 @@ bl dvmCanPutArrayElement @ test object type vs. array type cmp r0, #0 @ okay? beq common_errArrayStore @ no + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base + add r10, #offArrayObject_contents @ r0<- pointer to slot + GET_INST_OPCODE(ip) @ extract opcode from rINST + str r9, [r10] @ vBB[vCC]<- vAA + strb r2, [r2, r10, lsr #GC_CARD_SHIFT] @ mark card + GOTO_OPCODE(ip) @ jump to next instruction .L${opcode}_skip_check: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST GET_INST_OPCODE(ip) @ extract opcode from rINST diff --git a/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S index 1241c1334..b9abe5ace 100644 --- a/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S +++ b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S @@ -33,15 +33,15 @@ .L${opcode}_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if $isrange mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .L${opcode}_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -49,7 +49,8 @@ beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -81,8 +82,13 @@ .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this diff --git a/vm/mterp/armv5te/OP_IPUT.S b/vm/mterp/armv5te/OP_IPUT.S index 78d333d7a..53f4b4e2f 100644 --- a/vm/mterp/armv5te/OP_IPUT.S +++ b/vm/mterp/armv5te/OP_IPUT.S @@ -7,7 +7,7 @@ /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT.S b/vm/mterp/armv5te/OP_IPUT_OBJECT.S index 7a4d936cb..5e0fe834f 100644 --- a/vm/mterp/armv5te/OP_IPUT_OBJECT.S +++ b/vm/mterp/armv5te/OP_IPUT_OBJECT.S @@ -1,2 +1,51 @@ +%default { "barrier":"@ no-op ", "sqnum":"0" } %verify "executed" -%include "armv5te/OP_IPUT.S" +%verify "null object" +%verify "field already resolved" +%verify "field not yet resolved" +%verify "field cannot be resolved" + /* + * 32-bit instance field put. + * + * for: iput-object, iput-object-volatile + */ + /* op vA, vB, field@CCCC */ + mov r0, rINST, lsr #12 @ r0<- B + ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex + FETCH(r1, 1) @ r1<- field ref CCCC + ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields + GET_VREG(r9, r0) @ r9<- fp[B], the object pointer + ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr + cmp r0, #0 @ is resolved entry null? + bne .L${opcode}_finish @ no, already resolved +8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveInstField @ r0<- resolved InstField ptr + cmp r0, #0 @ success? + bne .L${opcode}_finish @ yes, finish up + b common_exceptionThrown +%break + + /* + * Currently: + * r0 holds resolved field + * r9 holds object + */ +.L${opcode}_finish: + @bl common_squeak${sqnum} + mov r1, rINST, lsr #8 @ r1<- A+ + ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field + and r1, r1, #15 @ r1<- A + cmp r9, #0 @ check object for null + GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + beq common_errNullObject @ object was null + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location + GET_INST_OPCODE(ip) @ extract opcode from rINST + $barrier @ releasing store + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S index 6838faf5c..648a69cc8 100644 --- a/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S +++ b/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S @@ -1,2 +1,19 @@ %verify "executed" -%include "armv5te/OP_IPUT_QUICK.S" +%verify "null object" + /* For: iput-object-quick */ + /* op vA, vB, offset@CCCC */ + mov r2, rINST, lsr #12 @ r2<- B + GET_VREG(r3, r2) @ r3<- fp[B], the object pointer + FETCH(r1, 1) @ r1<- field byte offset + cmp r3, #0 @ check object for null + mov r2, rINST, lsr #8 @ r2<- A(+) + beq common_errNullObject @ object was null + and r2, r2, #15 + GET_VREG(r0, r2) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + str r0, [r3, r1] @ obj.field (always 32 bits)<- r0 + cmp r0, #0 + strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card on non-null store + GET_INST_OPCODE(ip) @ extract opcode from rINST + GOTO_OPCODE(ip) @ jump to next instruction diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S index ba3f6152b..b4d24e7e7 100644 --- a/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S +++ b/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S @@ -1,2 +1,2 @@ %verify "executed" -%include "armv5te/OP_IPUT.S" {"barrier":"SMP_DMB"} +%include "armv5te/OP_IPUT_OBJECT.S" {"barrier":"SMP_DMB"} diff --git a/vm/mterp/armv5te/OP_IPUT_QUICK.S b/vm/mterp/armv5te/OP_IPUT_QUICK.S index c15da2bc8..ad76eca33 100644 --- a/vm/mterp/armv5te/OP_IPUT_QUICK.S +++ b/vm/mterp/armv5te/OP_IPUT_QUICK.S @@ -1,6 +1,6 @@ %verify "executed" %verify "null object" - /* For: iput-quick, iput-object-quick */ + /* For: iput-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B GET_VREG(r3, r2) @ r3<- fp[B], the object pointer diff --git a/vm/mterp/armv5te/OP_SPUT.S b/vm/mterp/armv5te/OP_SPUT.S index c9919ba18..e709b2256 100644 --- a/vm/mterp/armv5te/OP_SPUT.S +++ b/vm/mterp/armv5te/OP_SPUT.S @@ -6,7 +6,7 @@ /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex diff --git a/vm/mterp/armv5te/OP_SPUT_OBJECT.S b/vm/mterp/armv5te/OP_SPUT_OBJECT.S index 1048982c6..54a04fcfb 100644 --- a/vm/mterp/armv5te/OP_SPUT_OBJECT.S +++ b/vm/mterp/armv5te/OP_SPUT_OBJECT.S @@ -1,2 +1,38 @@ +%default { "barrier":"@ no-op " } %verify "executed" -%include "armv5te/OP_SPUT.S" +%verify "field already resolved" +%verify "field not yet resolved" +%verify "field cannot be resolved" + /* + * 32-bit SPUT handler for objects + * + * for: sput-object, sput-object-volatile + */ + /* op vAA, field@BBBB */ + ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex + FETCH(r1, 1) @ r1<- field ref BBBB + ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields + ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr + cmp r0, #0 @ is resolved entry null? + bne .L${opcode}_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .L${opcode}_finish @ yes, finish + b common_exceptionThrown @ no, handle exception + +%break +.L${opcode}_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + $barrier @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction diff --git a/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S index cfb2b27b3..fe12b9eed 100644 --- a/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S +++ b/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S @@ -1,2 +1,2 @@ %verify "executed" -%include "armv5te/OP_SPUT.S" {"barrier":"SMP_DMB"} +%include "armv5te/OP_SPUT_OBJECT.S" {"barrier":"SMP_DMB"} diff --git a/vm/mterp/c/gotoTargets.c b/vm/mterp/c/gotoTargets.c index c6c118bab..589f974fa 100644 --- a/vm/mterp/c/gotoTargets.c +++ b/vm/mterp/c/gotoTargets.c @@ -507,7 +507,6 @@ GOTO_TARGET(invokeSuperQuick, bool methodCallRange) GOTO_TARGET_END - /* * General handling for return-void, return, and return-wide. Put the * return value in "retval" before jumping here. @@ -735,6 +734,7 @@ GOTO_TARGET(exceptionThrown) GOTO_TARGET_END + /* * General handling for invoke-{virtual,super,direct,static,interface}, * including "quick" variants. @@ -917,8 +917,18 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, TRACE_METHOD_ENTER(self, methodToCall); #endif - ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, - methodToCall->name, methodToCall->shorty); +#if defined(WITH_JNI_TRACE) + bool trace = gDvm.jniTrace && + strstr(methodToCall->clazz->descriptor, gDvm.jniTrace); + if (trace) { + dvmLogNativeMethodEntry(methodToCall, newFp); + } + else +#endif + { + ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, + methodToCall->name, methodToCall->shorty); + } #if defined(WITH_JIT) /* Allow the Jit to end any pending trace building */ @@ -946,6 +956,12 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, dvmPopJniLocals(self, newSaveArea); self->curFrame = fp; +#if defined(WITH_JNI_TRACE) + if (trace) { + dvmLogNativeMethodExit(methodToCall, self, retval); + } +#endif + /* * If the native code threw an exception, or interpreted code * invoked by the native call threw one and nobody has cleared diff --git a/vm/mterp/common/asm-constants.h b/vm/mterp/common/asm-constants.h index dde675e88..bcff8f096 100644 --- a/vm/mterp/common/asm-constants.h +++ b/vm/mterp/common/asm-constants.h @@ -97,9 +97,22 @@ MTERP_OFFSET(offGlue_self, MterpGlue, self, 24) MTERP_OFFSET(offGlue_bailPtr, MterpGlue, bailPtr, 28) MTERP_OFFSET(offGlue_interpStackEnd, MterpGlue, interpStackEnd, 32) MTERP_OFFSET(offGlue_pSelfSuspendCount, MterpGlue, pSelfSuspendCount, 36) +MTERP_OFFSET(offGlue_cardTable, MterpGlue, cardTable, 40) #if defined(WITH_DEBUGGER) && defined(WITH_PROFILER) -MTERP_OFFSET(offGlue_pDebuggerActive, MterpGlue, pDebuggerActive, 40) -MTERP_OFFSET(offGlue_pActiveProfilers, MterpGlue, pActiveProfilers, 44) +MTERP_OFFSET(offGlue_pDebuggerActive, MterpGlue, pDebuggerActive, 44) +MTERP_OFFSET(offGlue_pActiveProfilers, MterpGlue, pActiveProfilers, 48) +MTERP_OFFSET(offGlue_entryPoint, MterpGlue, entryPoint, 52) +#if defined(WITH_JIT) +MTERP_OFFSET(offGlue_pJitProfTable, MterpGlue, pJitProfTable, 60) +MTERP_OFFSET(offGlue_jitState, MterpGlue, jitState, 64) +MTERP_OFFSET(offGlue_jitResumeNPC, MterpGlue, jitResumeNPC, 68) +MTERP_OFFSET(offGlue_jitResumeDPC, MterpGlue, jitResumeDPC, 72) +MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 76) +MTERP_OFFSET(offGlue_ppJitProfTable, MterpGlue, ppJitProfTable, 80) +MTERP_OFFSET(offGlue_icRechainCount, MterpGlue, icRechainCount, 84) +#endif +#elif defined(WITH_DEBUGGER) +MTERP_OFFSET(offGlue_pDebuggerActive, MterpGlue, pDebuggerActive, 44) MTERP_OFFSET(offGlue_entryPoint, MterpGlue, entryPoint, 48) #if defined(WITH_JIT) MTERP_OFFSET(offGlue_pJitProfTable, MterpGlue, pJitProfTable, 56) @@ -110,20 +123,19 @@ MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 72) MTERP_OFFSET(offGlue_ppJitProfTable, MterpGlue, ppJitProfTable, 76) MTERP_OFFSET(offGlue_icRechainCount, MterpGlue, icRechainCount, 80) #endif -#elif defined(WITH_DEBUGGER) -MTERP_OFFSET(offGlue_pDebuggerActive, MterpGlue, pDebuggerActive, 40) -MTERP_OFFSET(offGlue_entryPoint, MterpGlue, entryPoint, 44) +#elif defined(WITH_PROFILER) +MTERP_OFFSET(offGlue_pActiveProfilers, MterpGlue, pActiveProfilers, 44) +MTERP_OFFSET(offGlue_entryPoint, MterpGlue, entryPoint, 48) #if defined(WITH_JIT) -MTERP_OFFSET(offGlue_pJitProfTable, MterpGlue, pJitProfTable, 52) -MTERP_OFFSET(offGlue_jitState, MterpGlue, jitState, 56) -MTERP_OFFSET(offGlue_jitResumeNPC, MterpGlue, jitResumeNPC, 60) -MTERP_OFFSET(offGlue_jitResumeDPC, MterpGlue, jitResumeDPC, 64) -MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 68) -MTERP_OFFSET(offGlue_ppJitProfTable, MterpGlue, ppJitProfTable, 72) -MTERP_OFFSET(offGlue_icRechainCount, MterpGlue, icRechainCount, 76) +MTERP_OFFSET(offGlue_pJitProfTable, MterpGlue, pJitProfTable, 56) +MTERP_OFFSET(offGlue_jitState, MterpGlue, jitState, 60) +MTERP_OFFSET(offGlue_jitResumeNPC, MterpGlue, jitResumeNPC, 64) +MTERP_OFFSET(offGlue_jitResumeDPC, MterpGlue, jitResumeDPC, 68) +MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 72) +MTERP_OFFSET(offGlue_ppJitProfTable, MterpGlue, ppJitProfTable, 76) +MTERP_OFFSET(offGlue_icRechainCount, MterpGlue, icRechainCount, 80) #endif -#elif defined(WITH_PROFILER) -MTERP_OFFSET(offGlue_pActiveProfilers, MterpGlue, pActiveProfilers, 40) +#else MTERP_OFFSET(offGlue_entryPoint, MterpGlue, entryPoint, 44) #if defined(WITH_JIT) MTERP_OFFSET(offGlue_pJitProfTable, MterpGlue, pJitProfTable, 52) @@ -134,17 +146,6 @@ MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 68) MTERP_OFFSET(offGlue_ppJitProfTable, MterpGlue, ppJitProfTable, 72) MTERP_OFFSET(offGlue_icRechainCount, MterpGlue, icRechainCount, 76) #endif -#else -MTERP_OFFSET(offGlue_entryPoint, MterpGlue, entryPoint, 40) -#if defined(WITH_JIT) -MTERP_OFFSET(offGlue_pJitProfTable, MterpGlue, pJitProfTable, 48) -MTERP_OFFSET(offGlue_jitState, MterpGlue, jitState, 52) -MTERP_OFFSET(offGlue_jitResumeNPC, MterpGlue, jitResumeNPC, 56) -MTERP_OFFSET(offGlue_jitResumeDPC, MterpGlue, jitResumeDPC, 60) -MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 64) -MTERP_OFFSET(offGlue_ppJitProfTable, MterpGlue, ppJitProfTable, 68) -MTERP_OFFSET(offGlue_icRechainCount, MterpGlue, icRechainCount, 72) -#endif #endif /* make sure all JValue union members are stored at the same offset */ MTERP_OFFSET(offGlue_retval_z, MterpGlue, retval.z, 8) @@ -342,5 +343,8 @@ MTERP_CONSTANT(ACC_ABSTRACT, 0x0400) /* flags for dvmMalloc */ MTERP_CONSTANT(ALLOC_DONT_TRACK, 0x01) +/* for GC */ +MTERP_CONSTANT(GC_CARD_SHIFT, 7) + /* opcode number */ MTERP_CONSTANT(OP_MOVE_EXCEPTION, 0x0d) diff --git a/vm/mterp/config-x86 b/vm/mterp/config-x86 index 78ae1a0e3..627e06cd6 100644 --- a/vm/mterp/config-x86 +++ b/vm/mterp/config-x86 @@ -35,14 +35,6 @@ import c/opcommon.c op-start x86 # stub -- need native impl op OP_EXECUTE_INLINE_RANGE c - op OP_IGET_VOLATILE c - op OP_IPUT_VOLATILE c - op OP_SGET_VOLATILE c - op OP_SPUT_VOLATILE c - op OP_IGET_OBJECT_VOLATILE c - op OP_IPUT_OBJECT_VOLATILE c - op OP_SGET_OBJECT_VOLATILE c - op OP_SPUT_OBJECT_VOLATILE c op OP_IGET_WIDE_VOLATILE c op OP_IPUT_WIDE_VOLATILE c op OP_SGET_WIDE_VOLATILE c diff --git a/vm/mterp/out/InterpAsm-armv4t.S b/vm/mterp/out/InterpAsm-armv4t.S index 2e7dcc608..d82272f7f 100644 --- a/vm/mterp/out/InterpAsm-armv4t.S +++ b/vm/mterp/out/InterpAsm-armv4t.S @@ -2612,7 +2612,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2656,11 +2656,10 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT: /* 0x5b */ /* File: armv5te/OP_IPUT_OBJECT.S */ -/* File: armv5te/OP_IPUT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2679,7 +2678,6 @@ dalvik_inst: bne .LOP_IPUT_OBJECT_finish @ yes, finish up b common_exceptionThrown - /* ------------------------------ */ .balign 64 .L_OP_IPUT_BOOLEAN: /* 0x5c */ @@ -2689,7 +2687,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2718,7 +2716,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2747,7 +2745,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2776,7 +2774,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2991,7 +2989,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3041,11 +3039,10 @@ dalvik_inst: .balign 64 .L_OP_SPUT_OBJECT: /* 0x69 */ /* File: armv5te/OP_SPUT_OBJECT.S */ -/* File: armv5te/OP_SPUT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3053,15 +3050,14 @@ dalvik_inst: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - @ no-op @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_finish @ yes, finish + b common_exceptionThrown @ no, handle exception /* ------------------------------ */ @@ -3072,7 +3068,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3099,7 +3095,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3126,7 +3122,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3153,7 +3149,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7455,7 +7451,7 @@ d2i_doconv: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -7510,7 +7506,7 @@ d2i_doconv: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7833,7 +7829,7 @@ d2i_doconv: .balign 64 .L_OP_IPUT_QUICK: /* 0xf5 */ /* File: armv5te/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B GET_VREG(r3, r2) @ r3<- fp[B], the object pointer @@ -7872,8 +7868,7 @@ d2i_doconv: .balign 64 .L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */ /* File: armv5te/OP_IPUT_OBJECT_QUICK.S */ -/* File: armv5te/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-object-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B GET_VREG(r3, r2) @ r3<- fp[B], the object pointer @@ -7883,12 +7878,14 @@ d2i_doconv: beq common_errNullObject @ object was null and r2, r2, #15 GET_VREG(r0, r2) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base FETCH_ADVANCE_INST(2) @ advance rPC, load rINST str r0, [r3, r1] @ obj.field (always 32 bits)<- r0 + cmp r0, #0 + strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card on non-null store GET_INST_OPCODE(ip) @ extract opcode from rINST GOTO_OPCODE(ip) @ jump to next instruction - /* ------------------------------ */ .balign 64 .L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */ @@ -8001,11 +7998,11 @@ d2i_doconv: .balign 64 .L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */ /* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_IPUT.S */ +/* File: armv5te/OP_IPUT_OBJECT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -8056,11 +8053,11 @@ d2i_doconv: .balign 64 .L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */ /* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_SPUT.S */ +/* File: armv5te/OP_SPUT_OBJECT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -8068,15 +8065,15 @@ d2i_doconv: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_VOLATILE_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - SMP_DMB @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish + b common_exceptionThrown @ no, handle exception + /* ------------------------------ */ @@ -8352,15 +8349,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 0 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -8368,7 +8365,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8400,8 +8398,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8430,15 +8433,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_RANGE_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 1 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -8446,7 +8449,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8478,8 +8482,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8711,6 +8720,13 @@ OP_CMPG_FLOAT_nan: bl dvmCanPutArrayElement @ test object type vs. array type cmp r0, #0 @ okay? beq common_errArrayStore @ no + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base + add r10, #offArrayObject_contents @ r0<- pointer to slot + GET_INST_OPCODE(ip) @ extract opcode from rINST + str r9, [r10] @ vBB[vCC]<- vAA + strb r2, [r2, r10, lsr #GC_CARD_SHIFT] @ mark card + GOTO_OPCODE(ip) @ jump to next instruction .LOP_APUT_OBJECT_skip_check: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST GET_INST_OPCODE(ip) @ extract opcode from rINST @@ -8929,11 +8945,15 @@ OP_CMPG_FLOAT_nan: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST @ no-op @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_IPUT_BOOLEAN */ @@ -9162,19 +9182,18 @@ OP_CMPG_FLOAT_nan: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + @ no-op @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SPUT_BOOLEAN */ @@ -9769,11 +9788,15 @@ d2l_doconv: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST SMP_DMB @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SGET_OBJECT_VOLATILE */ @@ -9792,19 +9815,18 @@ d2l_doconv: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT_VOLATILE */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_VOLATILE_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + SMP_DMB @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction .size dvmAsmSisterStart, .-dvmAsmSisterStart .global dvmAsmSisterEnd diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S index 2e86a7f27..633a32d19 100644 --- a/vm/mterp/out/InterpAsm-armv5te-vfp.S +++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S @@ -2592,7 +2592,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2636,11 +2636,10 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT: /* 0x5b */ /* File: armv5te/OP_IPUT_OBJECT.S */ -/* File: armv5te/OP_IPUT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2659,7 +2658,6 @@ dalvik_inst: bne .LOP_IPUT_OBJECT_finish @ yes, finish up b common_exceptionThrown - /* ------------------------------ */ .balign 64 .L_OP_IPUT_BOOLEAN: /* 0x5c */ @@ -2669,7 +2667,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2698,7 +2696,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2727,7 +2725,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2756,7 +2754,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2971,7 +2969,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3021,11 +3019,10 @@ dalvik_inst: .balign 64 .L_OP_SPUT_OBJECT: /* 0x69 */ /* File: armv5te/OP_SPUT_OBJECT.S */ -/* File: armv5te/OP_SPUT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3033,15 +3030,14 @@ dalvik_inst: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - @ no-op @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_finish @ yes, finish + b common_exceptionThrown @ no, handle exception /* ------------------------------ */ @@ -3052,7 +3048,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3079,7 +3075,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3106,7 +3102,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3133,7 +3129,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7135,7 +7131,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -7190,7 +7186,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7512,7 +7508,7 @@ dalvik_inst: .balign 64 .L_OP_IPUT_QUICK: /* 0xf5 */ /* File: armv5te/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B GET_VREG(r3, r2) @ r3<- fp[B], the object pointer @@ -7550,8 +7546,7 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */ /* File: armv5te/OP_IPUT_OBJECT_QUICK.S */ -/* File: armv5te/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-object-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B GET_VREG(r3, r2) @ r3<- fp[B], the object pointer @@ -7561,12 +7556,14 @@ dalvik_inst: beq common_errNullObject @ object was null and r2, r2, #15 GET_VREG(r0, r2) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base FETCH_ADVANCE_INST(2) @ advance rPC, load rINST str r0, [r3, r1] @ obj.field (always 32 bits)<- r0 + cmp r0, #0 + strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card on non-null store GET_INST_OPCODE(ip) @ extract opcode from rINST GOTO_OPCODE(ip) @ jump to next instruction - /* ------------------------------ */ .balign 64 .L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */ @@ -7679,11 +7676,11 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */ /* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_IPUT.S */ +/* File: armv5te/OP_IPUT_OBJECT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -7734,11 +7731,11 @@ dalvik_inst: .balign 64 .L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */ /* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_SPUT.S */ +/* File: armv5te/OP_SPUT_OBJECT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7746,15 +7743,15 @@ dalvik_inst: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_VOLATILE_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - SMP_DMB @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish + b common_exceptionThrown @ no, handle exception + /* ------------------------------ */ @@ -8030,15 +8027,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 0 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -8046,7 +8043,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8078,8 +8076,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8108,15 +8111,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_RANGE_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 1 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -8124,7 +8127,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8156,8 +8160,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8251,6 +8260,13 @@ dvmAsmSisterStart: bl dvmCanPutArrayElement @ test object type vs. array type cmp r0, #0 @ okay? beq common_errArrayStore @ no + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base + add r10, #offArrayObject_contents @ r0<- pointer to slot + GET_INST_OPCODE(ip) @ extract opcode from rINST + str r9, [r10] @ vBB[vCC]<- vAA + strb r2, [r2, r10, lsr #GC_CARD_SHIFT] @ mark card + GOTO_OPCODE(ip) @ jump to next instruction .LOP_APUT_OBJECT_skip_check: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST GET_INST_OPCODE(ip) @ extract opcode from rINST @@ -8468,11 +8484,15 @@ dvmAsmSisterStart: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST @ no-op @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_IPUT_BOOLEAN */ @@ -8701,19 +8721,18 @@ dvmAsmSisterStart: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + @ no-op @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SPUT_BOOLEAN */ @@ -9307,11 +9326,15 @@ d2l_doconv: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST SMP_DMB @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SGET_OBJECT_VOLATILE */ @@ -9330,19 +9353,18 @@ d2l_doconv: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT_VOLATILE */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_VOLATILE_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + SMP_DMB @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction .size dvmAsmSisterStart, .-dvmAsmSisterStart .global dvmAsmSisterEnd diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S index feb908ca9..66f3a3d7c 100644 --- a/vm/mterp/out/InterpAsm-armv5te.S +++ b/vm/mterp/out/InterpAsm-armv5te.S @@ -2614,7 +2614,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2658,11 +2658,10 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT: /* 0x5b */ /* File: armv5te/OP_IPUT_OBJECT.S */ -/* File: armv5te/OP_IPUT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2681,7 +2680,6 @@ dalvik_inst: bne .LOP_IPUT_OBJECT_finish @ yes, finish up b common_exceptionThrown - /* ------------------------------ */ .balign 64 .L_OP_IPUT_BOOLEAN: /* 0x5c */ @@ -2691,7 +2689,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2720,7 +2718,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2749,7 +2747,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2778,7 +2776,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2993,7 +2991,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3043,11 +3041,10 @@ dalvik_inst: .balign 64 .L_OP_SPUT_OBJECT: /* 0x69 */ /* File: armv5te/OP_SPUT_OBJECT.S */ -/* File: armv5te/OP_SPUT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3055,15 +3052,14 @@ dalvik_inst: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - @ no-op @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_finish @ yes, finish + b common_exceptionThrown @ no, handle exception /* ------------------------------ */ @@ -3074,7 +3070,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3101,7 +3097,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3128,7 +3124,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3155,7 +3151,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7457,7 +7453,7 @@ d2i_doconv: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -7512,7 +7508,7 @@ d2i_doconv: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7834,7 +7830,7 @@ d2i_doconv: .balign 64 .L_OP_IPUT_QUICK: /* 0xf5 */ /* File: armv5te/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B GET_VREG(r3, r2) @ r3<- fp[B], the object pointer @@ -7872,8 +7868,7 @@ d2i_doconv: .balign 64 .L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */ /* File: armv5te/OP_IPUT_OBJECT_QUICK.S */ -/* File: armv5te/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-object-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B GET_VREG(r3, r2) @ r3<- fp[B], the object pointer @@ -7883,12 +7878,14 @@ d2i_doconv: beq common_errNullObject @ object was null and r2, r2, #15 GET_VREG(r0, r2) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base FETCH_ADVANCE_INST(2) @ advance rPC, load rINST str r0, [r3, r1] @ obj.field (always 32 bits)<- r0 + cmp r0, #0 + strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card on non-null store GET_INST_OPCODE(ip) @ extract opcode from rINST GOTO_OPCODE(ip) @ jump to next instruction - /* ------------------------------ */ .balign 64 .L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */ @@ -8001,11 +7998,11 @@ d2i_doconv: .balign 64 .L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */ /* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_IPUT.S */ +/* File: armv5te/OP_IPUT_OBJECT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -8056,11 +8053,11 @@ d2i_doconv: .balign 64 .L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */ /* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_SPUT.S */ +/* File: armv5te/OP_SPUT_OBJECT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -8068,15 +8065,15 @@ d2i_doconv: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_VOLATILE_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - SMP_DMB @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish + b common_exceptionThrown @ no, handle exception + /* ------------------------------ */ @@ -8352,15 +8349,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 0 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -8368,7 +8365,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8400,8 +8398,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8430,15 +8433,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_RANGE_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 1 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -8446,7 +8449,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8478,8 +8482,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8709,6 +8718,13 @@ OP_CMPG_FLOAT_nan: bl dvmCanPutArrayElement @ test object type vs. array type cmp r0, #0 @ okay? beq common_errArrayStore @ no + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base + add r10, #offArrayObject_contents @ r0<- pointer to slot + GET_INST_OPCODE(ip) @ extract opcode from rINST + str r9, [r10] @ vBB[vCC]<- vAA + strb r2, [r2, r10, lsr #GC_CARD_SHIFT] @ mark card + GOTO_OPCODE(ip) @ jump to next instruction .LOP_APUT_OBJECT_skip_check: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST GET_INST_OPCODE(ip) @ extract opcode from rINST @@ -8926,11 +8942,15 @@ OP_CMPG_FLOAT_nan: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST @ no-op @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_IPUT_BOOLEAN */ @@ -9159,19 +9179,18 @@ OP_CMPG_FLOAT_nan: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + @ no-op @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SPUT_BOOLEAN */ @@ -9765,11 +9784,15 @@ d2l_doconv: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST SMP_DMB @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SGET_OBJECT_VOLATILE */ @@ -9788,19 +9811,18 @@ d2l_doconv: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT_VOLATILE */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_VOLATILE_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + SMP_DMB @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction .size dvmAsmSisterStart, .-dvmAsmSisterStart .global dvmAsmSisterEnd diff --git a/vm/mterp/out/InterpAsm-armv7-a-neon.S b/vm/mterp/out/InterpAsm-armv7-a-neon.S index f573a4045..e837ddaaa 100644 --- a/vm/mterp/out/InterpAsm-armv7-a-neon.S +++ b/vm/mterp/out/InterpAsm-armv7-a-neon.S @@ -2636,11 +2636,10 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT: /* 0x5b */ /* File: armv5te/OP_IPUT_OBJECT.S */ -/* File: armv5te/OP_IPUT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2659,7 +2658,6 @@ dalvik_inst: bne .LOP_IPUT_OBJECT_finish @ yes, finish up b common_exceptionThrown - /* ------------------------------ */ .balign 64 .L_OP_IPUT_BOOLEAN: /* 0x5c */ @@ -2669,7 +2667,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2698,7 +2696,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2727,7 +2725,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2756,7 +2754,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2971,7 +2969,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3021,11 +3019,10 @@ dalvik_inst: .balign 64 .L_OP_SPUT_OBJECT: /* 0x69 */ /* File: armv5te/OP_SPUT_OBJECT.S */ -/* File: armv5te/OP_SPUT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3033,15 +3030,14 @@ dalvik_inst: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - @ no-op @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_finish @ yes, finish + b common_exceptionThrown @ no, handle exception /* ------------------------------ */ @@ -3052,7 +3048,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3079,7 +3075,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3106,7 +3102,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3133,7 +3129,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7089,7 +7085,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -7144,7 +7140,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7500,8 +7496,7 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */ /* File: armv5te/OP_IPUT_OBJECT_QUICK.S */ -/* File: armv5te/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-object-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B GET_VREG(r3, r2) @ r3<- fp[B], the object pointer @@ -7511,12 +7506,14 @@ dalvik_inst: beq common_errNullObject @ object was null and r2, r2, #15 GET_VREG(r0, r2) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base FETCH_ADVANCE_INST(2) @ advance rPC, load rINST str r0, [r3, r1] @ obj.field (always 32 bits)<- r0 + cmp r0, #0 + strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card on non-null store GET_INST_OPCODE(ip) @ extract opcode from rINST GOTO_OPCODE(ip) @ jump to next instruction - /* ------------------------------ */ .balign 64 .L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */ @@ -7629,11 +7626,11 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */ /* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_IPUT.S */ +/* File: armv5te/OP_IPUT_OBJECT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -7684,11 +7681,11 @@ dalvik_inst: .balign 64 .L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */ /* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_SPUT.S */ +/* File: armv5te/OP_SPUT_OBJECT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7696,15 +7693,15 @@ dalvik_inst: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_VOLATILE_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - SMP_DMB @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish + b common_exceptionThrown @ no, handle exception + /* ------------------------------ */ @@ -7980,15 +7977,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 0 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -7996,7 +7993,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8028,8 +8026,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8058,15 +8061,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_RANGE_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 1 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -8074,7 +8077,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8106,8 +8110,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8201,6 +8210,13 @@ dvmAsmSisterStart: bl dvmCanPutArrayElement @ test object type vs. array type cmp r0, #0 @ okay? beq common_errArrayStore @ no + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base + add r10, #offArrayObject_contents @ r0<- pointer to slot + GET_INST_OPCODE(ip) @ extract opcode from rINST + str r9, [r10] @ vBB[vCC]<- vAA + strb r2, [r2, r10, lsr #GC_CARD_SHIFT] @ mark card + GOTO_OPCODE(ip) @ jump to next instruction .LOP_APUT_OBJECT_skip_check: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST GET_INST_OPCODE(ip) @ extract opcode from rINST @@ -8402,11 +8418,15 @@ dvmAsmSisterStart: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST @ no-op @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_IPUT_BOOLEAN */ @@ -8635,19 +8655,18 @@ dvmAsmSisterStart: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + @ no-op @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SPUT_BOOLEAN */ @@ -9241,11 +9260,15 @@ d2l_doconv: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST SMP_DMB @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SGET_OBJECT_VOLATILE */ @@ -9264,19 +9287,18 @@ d2l_doconv: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT_VOLATILE */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_VOLATILE_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + SMP_DMB @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction .size dvmAsmSisterStart, .-dvmAsmSisterStart .global dvmAsmSisterEnd diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S index 32484e2ec..dc336387a 100644 --- a/vm/mterp/out/InterpAsm-armv7-a.S +++ b/vm/mterp/out/InterpAsm-armv7-a.S @@ -2636,11 +2636,10 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT: /* 0x5b */ /* File: armv5te/OP_IPUT_OBJECT.S */ -/* File: armv5te/OP_IPUT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2659,7 +2658,6 @@ dalvik_inst: bne .LOP_IPUT_OBJECT_finish @ yes, finish up b common_exceptionThrown - /* ------------------------------ */ .balign 64 .L_OP_IPUT_BOOLEAN: /* 0x5c */ @@ -2669,7 +2667,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2698,7 +2696,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2727,7 +2725,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2756,7 +2754,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -2971,7 +2969,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3021,11 +3019,10 @@ dalvik_inst: .balign 64 .L_OP_SPUT_OBJECT: /* 0x69 */ /* File: armv5te/OP_SPUT_OBJECT.S */ -/* File: armv5te/OP_SPUT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3033,15 +3030,14 @@ dalvik_inst: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - @ no-op @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_finish @ yes, finish + b common_exceptionThrown @ no, handle exception /* ------------------------------ */ @@ -3052,7 +3048,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3079,7 +3075,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3106,7 +3102,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -3133,7 +3129,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7089,7 +7085,7 @@ dalvik_inst: /* * General 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput, iput-boolean, iput-byte, iput-char, iput-short */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -7144,7 +7140,7 @@ dalvik_inst: /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7500,8 +7496,7 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */ /* File: armv5te/OP_IPUT_OBJECT_QUICK.S */ -/* File: armv5te/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-object-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B GET_VREG(r3, r2) @ r3<- fp[B], the object pointer @@ -7511,12 +7506,14 @@ dalvik_inst: beq common_errNullObject @ object was null and r2, r2, #15 GET_VREG(r0, r2) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base FETCH_ADVANCE_INST(2) @ advance rPC, load rINST str r0, [r3, r1] @ obj.field (always 32 bits)<- r0 + cmp r0, #0 + strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card on non-null store GET_INST_OPCODE(ip) @ extract opcode from rINST GOTO_OPCODE(ip) @ jump to next instruction - /* ------------------------------ */ .balign 64 .L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */ @@ -7629,11 +7626,11 @@ dalvik_inst: .balign 64 .L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */ /* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_IPUT.S */ +/* File: armv5te/OP_IPUT_OBJECT.S */ /* - * General 32-bit instance field put. + * 32-bit instance field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object, iput-object-volatile */ /* op vA, vB, field@CCCC */ mov r0, rINST, lsr #12 @ r0<- B @@ -7684,11 +7681,11 @@ dalvik_inst: .balign 64 .L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */ /* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */ -/* File: armv5te/OP_SPUT.S */ +/* File: armv5te/OP_SPUT_OBJECT.S */ /* - * General 32-bit SPUT handler. + * 32-bit SPUT handler for objects * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput-object, sput-object-volatile */ /* op vAA, field@BBBB */ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex @@ -7696,15 +7693,15 @@ dalvik_inst: ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr cmp r0, #0 @ is resolved entry null? - beq .LOP_SPUT_OBJECT_VOLATILE_resolve @ yes, do resolve -.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 - mov r2, rINST, lsr #8 @ r2<- AA - FETCH_ADVANCE_INST(2) @ advance rPC, load rINST - GET_VREG(r1, r2) @ r1<- fp[AA] - GET_INST_OPCODE(ip) @ extract opcode from rINST - SMP_DMB @ releasing store - str r1, [r0, #offStaticField_value] @ field<- vAA - GOTO_OPCODE(ip) @ jump to next instruction + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue + ldr r2, [rGLUE, #offGlue_method] @ r2<- current method + EXPORT_PC() @ resolve() could throw, so export now + ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz + bl dvmResolveStaticField @ r0<- resolved StaticField ptr + cmp r0, #0 @ success? + bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish + b common_exceptionThrown @ no, handle exception + /* ------------------------------ */ @@ -7980,15 +7977,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 0 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -7996,7 +7993,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8028,8 +8026,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8058,15 +8061,15 @@ dvmAsmSisterStart: .LOP_FILLED_NEW_ARRAY_RANGE_continue: ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags - ldrb r3, [r3, #1] @ r3<- descriptor[1] + ldrb rINST, [r3, #1] @ rINST<- descriptor[1] .if 1 mov r1, r10 @ r1<- AA (length) .else mov r1, r10, lsr #4 @ r1<- B (length) .endif - cmp r3, #'I' @ array of ints? - cmpne r3, #'L' @ array of objects? - cmpne r3, #'[' @ array of arrays? + cmp rINST, #'I' @ array of ints? + cmpne rINST, #'L' @ array of objects? + cmpne rINST, #'[' @ array of arrays? mov r9, r1 @ save length in r9 bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags) @@ -8074,7 +8077,8 @@ dvmAsmSisterStart: beq common_exceptionThrown @ alloc failed, handle exception FETCH(r1, 2) @ r1<- FEDC or CCCC - str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array + str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type add r0, r0, #offArrayObject_contents @ r0<- newArray->contents subs r9, r9, #1 @ length--, check for neg FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST @@ -8106,8 +8110,13 @@ dvmAsmSisterStart: .endif 2: - GET_INST_OPCODE(ip) @ ip<- opcode from rINST - GOTO_OPCODE(ip) @ execute it + ldr r0, [rGLUE, #offGlue_retval] @ r0<- object + ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ ip<- opcode from rINST + cmp r1, #'I' @ Is int array? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card if not + GOTO_OPCODE(ip) @ execute it /* * Throw an exception indicating that we have not implemented this @@ -8201,6 +8210,13 @@ dvmAsmSisterStart: bl dvmCanPutArrayElement @ test object type vs. array type cmp r0, #0 @ okay? beq common_errArrayStore @ no + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base + add r10, #offArrayObject_contents @ r0<- pointer to slot + GET_INST_OPCODE(ip) @ extract opcode from rINST + str r9, [r10] @ vBB[vCC]<- vAA + strb r2, [r2, r10, lsr #GC_CARD_SHIFT] @ mark card + GOTO_OPCODE(ip) @ jump to next instruction .LOP_APUT_OBJECT_skip_check: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST GET_INST_OPCODE(ip) @ extract opcode from rINST @@ -8402,11 +8418,15 @@ dvmAsmSisterStart: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST @ no-op @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_IPUT_BOOLEAN */ @@ -8635,19 +8655,18 @@ dvmAsmSisterStart: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + @ no-op @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SPUT_BOOLEAN */ @@ -9241,11 +9260,15 @@ d2l_doconv: and r1, r1, #15 @ r1<- A cmp r9, #0 @ check object for null GET_VREG(r0, r1) @ r0<- fp[A] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base beq common_errNullObject @ object was null FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + add r9, r3 @ r9<- direct ptr to target location GET_INST_OPCODE(ip) @ extract opcode from rINST SMP_DMB @ releasing store - str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0 + str r0, [r9] @ obj.field (8/16/32 bits)<- r0 + cmp r0, #0 @ stored a null reference? + strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not GOTO_OPCODE(ip) @ jump to next instruction /* continuation for OP_SGET_OBJECT_VOLATILE */ @@ -9264,19 +9287,18 @@ d2l_doconv: b common_exceptionThrown @ no, handle exception /* continuation for OP_SPUT_OBJECT_VOLATILE */ - - /* - * Continuation if the field has not yet been resolved. - * r1: BBBB field ref - */ -.LOP_SPUT_OBJECT_VOLATILE_resolve: - ldr r2, [rGLUE, #offGlue_method] @ r2<- current method - EXPORT_PC() @ resolve() could throw, so export now - ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz - bl dvmResolveStaticField @ r0<- resolved StaticField ptr - cmp r0, #0 @ success? - bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish - b common_exceptionThrown @ no, handle exception +.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0 + mov r2, rINST, lsr #8 @ r2<- AA + FETCH_ADVANCE_INST(2) @ advance rPC, load rINST + GET_VREG(r1, r2) @ r1<- fp[AA] + ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base + GET_INST_OPCODE(ip) @ extract opcode from rINST + add r0, #offStaticField_value @ r0<- pointer to store target + SMP_DMB @ releasing store + str r1, [r0] @ field<- vAA + cmp r1, #0 @ stored a null object? + strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ mark card if not + GOTO_OPCODE(ip) @ jump to next instruction .size dvmAsmSisterStart, .-dvmAsmSisterStart .global dvmAsmSisterEnd diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S index 914b1d170..5dd26010d 100644 --- a/vm/mterp/out/InterpAsm-x86.S +++ b/vm/mterp/out/InterpAsm-x86.S @@ -2221,12 +2221,10 @@ dvmAsmInstructionStart = .L_OP_NOP .balign 64 .L_OP_IPUT_OBJECT: /* 0x5b */ /* File: x86/OP_IPUT_OBJECT.S */ -/* File: x86/OP_IPUT.S */ - /* - * General 32-bit instance field put. + * Object field put. * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + * for: iput-object */ /* op vA, vB, field@CCCC */ GET_GLUE(%ecx) @@ -2246,7 +2244,6 @@ dvmAsmInstructionStart = .L_OP_NOP GET_GLUE(rIBASE) jmp .LOP_IPUT_OBJECT_resolve - /* ------------------------------ */ .balign 64 .L_OP_IPUT_BOOLEAN: /* 0x5c */ @@ -2560,7 +2557,7 @@ dvmAsmInstructionStart = .L_OP_NOP /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ GET_GLUE(%ecx) @@ -2609,28 +2606,21 @@ dvmAsmInstructionStart = .L_OP_NOP .balign 64 .L_OP_SPUT_OBJECT: /* 0x69 */ /* File: x86/OP_SPUT_OBJECT.S */ -/* File: x86/OP_SPUT.S */ /* - * General 32-bit SPUT handler. - * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * SPUT object handler. */ /* op vAA, field@BBBB */ GET_GLUE(%ecx) movzwl 2(rPC),%eax # eax<- field ref BBBB movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields - movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr + movl (%ecx,%eax,4),%eax # eax<- resolved StaticField testl %eax,%eax # resolved entry null? je .LOP_SPUT_OBJECT_resolve # if not, make it so .LOP_SPUT_OBJECT_finish: # field ptr in eax movzbl rINST_HI,%ecx # ecx<- AA GET_VREG(%ecx,%ecx) - FETCH_INST_WORD(2) - movl %ecx,offStaticField_value(%eax) - ADVANCE_PC(2) - GOTO_NEXT - + jmp .LOP_SPUT_OBJECT_continue /* ------------------------------ */ .balign 64 @@ -2640,7 +2630,7 @@ dvmAsmInstructionStart = .L_OP_NOP /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ GET_GLUE(%ecx) @@ -2667,7 +2657,7 @@ dvmAsmInstructionStart = .L_OP_NOP /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ GET_GLUE(%ecx) @@ -2694,7 +2684,7 @@ dvmAsmInstructionStart = .L_OP_NOP /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ GET_GLUE(%ecx) @@ -2721,7 +2711,7 @@ dvmAsmInstructionStart = .L_OP_NOP /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ GET_GLUE(%ecx) @@ -5684,73 +5674,145 @@ dvmAsmInstructionStart = .L_OP_NOP /* ------------------------------ */ .balign 64 .L_OP_IGET_VOLATILE: /* 0xe3 */ - /* (stub) */ - GET_GLUE(%ecx) - SAVE_PC_TO_GLUE(%ecx) # only need to export these two - SAVE_FP_TO_GLUE(%ecx) # only need to export these two - movl %ecx,OUT_ARG0(%esp) # glue is first arg to function - call dvmMterp_OP_IGET_VOLATILE # do the real work +/* File: x86/OP_IGET_VOLATILE.S */ +/* File: x86/OP_IGET.S */ + /* + * General 32-bit instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + /* op vA, vB, field@CCCC */ GET_GLUE(%ecx) - LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values - LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values - FETCH_INST() - GOTO_NEXT + SPILL(rIBASE) # need another reg + movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC + movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex + movzbl rINST_HI,%ecx # ecx<- BA + sarl $4,%ecx # ecx<- B + movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields + movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA + andb $0xf,rINST_LO # rINST_FULL<- A + GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr + movl (%eax,rIBASE,4),%eax # resolved entry + testl %eax,%eax # is resolved entry null? + jne .LOP_IGET_VOLATILE_finish # no, already resolved + movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField + GET_GLUE(rIBASE) + jmp .LOP_IGET_VOLATILE_resolve + + /* ------------------------------ */ .balign 64 .L_OP_IPUT_VOLATILE: /* 0xe4 */ - /* (stub) */ - GET_GLUE(%ecx) - SAVE_PC_TO_GLUE(%ecx) # only need to export these two - SAVE_FP_TO_GLUE(%ecx) # only need to export these two - movl %ecx,OUT_ARG0(%esp) # glue is first arg to function - call dvmMterp_OP_IPUT_VOLATILE # do the real work +/* File: x86/OP_IPUT_VOLATILE.S */ +/* File: x86/OP_IPUT.S */ + + /* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ GET_GLUE(%ecx) - LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values - LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values - FETCH_INST() - GOTO_NEXT + SPILL(rIBASE) # need another reg + movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC + movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex + movzbl rINST_HI,%ecx # ecx<- BA + sarl $4,%ecx # ecx<- B + movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields + movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA + andb $0xf,rINST_LO # rINST_FULL<- A + GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr + movl (%eax,rIBASE,4),%eax # resolved entry + testl %eax,%eax # is resolved entry null? + jne .LOP_IPUT_VOLATILE_finish # no, already resolved + movl rIBASE,OUT_ARG1(%esp) + GET_GLUE(rIBASE) + jmp .LOP_IPUT_VOLATILE_resolve + + /* ------------------------------ */ .balign 64 .L_OP_SGET_VOLATILE: /* 0xe5 */ - /* (stub) */ - GET_GLUE(%ecx) - SAVE_PC_TO_GLUE(%ecx) # only need to export these two - SAVE_FP_TO_GLUE(%ecx) # only need to export these two - movl %ecx,OUT_ARG0(%esp) # glue is first arg to function - call dvmMterp_OP_SGET_VOLATILE # do the real work +/* File: x86/OP_SGET_VOLATILE.S */ +/* File: x86/OP_SGET.S */ + /* + * General 32-bit SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field@BBBB */ GET_GLUE(%ecx) - LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values - LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values - FETCH_INST() + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex + movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields + movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr + testl %eax,%eax # resolved entry null? + je .LOP_SGET_VOLATILE_resolve # if not, make it so +.LOP_SGET_VOLATILE_finish: # field ptr in eax + movl offStaticField_value(%eax),%eax + movzbl rINST_HI,%ecx # ecx<- AA + FETCH_INST_WORD(2) + ADVANCE_PC(2) + SET_VREG(%eax,%ecx) GOTO_NEXT + + /* ------------------------------ */ .balign 64 .L_OP_SPUT_VOLATILE: /* 0xe6 */ - /* (stub) */ - GET_GLUE(%ecx) - SAVE_PC_TO_GLUE(%ecx) # only need to export these two - SAVE_FP_TO_GLUE(%ecx) # only need to export these two - movl %ecx,OUT_ARG0(%esp) # glue is first arg to function - call dvmMterp_OP_SPUT_VOLATILE # do the real work +/* File: x86/OP_SPUT_VOLATILE.S */ +/* File: x86/OP_SPUT.S */ + /* + * General 32-bit SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ GET_GLUE(%ecx) - LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values - LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values - FETCH_INST() + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex + movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields + movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr + testl %eax,%eax # resolved entry null? + je .LOP_SPUT_VOLATILE_resolve # if not, make it so +.LOP_SPUT_VOLATILE_finish: # field ptr in eax + movzbl rINST_HI,%ecx # ecx<- AA + GET_VREG(%ecx,%ecx) + FETCH_INST_WORD(2) + movl %ecx,offStaticField_value(%eax) + ADVANCE_PC(2) GOTO_NEXT + + /* ------------------------------ */ .balign 64 .L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */ - /* (stub) */ - GET_GLUE(%ecx) - SAVE_PC_TO_GLUE(%ecx) # only need to export these two - SAVE_FP_TO_GLUE(%ecx) # only need to export these two - movl %ecx,OUT_ARG0(%esp) # glue is first arg to function - call dvmMterp_OP_IGET_OBJECT_VOLATILE # do the real work +/* File: x86/OP_IGET_OBJECT_VOLATILE.S */ +/* File: x86/OP_IGET.S */ + /* + * General 32-bit instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + /* op vA, vB, field@CCCC */ GET_GLUE(%ecx) - LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values - LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values - FETCH_INST() - GOTO_NEXT + SPILL(rIBASE) # need another reg + movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC + movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex + movzbl rINST_HI,%ecx # ecx<- BA + sarl $4,%ecx # ecx<- B + movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields + movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA + andb $0xf,rINST_LO # rINST_FULL<- A + GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr + movl (%eax,rIBASE,4),%eax # resolved entry + testl %eax,%eax # is resolved entry null? + jne .LOP_IGET_OBJECT_VOLATILE_finish # no, already resolved + movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField + GET_GLUE(rIBASE) + jmp .LOP_IGET_OBJECT_VOLATILE_resolve + + /* ------------------------------ */ .balign 64 .L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */ @@ -5968,7 +6030,7 @@ dvmAsmInstructionStart = .L_OP_NOP .balign 64 .L_OP_IPUT_QUICK: /* 0xf5 */ /* File: x86/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-quick */ /* op vA, vB, offset@CCCC */ movzbl rINST_HI,%ecx # ecx<- BA sarl $4,%ecx # ecx<- B @@ -6011,8 +6073,7 @@ dvmAsmInstructionStart = .L_OP_NOP .balign 64 .L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */ /* File: x86/OP_IPUT_OBJECT_QUICK.S */ -/* File: x86/OP_IPUT_QUICK.S */ - /* For: iput-quick, iput-object-quick */ + /* For: iput-object-quick */ /* op vA, vB, offset@CCCC */ movzbl rINST_HI,%ecx # ecx<- BA sarl $4,%ecx # ecx<- B @@ -6021,13 +6082,11 @@ dvmAsmInstructionStart = .L_OP_NOP andb $0xf,rINST_LO # rINST_FULL<- A GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A] movzwl 2(rPC),%eax # eax<- field byte offset - testl %ecx,%ecx # is object null? + testl %ecx,%ecx # is object null? je common_errNullObject movl rINST_FULL,(%ecx,%eax,1) - FETCH_INST_WORD(2) - ADVANCE_PC(2) - GOTO_NEXT - + GET_GLUE(%eax) + jmp .LOP_IPUT_OBJECT_QUICK_finish /* ------------------------------ */ .balign 64 @@ -6142,45 +6201,81 @@ dvmAsmInstructionStart = .L_OP_NOP /* ------------------------------ */ .balign 64 .L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */ - /* (stub) */ - GET_GLUE(%ecx) - SAVE_PC_TO_GLUE(%ecx) # only need to export these two - SAVE_FP_TO_GLUE(%ecx) # only need to export these two - movl %ecx,OUT_ARG0(%esp) # glue is first arg to function - call dvmMterp_OP_IPUT_OBJECT_VOLATILE # do the real work +/* File: x86/OP_IPUT_OBJECT_VOLATILE.S */ +/* File: x86/OP_IPUT_OBJECT.S */ + /* + * Object field put. + * + * for: iput-object + */ + /* op vA, vB, field@CCCC */ GET_GLUE(%ecx) - LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values - LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values - FETCH_INST() - GOTO_NEXT + SPILL(rIBASE) # need another reg + movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC + movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex + movzbl rINST_HI,%ecx # ecx<- BA + sarl $4,%ecx # ecx<- B + movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields + movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA + andb $0xf,rINST_LO # rINST_FULL<- A + GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr + movl (%eax,rIBASE,4),%eax # resolved entry + testl %eax,%eax # is resolved entry null? + jne .LOP_IPUT_OBJECT_VOLATILE_finish # no, already resolved + movl rIBASE,OUT_ARG1(%esp) + GET_GLUE(rIBASE) + jmp .LOP_IPUT_OBJECT_VOLATILE_resolve + + /* ------------------------------ */ .balign 64 .L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */ - /* (stub) */ - GET_GLUE(%ecx) - SAVE_PC_TO_GLUE(%ecx) # only need to export these two - SAVE_FP_TO_GLUE(%ecx) # only need to export these two - movl %ecx,OUT_ARG0(%esp) # glue is first arg to function - call dvmMterp_OP_SGET_OBJECT_VOLATILE # do the real work +/* File: x86/OP_SGET_OBJECT_VOLATILE.S */ +/* File: x86/OP_SGET.S */ + /* + * General 32-bit SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field@BBBB */ GET_GLUE(%ecx) - LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values - LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values - FETCH_INST() + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex + movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields + movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr + testl %eax,%eax # resolved entry null? + je .LOP_SGET_OBJECT_VOLATILE_resolve # if not, make it so +.LOP_SGET_OBJECT_VOLATILE_finish: # field ptr in eax + movl offStaticField_value(%eax),%eax + movzbl rINST_HI,%ecx # ecx<- AA + FETCH_INST_WORD(2) + ADVANCE_PC(2) + SET_VREG(%eax,%ecx) GOTO_NEXT + + /* ------------------------------ */ .balign 64 .L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */ - /* (stub) */ - GET_GLUE(%ecx) - SAVE_PC_TO_GLUE(%ecx) # only need to export these two - SAVE_FP_TO_GLUE(%ecx) # only need to export these two - movl %ecx,OUT_ARG0(%esp) # glue is first arg to function - call dvmMterp_OP_SPUT_OBJECT_VOLATILE # do the real work +/* File: x86/OP_SPUT_OBJECT_VOLATILE.S */ +/* File: x86/OP_SPUT_OBJECT.S */ + /* + * SPUT object handler. + */ + /* op vAA, field@BBBB */ GET_GLUE(%ecx) - LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values - LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values - FETCH_INST() - GOTO_NEXT + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex + movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields + movl (%ecx,%eax,4),%eax # eax<- resolved StaticField + testl %eax,%eax # resolved entry null? + je .LOP_SPUT_OBJECT_VOLATILE_resolve # if not, make it so +.LOP_SPUT_OBJECT_VOLATILE_finish: # field ptr in eax + movzbl rINST_HI,%ecx # ecx<- AA + GET_VREG(%ecx,%ecx) + jmp .LOP_SPUT_OBJECT_VOLATILE_continue + + /* ------------------------------ */ .balign 64 .L_OP_UNUSED_FF: /* 0xff */ @@ -6570,6 +6665,7 @@ dvmAsmSisterStart: movl $ALLOC_DONT_TRACK,OUT_ARG2(%esp) # arg2<- flags movzbl 1(%ecx),%ecx # ecx<- descriptor[1] movl %eax,OUT_ARG0(%esp) # arg0<- arrayClass + GET_GLUE(%eax) cmpb $'I',%cl # supported? je 1f cmpb $'L',%cl @@ -6577,6 +6673,7 @@ dvmAsmSisterStart: cmpb $'[',%cl jne .LOP_FILLED_NEW_ARRAY_notimpl # no, not handled yet 1: + movl %ecx,offGlue_retval+4(%eax) # save type .if (!0) SPILL_TMP(rINST_FULL) # save copy, need "B" later sarl $4,rINST_FULL @@ -6610,7 +6707,9 @@ dvmAsmSisterStart: FETCH_INST_WORD(3) rep movsd + GET_GLUE(%ecx) UNSPILL(rIBASE) + movl offGlue_retval+4(%ecx),%eax # eax<- type UNSPILL(rFP) .else testl rINST_FULL,rINST_FULL @@ -6629,10 +6728,19 @@ dvmAsmSisterStart: sub $1,rINST_FULL jne 3b 4: + GET_GLUE(%ecx) UNSPILL(rPC) + movl offGlue_retval+4(%ecx),%eax # eax<- type FETCH_INST_WORD(3) .endif + cmpb $'I',%al # Int array? + je 5f # skip card mark if so + movl offGlue_retval(%ecx),%eax # eax<- object head + movl offGlue_cardTable(%ecx),%ecx # card table base + shrl $GC_CARD_SHIFT,%eax # convert to card num + movb %cl,(%ecx,%eax) # mark card +5: ADVANCE_PC(3) GOTO_NEXT @@ -6674,6 +6782,7 @@ dvmAsmSisterStart: movl $ALLOC_DONT_TRACK,OUT_ARG2(%esp) # arg2<- flags movzbl 1(%ecx),%ecx # ecx<- descriptor[1] movl %eax,OUT_ARG0(%esp) # arg0<- arrayClass + GET_GLUE(%eax) cmpb $'I',%cl # supported? je 1f cmpb $'L',%cl @@ -6681,6 +6790,7 @@ dvmAsmSisterStart: cmpb $'[',%cl jne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl # no, not handled yet 1: + movl %ecx,offGlue_retval+4(%eax) # save type .if (!1) SPILL_TMP(rINST_FULL) # save copy, need "B" later sarl $4,rINST_FULL @@ -6714,7 +6824,9 @@ dvmAsmSisterStart: FETCH_INST_WORD(3) rep movsd + GET_GLUE(%ecx) UNSPILL(rIBASE) + movl offGlue_retval+4(%ecx),%eax # eax<- type UNSPILL(rFP) .else testl rINST_FULL,rINST_FULL @@ -6733,10 +6845,19 @@ dvmAsmSisterStart: sub $1,rINST_FULL jne 3b 4: + GET_GLUE(%ecx) UNSPILL(rPC) + movl offGlue_retval+4(%ecx),%eax # eax<- type FETCH_INST_WORD(3) .endif + cmpb $'I',%al # Int array? + je 5f # skip card mark if so + movl offGlue_retval(%ecx),%eax # eax<- object head + movl offGlue_cardTable(%ecx),%ecx # card table base + shrl $GC_CARD_SHIFT,%eax # convert to card num + movb %cl,(%ecx,%eax) # mark card +5: ADVANCE_PC(3) GOTO_NEXT @@ -6838,7 +6959,15 @@ dvmAsmSisterStart: UNSPILL(rPC) UNSPILL_TMP(%ecx) testl %eax,%eax + GET_GLUE(%eax) je common_errArrayStore + movl offGlue_cardTable(%eax),%eax # get card table base + movl rINST_FULL,(%ecx) + FETCH_INST_WORD(2) + shrl $GC_CARD_SHIFT,%ecx # convert addr to card number + movb %al,(%eax,%ecx) # mark card + ADVANCE_PC(2) + GOTO_NEXT .LOP_APUT_OBJECT_skip_check: movl rINST_FULL,(%ecx) @@ -7213,8 +7342,15 @@ dvmAsmSisterStart: UNSPILL(rIBASE) testl %ecx,%ecx # object null? je common_errNullObject # object was null - movl rINST_FULL,(%ecx,%eax,1) # obj.field <- v[A](8/16/32 bits) + movl rINST_FULL,(%ecx,%eax) # obj.field <- v[A](8/16/32 bits) + GET_GLUE(%eax) + testl rINST_FULL,rINST_FULL # stored a NULL? + movl offGlue_cardTable(%eax),%eax # get card table base FETCH_INST_WORD(2) + je 1f # skip card mark if null store + shrl $GC_CARD_SHIFT,%ecx # object head to card number + movb %al,(%eax,%ecx) # mark card +1: ADVANCE_PC(2) GOTO_NEXT @@ -7544,9 +7680,20 @@ dvmAsmSisterStart: /* continuation for OP_SPUT_OBJECT */ - /* - * Go resolve the field - */ + +.LOP_SPUT_OBJECT_continue: + movl %ecx,offStaticField_value(%eax) + testl %ecx,%ecx + GET_GLUE(%ecx) + FETCH_INST_WORD(2) + je 1f + movl offGlue_cardTable(%ecx),%ecx # get card table base + shrl $GC_CARD_SHIFT,%eax # head to card number + movb %cl,(%ecx,%eax) # mark card +1: + ADVANCE_PC(2) + GOTO_NEXT + .LOP_SPUT_OBJECT_resolve: GET_GLUE(%ecx) movzwl 2(rPC),%eax # eax<- field ref BBBB @@ -8319,6 +8466,156 @@ dvmAsmSisterStart: ADVANCE_PC(2) GOTO_NEXT +/* continuation for OP_IGET_VOLATILE */ + + +.LOP_IGET_VOLATILE_resolve: + EXPORT_PC() + SPILL(rPC) + movl offGlue_method(rIBASE),rPC # rPC<- current method + UNSPILL(rIBASE) + movl offMethod_clazz(rPC),rPC # rPC<- method->clazz + SPILL_TMP(%ecx) # save object pointer across call + movl rPC,OUT_ARG0(%esp) # pass in method->clazz + call dvmResolveInstField # ... to dvmResolveInstField + UNSPILL_TMP(%ecx) + UNSPILL(rPC) + testl %eax,%eax # ... which returns InstrField ptr + jne .LOP_IGET_VOLATILE_finish + jmp common_exceptionThrown + +.LOP_IGET_VOLATILE_finish: + /* + * Currently: + * eax holds resolved field + * ecx holds object + * rIBASE is scratch, but needs to be unspilled + * rINST_FULL holds A + */ + movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field + UNSPILL(rIBASE) + testl %ecx,%ecx # object null? + je common_errNullObject # object was null + movl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits) + movl rINST_FULL,%eax # eax<- A + FETCH_INST_WORD(2) + SET_VREG(%ecx,%eax) + ADVANCE_PC(2) + GOTO_NEXT + +/* continuation for OP_IPUT_VOLATILE */ + + +.LOP_IPUT_VOLATILE_resolve: + EXPORT_PC() + SPILL(rPC) + movl offGlue_method(rIBASE),rPC # rPC<- current method + UNSPILL(rIBASE) + movl offMethod_clazz(rPC),rPC # rPC<- method->clazz + SPILL_TMP(%ecx) # save object pointer across call + movl rPC,OUT_ARG0(%esp) # pass in method->clazz + call dvmResolveInstField # ... to dvmResolveInstField + UNSPILL_TMP(%ecx) + UNSPILL(rPC) + testl %eax,%eax # ... which returns InstrField ptr + jne .LOP_IPUT_VOLATILE_finish + jmp common_exceptionThrown + +.LOP_IPUT_VOLATILE_finish: + /* + * Currently: + * eax holds resolved field + * ecx holds object + * rIBASE is scratch, but needs to be unspilled + * rINST_FULL holds A + */ + GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A] + movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field + UNSPILL(rIBASE) + testl %ecx,%ecx # object null? + je common_errNullObject # object was null + movl rINST_FULL,(%ecx,%eax,1) # obj.field <- v[A](8/16/32 bits) + FETCH_INST_WORD(2) + ADVANCE_PC(2) + GOTO_NEXT + +/* continuation for OP_SGET_VOLATILE */ + + /* + * Go resolve the field + */ +.LOP_SGET_VOLATILE_resolve: + GET_GLUE(%ecx) + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_method(%ecx),%ecx # ecx<- current method + EXPORT_PC() # could throw, need to export + movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz + SPILL(rPC) + movl %eax,OUT_ARG1(%esp) + movl %ecx,OUT_ARG0(%esp) + call dvmResolveStaticField # eax<- resolved StaticField ptr + UNSPILL(rPC) + testl %eax,%eax + jne .LOP_SGET_VOLATILE_finish # success, continue + jmp common_exceptionThrown # no, handle exception + +/* continuation for OP_SPUT_VOLATILE */ + + /* + * Go resolve the field + */ +.LOP_SPUT_VOLATILE_resolve: + GET_GLUE(%ecx) + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_method(%ecx),%ecx # ecx<- current method + EXPORT_PC() # could throw, need to export + movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz + SPILL(rPC) + movl %eax,OUT_ARG1(%esp) + movl %ecx,OUT_ARG0(%esp) + call dvmResolveStaticField # eax<- resolved StaticField ptr + UNSPILL(rPC) + testl %eax,%eax + jne .LOP_SPUT_VOLATILE_finish # success, continue + jmp common_exceptionThrown # no, handle exception + +/* continuation for OP_IGET_OBJECT_VOLATILE */ + + +.LOP_IGET_OBJECT_VOLATILE_resolve: + EXPORT_PC() + SPILL(rPC) + movl offGlue_method(rIBASE),rPC # rPC<- current method + UNSPILL(rIBASE) + movl offMethod_clazz(rPC),rPC # rPC<- method->clazz + SPILL_TMP(%ecx) # save object pointer across call + movl rPC,OUT_ARG0(%esp) # pass in method->clazz + call dvmResolveInstField # ... to dvmResolveInstField + UNSPILL_TMP(%ecx) + UNSPILL(rPC) + testl %eax,%eax # ... which returns InstrField ptr + jne .LOP_IGET_OBJECT_VOLATILE_finish + jmp common_exceptionThrown + +.LOP_IGET_OBJECT_VOLATILE_finish: + /* + * Currently: + * eax holds resolved field + * ecx holds object + * rIBASE is scratch, but needs to be unspilled + * rINST_FULL holds A + */ + movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field + UNSPILL(rIBASE) + testl %ecx,%ecx # object null? + je common_errNullObject # object was null + movl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits) + movl rINST_FULL,%eax # eax<- A + FETCH_INST_WORD(2) + SET_VREG(%ecx,%eax) + ADVANCE_PC(2) + GOTO_NEXT + /* continuation for OP_EXECUTE_INLINE */ .LOP_EXECUTE_INLINE_continue: @@ -8361,6 +8658,113 @@ dvmAsmSisterStart: jmp *gDvmInlineOpsTable(%eax) # will return to caller of .LOP_EXECUTE_INLINE_continue +/* continuation for OP_IPUT_OBJECT_QUICK */ + +.LOP_IPUT_OBJECT_QUICK_finish: + testl rINST_FULL,rINST_FULL # did we store null? + FETCH_INST_WORD(2) + movl offGlue_cardTable(%eax),%eax # get card table base + je 1f # skip card mark if null store + shrl $GC_CARD_SHIFT,%ecx # object head to card number + movb %al,(%eax,%ecx) # mark card +1: + ADVANCE_PC(2) + GOTO_NEXT + +/* continuation for OP_IPUT_OBJECT_VOLATILE */ + + +.LOP_IPUT_OBJECT_VOLATILE_resolve: + EXPORT_PC() + SPILL(rPC) + movl offGlue_method(rIBASE),rPC # rPC<- current method + UNSPILL(rIBASE) + movl offMethod_clazz(rPC),rPC # rPC<- method->clazz + SPILL_TMP(%ecx) # save object pointer across call + movl rPC,OUT_ARG0(%esp) # pass in method->clazz + call dvmResolveInstField # ... to dvmResolveInstField + UNSPILL_TMP(%ecx) + UNSPILL(rPC) + testl %eax,%eax # ... which returns InstrField ptr + jne .LOP_IPUT_OBJECT_VOLATILE_finish + jmp common_exceptionThrown + +.LOP_IPUT_OBJECT_VOLATILE_finish: + /* + * Currently: + * eax holds resolved field + * ecx holds object + * rIBASE is scratch, but needs to be unspilled + * rINST_FULL holds A + */ + GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A] + movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field + UNSPILL(rIBASE) + testl %ecx,%ecx # object null? + je common_errNullObject # object was null + movl rINST_FULL,(%ecx,%eax) # obj.field <- v[A](8/16/32 bits) + GET_GLUE(%eax) + testl rINST_FULL,rINST_FULL # stored a NULL? + movl offGlue_cardTable(%eax),%eax # get card table base + FETCH_INST_WORD(2) + je 1f # skip card mark if null store + shrl $GC_CARD_SHIFT,%ecx # object head to card number + movb %al,(%eax,%ecx) # mark card +1: + ADVANCE_PC(2) + GOTO_NEXT + +/* continuation for OP_SGET_OBJECT_VOLATILE */ + + /* + * Go resolve the field + */ +.LOP_SGET_OBJECT_VOLATILE_resolve: + GET_GLUE(%ecx) + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_method(%ecx),%ecx # ecx<- current method + EXPORT_PC() # could throw, need to export + movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz + SPILL(rPC) + movl %eax,OUT_ARG1(%esp) + movl %ecx,OUT_ARG0(%esp) + call dvmResolveStaticField # eax<- resolved StaticField ptr + UNSPILL(rPC) + testl %eax,%eax + jne .LOP_SGET_OBJECT_VOLATILE_finish # success, continue + jmp common_exceptionThrown # no, handle exception + +/* continuation for OP_SPUT_OBJECT_VOLATILE */ + + +.LOP_SPUT_OBJECT_VOLATILE_continue: + movl %ecx,offStaticField_value(%eax) + testl %ecx,%ecx + GET_GLUE(%ecx) + FETCH_INST_WORD(2) + je 1f + movl offGlue_cardTable(%ecx),%ecx # get card table base + shrl $GC_CARD_SHIFT,%eax # head to card number + movb %cl,(%ecx,%eax) # mark card +1: + ADVANCE_PC(2) + GOTO_NEXT + +.LOP_SPUT_OBJECT_VOLATILE_resolve: + GET_GLUE(%ecx) + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_method(%ecx),%ecx # ecx<- current method + EXPORT_PC() # could throw, need to export + movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz + SPILL(rPC) + movl %eax,OUT_ARG1(%esp) + movl %ecx,OUT_ARG0(%esp) + call dvmResolveStaticField # eax<- resolved StaticField ptr + UNSPILL(rPC) + testl %eax,%eax + jne .LOP_SPUT_OBJECT_VOLATILE_finish # success, continue + jmp common_exceptionThrown # no, handle exception + .size dvmAsmSisterStart, .-dvmAsmSisterStart .global dvmAsmSisterEnd dvmAsmSisterEnd: diff --git a/vm/mterp/out/InterpC-allstubs.c b/vm/mterp/out/InterpC-allstubs.c index 39cd6ba6e..8f7f4ecc9 100644 --- a/vm/mterp/out/InterpC-allstubs.c +++ b/vm/mterp/out/InterpC-allstubs.c @@ -3641,7 +3641,6 @@ GOTO_TARGET(invokeSuperQuick, bool methodCallRange) GOTO_TARGET_END - /* * General handling for return-void, return, and return-wide. Put the * return value in "retval" before jumping here. @@ -3869,6 +3868,7 @@ GOTO_TARGET(exceptionThrown) GOTO_TARGET_END + /* * General handling for invoke-{virtual,super,direct,static,interface}, * including "quick" variants. @@ -4051,8 +4051,18 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, TRACE_METHOD_ENTER(self, methodToCall); #endif - ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, - methodToCall->name, methodToCall->shorty); +#if defined(WITH_JNI_TRACE) + bool trace = gDvm.jniTrace && + strstr(methodToCall->clazz->descriptor, gDvm.jniTrace); + if (trace) { + dvmLogNativeMethodEntry(methodToCall, newFp); + } + else +#endif + { + ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, + methodToCall->name, methodToCall->shorty); + } #if defined(WITH_JIT) /* Allow the Jit to end any pending trace building */ @@ -4080,6 +4090,12 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, dvmPopJniLocals(self, newSaveArea); self->curFrame = fp; +#if defined(WITH_JNI_TRACE) + if (trace) { + dvmLogNativeMethodExit(methodToCall, self, retval); + } +#endif + /* * If the native code threw an exception, or interpreted code * invoked by the native call threw one and nobody has cleared diff --git a/vm/mterp/out/InterpC-portdbg.c b/vm/mterp/out/InterpC-portdbg.c index 9468b24f7..92b97b0ab 100644 --- a/vm/mterp/out/InterpC-portdbg.c +++ b/vm/mterp/out/InterpC-portdbg.c @@ -3925,7 +3925,6 @@ GOTO_TARGET(invokeSuperQuick, bool methodCallRange) GOTO_TARGET_END - /* * General handling for return-void, return, and return-wide. Put the * return value in "retval" before jumping here. @@ -4153,6 +4152,7 @@ GOTO_TARGET(exceptionThrown) GOTO_TARGET_END + /* * General handling for invoke-{virtual,super,direct,static,interface}, * including "quick" variants. @@ -4335,8 +4335,18 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, TRACE_METHOD_ENTER(self, methodToCall); #endif - ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, - methodToCall->name, methodToCall->shorty); +#if defined(WITH_JNI_TRACE) + bool trace = gDvm.jniTrace && + strstr(methodToCall->clazz->descriptor, gDvm.jniTrace); + if (trace) { + dvmLogNativeMethodEntry(methodToCall, newFp); + } + else +#endif + { + ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, + methodToCall->name, methodToCall->shorty); + } #if defined(WITH_JIT) /* Allow the Jit to end any pending trace building */ @@ -4364,6 +4374,12 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, dvmPopJniLocals(self, newSaveArea); self->curFrame = fp; +#if defined(WITH_JNI_TRACE) + if (trace) { + dvmLogNativeMethodExit(methodToCall, self, retval); + } +#endif + /* * If the native code threw an exception, or interpreted code * invoked by the native call threw one and nobody has cleared diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c index 5b0d45b00..9be8a8e16 100644 --- a/vm/mterp/out/InterpC-portstd.c +++ b/vm/mterp/out/InterpC-portstd.c @@ -3664,7 +3664,6 @@ GOTO_TARGET(invokeSuperQuick, bool methodCallRange) GOTO_TARGET_END - /* * General handling for return-void, return, and return-wide. Put the * return value in "retval" before jumping here. @@ -3892,6 +3891,7 @@ GOTO_TARGET(exceptionThrown) GOTO_TARGET_END + /* * General handling for invoke-{virtual,super,direct,static,interface}, * including "quick" variants. @@ -4074,8 +4074,18 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, TRACE_METHOD_ENTER(self, methodToCall); #endif - ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, - methodToCall->name, methodToCall->shorty); +#if defined(WITH_JNI_TRACE) + bool trace = gDvm.jniTrace && + strstr(methodToCall->clazz->descriptor, gDvm.jniTrace); + if (trace) { + dvmLogNativeMethodEntry(methodToCall, newFp); + } + else +#endif + { + ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, + methodToCall->name, methodToCall->shorty); + } #if defined(WITH_JIT) /* Allow the Jit to end any pending trace building */ @@ -4103,6 +4113,12 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, dvmPopJniLocals(self, newSaveArea); self->curFrame = fp; +#if defined(WITH_JNI_TRACE) + if (trace) { + dvmLogNativeMethodExit(methodToCall, self, retval); + } +#endif + /* * If the native code threw an exception, or interpreted code * invoked by the native call threw one and nobody has cleared diff --git a/vm/mterp/out/InterpC-x86-atom.c b/vm/mterp/out/InterpC-x86-atom.c index 7da241c70..13d05cc22 100644 --- a/vm/mterp/out/InterpC-x86-atom.c +++ b/vm/mterp/out/InterpC-x86-atom.c @@ -1835,7 +1835,6 @@ GOTO_TARGET(invokeSuperQuick, bool methodCallRange) GOTO_TARGET_END - /* * General handling for return-void, return, and return-wide. Put the * return value in "retval" before jumping here. @@ -2063,6 +2062,7 @@ GOTO_TARGET(exceptionThrown) GOTO_TARGET_END + /* * General handling for invoke-{virtual,super,direct,static,interface}, * including "quick" variants. @@ -2245,8 +2245,18 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, TRACE_METHOD_ENTER(self, methodToCall); #endif - ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, - methodToCall->name, methodToCall->shorty); +#if defined(WITH_JNI_TRACE) + bool trace = gDvm.jniTrace && + strstr(methodToCall->clazz->descriptor, gDvm.jniTrace); + if (trace) { + dvmLogNativeMethodEntry(methodToCall, newFp); + } + else +#endif + { + ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, + methodToCall->name, methodToCall->shorty); + } #if defined(WITH_JIT) /* Allow the Jit to end any pending trace building */ @@ -2274,6 +2284,12 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, dvmPopJniLocals(self, newSaveArea); self->curFrame = fp; +#if defined(WITH_JNI_TRACE) + if (trace) { + dvmLogNativeMethodExit(methodToCall, self, retval); + } +#endif + /* * If the native code threw an exception, or interpreted code * invoked by the native call threw one and nobody has cleared diff --git a/vm/mterp/out/InterpC-x86.c b/vm/mterp/out/InterpC-x86.c index 86fe9729e..1ce03027f 100644 --- a/vm/mterp/out/InterpC-x86.c +++ b/vm/mterp/out/InterpC-x86.c @@ -1201,26 +1201,6 @@ GOTO_TARGET_DECL(exceptionThrown); } \ FINISH(2); -/* File: c/OP_IGET_VOLATILE.c */ -HANDLE_IGET_X(OP_IGET_VOLATILE, "-volatile", IntVolatile, ) -OP_END - -/* File: c/OP_IPUT_VOLATILE.c */ -HANDLE_IPUT_X(OP_IPUT_VOLATILE, "-volatile", IntVolatile, ) -OP_END - -/* File: c/OP_SGET_VOLATILE.c */ -HANDLE_SGET_X(OP_SGET_VOLATILE, "-volatile", IntVolatile, ) -OP_END - -/* File: c/OP_SPUT_VOLATILE.c */ -HANDLE_SPUT_X(OP_SPUT_VOLATILE, "-volatile", IntVolatile, ) -OP_END - -/* File: c/OP_IGET_OBJECT_VOLATILE.c */ -HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT) -OP_END - /* File: c/OP_IGET_WIDE_VOLATILE.c */ HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE) OP_END @@ -1282,18 +1262,6 @@ HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/) FINISH(3); OP_END -/* File: c/OP_IPUT_OBJECT_VOLATILE.c */ -HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT) -OP_END - -/* File: c/OP_SGET_OBJECT_VOLATILE.c */ -HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT) -OP_END - -/* File: c/OP_SPUT_OBJECT_VOLATILE.c */ -HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT) -OP_END - /* File: c/gotoTargets.c */ /* * C footer. This has some common code shared by the various targets. @@ -1804,7 +1772,6 @@ GOTO_TARGET(invokeSuperQuick, bool methodCallRange) GOTO_TARGET_END - /* * General handling for return-void, return, and return-wide. Put the * return value in "retval" before jumping here. @@ -2032,6 +1999,7 @@ GOTO_TARGET(exceptionThrown) GOTO_TARGET_END + /* * General handling for invoke-{virtual,super,direct,static,interface}, * including "quick" variants. @@ -2214,8 +2182,18 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, TRACE_METHOD_ENTER(self, methodToCall); #endif - ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, - methodToCall->name, methodToCall->shorty); +#if defined(WITH_JNI_TRACE) + bool trace = gDvm.jniTrace && + strstr(methodToCall->clazz->descriptor, gDvm.jniTrace); + if (trace) { + dvmLogNativeMethodEntry(methodToCall, newFp); + } + else +#endif + { + ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, + methodToCall->name, methodToCall->shorty); + } #if defined(WITH_JIT) /* Allow the Jit to end any pending trace building */ @@ -2243,6 +2221,12 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, dvmPopJniLocals(self, newSaveArea); self->curFrame = fp; +#if defined(WITH_JNI_TRACE) + if (trace) { + dvmLogNativeMethodExit(methodToCall, self, retval); + } +#endif + /* * If the native code threw an exception, or interpreted code * invoked by the native call threw one and nobody has cleared diff --git a/vm/mterp/x86-atom/TODO.txt b/vm/mterp/x86-atom/TODO.txt index 5b89db95d..abeee31eb 100644 --- a/vm/mterp/x86-atom/TODO.txt +++ b/vm/mterp/x86-atom/TODO.txt @@ -1,5 +1,6 @@ Items requiring attention: +(hi) Add gc card marking to aput/iput/sput/new_filled_array (hi) Correct stack overflow handling (dvmCleanupStackOverflow takes an additional argument now) (hi) "debugger active" test in common_periodicChecks must handle diff --git a/vm/mterp/x86/OP_APUT_OBJECT.S b/vm/mterp/x86/OP_APUT_OBJECT.S index 20699503c..d2ec56557 100644 --- a/vm/mterp/x86/OP_APUT_OBJECT.S +++ b/vm/mterp/x86/OP_APUT_OBJECT.S @@ -37,7 +37,15 @@ UNSPILL(rPC) UNSPILL_TMP(%ecx) testl %eax,%eax + GET_GLUE(%eax) je common_errArrayStore + movl offGlue_cardTable(%eax),%eax # get card table base + movl rINST_FULL,(%ecx) + FETCH_INST_WORD(2) + shrl $$GC_CARD_SHIFT,%ecx # convert addr to card number + movb %al,(%eax,%ecx) # mark card + ADVANCE_PC(2) + GOTO_NEXT .L${opcode}_skip_check: movl rINST_FULL,(%ecx) diff --git a/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S index a98f6b955..b990aeb81 100644 --- a/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S +++ b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S @@ -48,6 +48,7 @@ movl $$ALLOC_DONT_TRACK,OUT_ARG2(%esp) # arg2<- flags movzbl 1(%ecx),%ecx # ecx<- descriptor[1] movl %eax,OUT_ARG0(%esp) # arg0<- arrayClass + GET_GLUE(%eax) cmpb $$'I',%cl # supported? je 1f cmpb $$'L',%cl @@ -55,6 +56,7 @@ cmpb $$'[',%cl jne .L${opcode}_notimpl # no, not handled yet 1: + movl %ecx,offGlue_retval+4(%eax) # save type .if (!$isrange) SPILL_TMP(rINST_FULL) # save copy, need "B" later sarl $$4,rINST_FULL @@ -88,7 +90,9 @@ FETCH_INST_WORD(3) rep movsd + GET_GLUE(%ecx) UNSPILL(rIBASE) + movl offGlue_retval+4(%ecx),%eax # eax<- type UNSPILL(rFP) .else testl rINST_FULL,rINST_FULL @@ -107,10 +111,19 @@ sub $$1,rINST_FULL jne 3b 4: + GET_GLUE(%ecx) UNSPILL(rPC) + movl offGlue_retval+4(%ecx),%eax # eax<- type FETCH_INST_WORD(3) .endif + cmpb $$'I',%al # Int array? + je 5f # skip card mark if so + movl offGlue_retval(%ecx),%eax # eax<- object head + movl offGlue_cardTable(%ecx),%ecx # card table base + shrl $$GC_CARD_SHIFT,%eax # convert to card num + movb %cl,(%ecx,%eax) # mark card +5: ADVANCE_PC(3) GOTO_NEXT diff --git a/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S new file mode 100644 index 000000000..4576d391d --- /dev/null +++ b/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S @@ -0,0 +1,3 @@ +%verify "executed" +%verify "negative value is sign-extended" +%include "x86/OP_IGET.S" diff --git a/vm/mterp/x86/OP_IGET_VOLATILE.S b/vm/mterp/x86/OP_IGET_VOLATILE.S new file mode 100644 index 000000000..4576d391d --- /dev/null +++ b/vm/mterp/x86/OP_IGET_VOLATILE.S @@ -0,0 +1,3 @@ +%verify "executed" +%verify "negative value is sign-extended" +%include "x86/OP_IGET.S" diff --git a/vm/mterp/x86/OP_IPUT_OBJECT.S b/vm/mterp/x86/OP_IPUT_OBJECT.S index caf007e6e..39c64e712 100644 --- a/vm/mterp/x86/OP_IPUT_OBJECT.S +++ b/vm/mterp/x86/OP_IPUT_OBJECT.S @@ -1,2 +1,70 @@ +%default { "sqnum":"0" } %verify "executed" -%include "x86/OP_IPUT.S" +%verify "null object" +%verify "field already resolved" +%verify "field not yet resolved" +%verify "field cannot be resolved" + /* + * Object field put. + * + * for: iput-object + */ + /* op vA, vB, field@CCCC */ + GET_GLUE(%ecx) + SPILL(rIBASE) # need another reg + movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC + movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex + movzbl rINST_HI,%ecx # ecx<- BA + sarl $$4,%ecx # ecx<- B + movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields + movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA + andb $$0xf,rINST_LO # rINST_FULL<- A + GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr + movl (%eax,rIBASE,4),%eax # resolved entry + testl %eax,%eax # is resolved entry null? + jne .L${opcode}_finish # no, already resolved + movl rIBASE,OUT_ARG1(%esp) + GET_GLUE(rIBASE) + jmp .L${opcode}_resolve +%break + + +.L${opcode}_resolve: + EXPORT_PC() + SPILL(rPC) + movl offGlue_method(rIBASE),rPC # rPC<- current method + UNSPILL(rIBASE) + movl offMethod_clazz(rPC),rPC # rPC<- method->clazz + SPILL_TMP(%ecx) # save object pointer across call + movl rPC,OUT_ARG0(%esp) # pass in method->clazz + call dvmResolveInstField # ... to dvmResolveInstField + UNSPILL_TMP(%ecx) + UNSPILL(rPC) + testl %eax,%eax # ... which returns InstrField ptr + jne .L${opcode}_finish + jmp common_exceptionThrown + +.L${opcode}_finish: + /* + * Currently: + * eax holds resolved field + * ecx holds object + * rIBASE is scratch, but needs to be unspilled + * rINST_FULL holds A + */ + GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A] + movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field + UNSPILL(rIBASE) + testl %ecx,%ecx # object null? + je common_errNullObject # object was null + movl rINST_FULL,(%ecx,%eax) # obj.field <- v[A](8/16/32 bits) + GET_GLUE(%eax) + testl rINST_FULL,rINST_FULL # stored a NULL? + movl offGlue_cardTable(%eax),%eax # get card table base + FETCH_INST_WORD(2) + je 1f # skip card mark if null store + shrl $$GC_CARD_SHIFT,%ecx # object head to card number + movb %al,(%eax,%ecx) # mark card +1: + ADVANCE_PC(2) + GOTO_NEXT diff --git a/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S index a8e851cb2..fb7205b38 100644 --- a/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S +++ b/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S @@ -1,2 +1,28 @@ %verify "executed" -%include "x86/OP_IPUT_QUICK.S" +%verify "null object" + /* For: iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINST_HI,%ecx # ecx<- BA + sarl $$4,%ecx # ecx<- B + GET_VREG(%ecx,%ecx) # vB (object we're operating on) + movzbl rINST_HI,rINST_FULL + andb $$0xf,rINST_LO # rINST_FULL<- A + GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A] + movzwl 2(rPC),%eax # eax<- field byte offset + testl %ecx,%ecx # is object null? + je common_errNullObject + movl rINST_FULL,(%ecx,%eax,1) + GET_GLUE(%eax) + jmp .L${opcode}_finish +%break + +.L${opcode}_finish: + testl rINST_FULL,rINST_FULL # did we store null? + FETCH_INST_WORD(2) + movl offGlue_cardTable(%eax),%eax # get card table base + je 1f # skip card mark if null store + shrl $$GC_CARD_SHIFT,%ecx # object head to card number + movb %al,(%eax,%ecx) # mark card +1: + ADVANCE_PC(2) + GOTO_NEXT diff --git a/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S new file mode 100644 index 000000000..3959c06dd --- /dev/null +++ b/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S @@ -0,0 +1,2 @@ +%verify "executed" +%include "x86/OP_IPUT_OBJECT.S" diff --git a/vm/mterp/x86/OP_IPUT_QUICK.S b/vm/mterp/x86/OP_IPUT_QUICK.S index 60d6fadc4..e62ec00fc 100644 --- a/vm/mterp/x86/OP_IPUT_QUICK.S +++ b/vm/mterp/x86/OP_IPUT_QUICK.S @@ -1,6 +1,6 @@ %verify "executed" %verify "null object" - /* For: iput-quick, iput-object-quick */ + /* For: iput-quick */ /* op vA, vB, offset@CCCC */ movzbl rINST_HI,%ecx # ecx<- BA sarl $$4,%ecx # ecx<- B diff --git a/vm/mterp/x86/OP_IPUT_VOLATILE.S b/vm/mterp/x86/OP_IPUT_VOLATILE.S new file mode 100644 index 000000000..caf007e6e --- /dev/null +++ b/vm/mterp/x86/OP_IPUT_VOLATILE.S @@ -0,0 +1,2 @@ +%verify "executed" +%include "x86/OP_IPUT.S" diff --git a/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S new file mode 100644 index 000000000..3b3a492d1 --- /dev/null +++ b/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S @@ -0,0 +1,2 @@ +%verify "executed" +%include "x86/OP_SGET.S" diff --git a/vm/mterp/x86/OP_SGET_VOLATILE.S b/vm/mterp/x86/OP_SGET_VOLATILE.S new file mode 100644 index 000000000..3b3a492d1 --- /dev/null +++ b/vm/mterp/x86/OP_SGET_VOLATILE.S @@ -0,0 +1,2 @@ +%verify "executed" +%include "x86/OP_SGET.S" diff --git a/vm/mterp/x86/OP_SPUT.S b/vm/mterp/x86/OP_SPUT.S index 952ca8750..440dcfbcf 100644 --- a/vm/mterp/x86/OP_SPUT.S +++ b/vm/mterp/x86/OP_SPUT.S @@ -5,7 +5,7 @@ /* * General 32-bit SPUT handler. * - * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short + * for: sput, sput-boolean, sput-byte, sput-char, sput-short */ /* op vAA, field@BBBB */ GET_GLUE(%ecx) diff --git a/vm/mterp/x86/OP_SPUT_OBJECT.S b/vm/mterp/x86/OP_SPUT_OBJECT.S index ec0489fe3..f55d15110 100644 --- a/vm/mterp/x86/OP_SPUT_OBJECT.S +++ b/vm/mterp/x86/OP_SPUT_OBJECT.S @@ -1,2 +1,49 @@ %verify "executed" -%include "x86/OP_SPUT.S" +%verify "field already resolved" +%verify "field not yet resolved" +%verify "field cannot be resolved" + /* + * SPUT object handler. + */ + /* op vAA, field@BBBB */ + GET_GLUE(%ecx) + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex + movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields + movl (%ecx,%eax,4),%eax # eax<- resolved StaticField + testl %eax,%eax # resolved entry null? + je .L${opcode}_resolve # if not, make it so +.L${opcode}_finish: # field ptr in eax + movzbl rINST_HI,%ecx # ecx<- AA + GET_VREG(%ecx,%ecx) + jmp .L${opcode}_continue +%break + + +.L${opcode}_continue: + movl %ecx,offStaticField_value(%eax) + testl %ecx,%ecx + GET_GLUE(%ecx) + FETCH_INST_WORD(2) + je 1f + movl offGlue_cardTable(%ecx),%ecx # get card table base + shrl $$GC_CARD_SHIFT,%eax # head to card number + movb %cl,(%ecx,%eax) # mark card +1: + ADVANCE_PC(2) + GOTO_NEXT + +.L${opcode}_resolve: + GET_GLUE(%ecx) + movzwl 2(rPC),%eax # eax<- field ref BBBB + movl offGlue_method(%ecx),%ecx # ecx<- current method + EXPORT_PC() # could throw, need to export + movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz + SPILL(rPC) + movl %eax,OUT_ARG1(%esp) + movl %ecx,OUT_ARG0(%esp) + call dvmResolveStaticField # eax<- resolved StaticField ptr + UNSPILL(rPC) + testl %eax,%eax + jne .L${opcode}_finish # success, continue + jmp common_exceptionThrown # no, handle exception diff --git a/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S new file mode 100644 index 000000000..afc666874 --- /dev/null +++ b/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S @@ -0,0 +1,2 @@ +%verify "executed" +%include "x86/OP_SPUT_OBJECT.S" diff --git a/vm/mterp/x86/OP_SPUT_VOLATILE.S b/vm/mterp/x86/OP_SPUT_VOLATILE.S new file mode 100644 index 000000000..ec0489fe3 --- /dev/null +++ b/vm/mterp/x86/OP_SPUT_VOLATILE.S @@ -0,0 +1,2 @@ +%verify "executed" +%include "x86/OP_SPUT.S" diff --git a/vm/native/java_lang_Class.c b/vm/native/java_lang_Class.c index 1bb924176..3c772d9e3 100644 --- a/vm/native/java_lang_Class.c +++ b/vm/native/java_lang_Class.c @@ -320,11 +320,11 @@ static void Dalvik_java_lang_Class_getModifiers(const u4* args, JValue* pResult) } /* - * public String getName() + * private native String getNameNative() * * Return the class' name. */ -static void Dalvik_java_lang_Class_getName(const u4* args, JValue* pResult) +static void Dalvik_java_lang_Class_getNameNative(const u4* args, JValue* pResult) { ClassObject* clazz = (ClassObject*) args[0]; const char* descriptor = clazz->descriptor; @@ -772,8 +772,8 @@ const DalvikNativeMethod dvm_java_lang_Class[] = { Dalvik_java_lang_Class_getInterfaces }, { "getModifiers", "(Ljava/lang/Class;Z)I", Dalvik_java_lang_Class_getModifiers }, - { "getName", "()Ljava/lang/String;", - Dalvik_java_lang_Class_getName }, + { "getNameNative", "()Ljava/lang/String;", + Dalvik_java_lang_Class_getNameNative }, { "getSuperclass", "()Ljava/lang/Class;", Dalvik_java_lang_Class_getSuperclass }, { "isAssignableFrom", "(Ljava/lang/Class;)Z", diff --git a/vm/native/java_lang_System.c b/vm/native/java_lang_System.c index 064d6da04..96cc144a6 100644 --- a/vm/native/java_lang_System.c +++ b/vm/native/java_lang_System.c @@ -71,7 +71,9 @@ static void Dalvik_java_lang_System_arraycopy(const u4* args, JValue* pResult) srcPos > (int) srcArray->length - length || dstPos > (int) dstArray->length - length) { - dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", NULL); + dvmThrowExceptionFmt("Ljava/lang/ArrayIndexOutOfBoundsException;", + "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d", + srcArray->length, srcPos, dstArray->length, dstPos, length); RETURN_VOID(); } diff --git a/vm/oo/Class.c b/vm/oo/Class.c index cbb3d8f80..f10a9c601 100644 --- a/vm/oo/Class.c +++ b/vm/oo/Class.c @@ -23,6 +23,7 @@ #include "Dalvik.h" #include "libdex/DexClass.h" +#include "analysis/Optimize.h" #include <stdlib.h> #include <stddef.h> @@ -1291,8 +1292,11 @@ static ClassObject* findClassFromLoaderNoInit(const char* descriptor, dvmReleaseTrackedAlloc(excep, self); clazz = NULL; goto bail; - } else { - assert(clazz != NULL); + } else if (clazz == NULL) { + LOGW("ClassLoader returned NULL w/o exception pending\n"); + dvmThrowException("Ljava/lang/NullPointerException;", + "ClassLoader returned null"); + goto bail; } dvmAddInitiatingLoader(clazz, loader); @@ -1955,11 +1959,9 @@ void dvmFreeClassInnards(ClassObject* clazz) } while (0) /* arrays just point at Object's vtable; don't free vtable in this case. - * dvmIsArrayClass() checks clazz->descriptor, so we have to do this check - * before freeing the name. */ clazz->vtableCount = -1; - if (dvmIsArrayClass(clazz)) { + if (clazz->vtable == gDvm.classJavaLangObject->vtable) { clazz->vtable = NULL; } else { NULL_AND_LINEAR_FREE(clazz->vtable); @@ -2370,6 +2372,11 @@ static bool precacheReferenceOffsets(ClassObject* clazz) "queueNext", "Ljava/lang/ref/Reference;"); assert(gDvm.offJavaLangRefReference_queueNext >= 0); + gDvm.offJavaLangRefReference_pendingNext = + dvmFindFieldOffset(gDvm.classJavaLangRefReference, + "pendingNext", "Ljava/lang/ref/Reference;"); + assert(gDvm.offJavaLangRefReference_pendingNext >= 0); + /* enqueueInternal() is private and thus a direct method. */ meth = dvmFindDirectMethodByDescriptor(clazz, "enqueueInternal", "()Z"); assert(meth != NULL); @@ -4284,9 +4291,9 @@ bool dvmInitClass(ClassObject* clazz) verify_failed: dvmThrowExceptionWithClassMessage("Ljava/lang/VerifyError;", clazz->descriptor); - dvmSetFieldObject((Object *)clazz, - offsetof(ClassObject, verifyErrorClass), - (Object *)dvmGetException(self)->clazz); + dvmSetFieldObject((Object*) clazz, + offsetof(ClassObject, verifyErrorClass), + (Object*) dvmGetException(self)->clazz); clazz->status = CLASS_ERROR; goto bail_unlock; } @@ -4295,8 +4302,20 @@ verify_failed: } noverify: + /* + * We need to ensure that certain instructions, notably accesses to + * volatile fields, are replaced before any code is executed. This + * must happen even if DEX optimizations are disabled. + */ + if (!IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED)) { + LOGV("+++ late optimize on %s (pv=%d)\n", + clazz->descriptor, IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED)); + dvmOptimizeClass(clazz, true); + SET_CLASS_FLAG(clazz, CLASS_ISOPTIMIZED); + } + #ifdef WITH_DEBUGGER - /* update instruction stream now that the verifier is done */ + /* update instruction stream now that verification + optimization is done */ dvmFlushBreakpoints(clazz); #endif @@ -4459,7 +4478,7 @@ noverify: * need to throw an ExceptionInInitializerError, but we want to * tuck the original exception into the "cause" field. */ - LOGW("Exception %s thrown during %s.<clinit>\n", + LOGW("Exception %s thrown while initializing %s\n", (dvmGetException(self)->clazz)->descriptor, clazz->descriptor); throwClinitError(); //LOGW("+++ replaced\n"); @@ -4508,19 +4527,47 @@ bail_unlock: /* * Replace method->nativeFunc and method->insns with new values. This is - * performed on resolution of a native method. + * commonly performed after successful resolution of a native method. + * + * There are three basic states: + * (1) (initial) nativeFunc = dvmResolveNativeMethod, insns = NULL + * (2) (internal native) nativeFunc = <impl>, insns = NULL + * (3) (JNI) nativeFunc = JNI call bridge, insns = <impl> + * + * nativeFunc must never be NULL for a native method. + * + * The most common transitions are (1)->(2) and (1)->(3). The former is + * atomic, since only one field is updated; the latter is not, but since + * dvmResolveNativeMethod ignores the "insns" field we just need to make + * sure the update happens in the correct order. + * + * A transition from (2)->(1) would work fine, but (3)->(1) will not, + * because both fields change. If we did this while a thread was executing + * in the call bridge, we could null out the "insns" field right before + * the bridge tried to call through it. So, once "insns" is set, we do + * not allow it to be cleared. A NULL value for the "insns" argument is + * treated as "do not change existing value". */ -void dvmSetNativeFunc(const Method* method, DalvikBridgeFunc func, +void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func, const u2* insns) { ClassObject* clazz = method->clazz; + assert(func != NULL); + /* just open up both; easier that way */ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods); dvmLinearReadWrite(clazz->classLoader, clazz->directMethods); - ((Method*)method)->nativeFunc = func; - ((Method*)method)->insns = insns; + if (insns != NULL) { + /* update both, ensuring that "insns" is observed first */ + method->insns = insns; + android_atomic_release_store((int32_t) func, + (void*) &method->nativeFunc); + } else { + /* only update nativeFunc */ + method->nativeFunc = func; + } dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods); dvmLinearReadOnly(clazz->classLoader, clazz->directMethods); diff --git a/vm/oo/Class.h b/vm/oo/Class.h index 7457a60eb..e27ef79bc 100644 --- a/vm/oo/Class.h +++ b/vm/oo/Class.h @@ -139,10 +139,10 @@ void dvmAddInitiatingLoader(ClassObject* clazz, Object* loader); bool dvmLoaderInInitiatingList(const ClassObject* clazz, const Object* loader); /* - * Update method's "nativeFunc" and "insns" after native method resolution. + * Update method's "nativeFunc" and "insns". If "insns" is NULL, the + * current method->insns value is not changed. */ -void dvmSetNativeFunc(const Method* method, DalvikBridgeFunc func, - const u2* insns); +void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func, const u2* insns); /* * Set the method's "registerMap" field. diff --git a/vm/oo/ObjectInlines.h b/vm/oo/ObjectInlines.h index 11920591d..d26183b15 100644 --- a/vm/oo/ObjectInlines.h +++ b/vm/oo/ObjectInlines.h @@ -21,12 +21,15 @@ #define _DALVIK_OO_OBJECTINLINES /* - * Store a single value in the array, and note in the write barrier. + * Store a single value in the array, and if the value isn't null, + * note in the write barrier. */ INLINE void dvmSetObjectArrayElement(const ArrayObject* obj, int index, Object* val) { ((Object **)(obj)->contents)[index] = val; - dvmWriteBarrierArray(obj, index, index+1); + if (val != NULL) { + dvmWriteBarrierArray(obj, index, index + 1); + } } @@ -38,6 +41,9 @@ INLINE void dvmSetObjectArrayElement(const ArrayObject* obj, int index, * * The VM treats all fields as 32 or 64 bits, so the field set functions * write 32 bits even if the underlying type is smaller. + * + * Setting Object types to non-null values includes a call to the + * write barrier. */ #define BYTE_OFFSET(_ptr, _offset) ((void*) (((u1*)(_ptr)) + (_offset))) @@ -114,7 +120,9 @@ INLINE void dvmSetFieldDouble(Object* obj, int offset, double val) { INLINE void dvmSetFieldObject(Object* obj, int offset, Object* val) { JValue* lhs = BYTE_OFFSET(obj, offset); lhs->l = val; - dvmWriteBarrierField(obj, &lhs->l); + if (val != NULL) { + dvmWriteBarrierField(obj, &lhs->l); + } } INLINE void dvmSetFieldIntVolatile(Object* obj, int offset, s4 val) { s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i; @@ -123,7 +131,9 @@ INLINE void dvmSetFieldIntVolatile(Object* obj, int offset, s4 val) { INLINE void dvmSetFieldObjectVolatile(Object* obj, int offset, Object* val) { void** ptr = &((JValue*)BYTE_OFFSET(obj, offset))->l; android_atomic_release_store((int32_t)val, (int32_t*)ptr); - dvmWriteBarrierField(obj, ptr); + if (val != NULL) { + dvmWriteBarrierField(obj, ptr); + } } INLINE void dvmSetFieldLongVolatile(Object* obj, int offset, s8 val) { s8* addr = BYTE_OFFSET(obj, offset); @@ -206,7 +216,9 @@ INLINE void dvmSetStaticFieldDouble(StaticField* sfield, double val) { } INLINE void dvmSetStaticFieldObject(StaticField* sfield, Object* val) { sfield->value.l = val; - dvmWriteBarrierField((Object *)sfield->field.clazz, &sfield->value.l); + if (val != NULL) { + dvmWriteBarrierField((Object *)sfield->field.clazz, &sfield->value.l); + } } INLINE void dvmSetStaticFieldIntVolatile(StaticField* sfield, s4 val) { s4* ptr = &(sfield->value.i); @@ -215,7 +227,9 @@ INLINE void dvmSetStaticFieldIntVolatile(StaticField* sfield, s4 val) { INLINE void dvmSetStaticFieldObjectVolatile(StaticField* sfield, Object* val) { void** ptr = &(sfield->value.l); android_atomic_release_store((int32_t)val, (int32_t*)ptr); - dvmWriteBarrierField((Object *)sfield->field.clazz, &sfield->value.l); + if (val != NULL) { + dvmWriteBarrierField((Object *)sfield->field.clazz, &sfield->value.l); + } } INLINE void dvmSetStaticFieldLongVolatile(StaticField* sfield, s8 val) { s8* addr = &sfield->value.j; |
