summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/embedded-vm-control.html17
-rw-r--r--tests/086-null-super/expected.txt1
-rw-r--r--tests/086-null-super/info.txt7
-rw-r--r--tests/086-null-super/src/Main.java165
-rw-r--r--tests/087-gc-after-link/expected.txt2
-rw-r--r--tests/087-gc-after-link/info.txt8
-rw-r--r--tests/087-gc-after-link/src/Main.java167
-rw-r--r--vm/CheckJni.c66
-rw-r--r--vm/Common.h2
-rw-r--r--vm/Dalvik.h1
-rw-r--r--vm/Debugger.c14
-rw-r--r--vm/Debugger.h1
-rw-r--r--vm/Dvm.mk2
-rw-r--r--vm/DvmDex.c28
-rw-r--r--vm/DvmDex.h3
-rw-r--r--vm/Exception.c2
-rw-r--r--vm/Globals.h20
-rw-r--r--vm/Init.c20
-rw-r--r--vm/Intern.c120
-rw-r--r--vm/Jni.c132
-rw-r--r--vm/Misc.c29
-rw-r--r--vm/Misc.h19
-rw-r--r--vm/Native.c185
-rw-r--r--vm/Native.h13
-rw-r--r--vm/StdioConverter.c2
-rw-r--r--vm/Sync.c28
-rw-r--r--vm/Thread.c7
-rw-r--r--vm/alloc/CardTable.c142
-rw-r--r--vm/alloc/CardTable.h76
-rw-r--r--vm/alloc/Copying.c10
-rw-r--r--vm/alloc/Heap.c156
-rw-r--r--vm/alloc/Heap.h6
-rw-r--r--vm/alloc/HeapBitmap.c51
-rw-r--r--vm/alloc/HeapBitmap.h23
-rw-r--r--vm/alloc/HeapInternal.h9
-rw-r--r--vm/alloc/HeapSource.c101
-rw-r--r--vm/alloc/HeapSource.h5
-rw-r--r--vm/alloc/MarkSweep.c435
-rw-r--r--vm/alloc/MarkSweep.h4
-rw-r--r--vm/alloc/Verify.c201
-rw-r--r--vm/alloc/Visit.c229
-rw-r--r--vm/alloc/Visit.h14
-rw-r--r--vm/alloc/VisitInlines.h180
-rw-r--r--vm/alloc/WriteBarrier.h14
-rw-r--r--vm/analysis/CodeVerify.c75
-rw-r--r--vm/analysis/DexPrepare.c2
-rw-r--r--vm/analysis/Optimize.c321
-rw-r--r--vm/analysis/Optimize.h2
-rw-r--r--vm/arch/x86-atom/Call386ABI.S2
-rw-r--r--vm/compiler/CompilerUtility.h2
-rw-r--r--vm/compiler/Frontend.c5
-rw-r--r--vm/compiler/codegen/CompilerCodegen.h3
-rw-r--r--vm/compiler/codegen/arm/ArchUtility.c27
-rw-r--r--vm/compiler/codegen/arm/ArmLIR.h11
-rw-r--r--vm/compiler/codegen/arm/Assemble.c5
-rw-r--r--vm/compiler/codegen/arm/CodegenDriver.c107
-rw-r--r--vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c7
-rw-r--r--vm/compiler/codegen/arm/armv5te/ArchVariant.c7
-rw-r--r--vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c8
-rw-r--r--vm/compiler/codegen/arm/armv7-a/ArchVariant.c8
-rw-r--r--vm/interp/Interp.c4
-rw-r--r--vm/interp/InterpDefs.h6
-rw-r--r--vm/jdwp/JdwpHandler.c26
-rw-r--r--vm/mterp/Mterp.c1
-rw-r--r--vm/mterp/armv5te/OP_APUT_OBJECT.S7
-rw-r--r--vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S20
-rw-r--r--vm/mterp/armv5te/OP_IPUT.S2
-rw-r--r--vm/mterp/armv5te/OP_IPUT_OBJECT.S51
-rw-r--r--vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S19
-rw-r--r--vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_IPUT_QUICK.S2
-rw-r--r--vm/mterp/armv5te/OP_SPUT.S2
-rw-r--r--vm/mterp/armv5te/OP_SPUT_OBJECT.S38
-rw-r--r--vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/c/gotoTargets.c22
-rw-r--r--vm/mterp/common/asm-constants.h54
-rw-r--r--vm/mterp/config-x868
-rw-r--r--vm/mterp/out/InterpAsm-armv4t.S200
-rw-r--r--vm/mterp/out/InterpAsm-armv5te-vfp.S200
-rw-r--r--vm/mterp/out/InterpAsm-armv5te.S200
-rw-r--r--vm/mterp/out/InterpAsm-armv7-a-neon.S196
-rw-r--r--vm/mterp/out/InterpAsm-armv7-a.S196
-rw-r--r--vm/mterp/out/InterpAsm-x86.S622
-rw-r--r--vm/mterp/out/InterpC-allstubs.c22
-rw-r--r--vm/mterp/out/InterpC-portdbg.c22
-rw-r--r--vm/mterp/out/InterpC-portstd.c22
-rw-r--r--vm/mterp/out/InterpC-x86-atom.c22
-rw-r--r--vm/mterp/out/InterpC-x86.c54
-rw-r--r--vm/mterp/x86-atom/TODO.txt1
-rw-r--r--vm/mterp/x86/OP_APUT_OBJECT.S8
-rw-r--r--vm/mterp/x86/OP_FILLED_NEW_ARRAY.S13
-rw-r--r--vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S3
-rw-r--r--vm/mterp/x86/OP_IGET_VOLATILE.S3
-rw-r--r--vm/mterp/x86/OP_IPUT_OBJECT.S70
-rw-r--r--vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S28
-rw-r--r--vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_IPUT_QUICK.S2
-rw-r--r--vm/mterp/x86/OP_IPUT_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_SGET_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_SPUT.S2
-rw-r--r--vm/mterp/x86/OP_SPUT_OBJECT.S49
-rw-r--r--vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_SPUT_VOLATILE.S2
-rw-r--r--vm/native/java_lang_Class.c8
-rw-r--r--vm/native/java_lang_System.c4
-rw-r--r--vm/oo/Class.c75
-rw-r--r--vm/oo/Class.h6
-rw-r--r--vm/oo/ObjectInlines.h26
109 files changed, 4002 insertions, 1639 deletions
diff --git a/docs/embedded-vm-control.html b/docs/embedded-vm-control.html
index e3797f33f..ec2b69462 100644
--- a/docs/embedded-vm-control.html
+++ b/docs/embedded-vm-control.html
@@ -16,6 +16,7 @@
<li><a href="#dp">Deadlock Prediction</a>
<li><a href="#stackdump">Stack Dumps</a>
<li><a href="#dexcheck">DEX File Checksums</a>
+ <li><a href="#general">General Flags</a>
</ul>
<h2><a name="introduction">Introduction (read this first!)</a></h2>
@@ -288,6 +289,22 @@ problems it may be useful to add the property to
to check for corruption in a large set of files.
+<h2><a name="general">General Flags</a></h2>
+
+<p>In the "Honeycomb" release, a general mechanism for passing flags to
+the VM was introduced:
+
+<pre>adb shell setprop dalvik.vm.extra-opts "flag1 flag2 ... flagN"</pre>
+
+<p>The flags are separated by spaces. You can specify as many as you want
+so long as they all fit within the system property value length limit
+(currently 92 characters).
+
+<p>The extra-opts flags will be added at the end of the command line,
+which means they will override earlier settings. This can be used, for
+example, to experiment with different values for <code>-Xmx</code> even
+though the Android framework is setting it explicitly.
+
<address>Copyright &copy; 2008 The Android Open Source Project</address>
</body></html>
diff --git a/tests/086-null-super/expected.txt b/tests/086-null-super/expected.txt
new file mode 100644
index 000000000..20c6796db
--- /dev/null
+++ b/tests/086-null-super/expected.txt
@@ -0,0 +1 @@
+Got expected ITE/NPE
diff --git a/tests/086-null-super/info.txt b/tests/086-null-super/info.txt
new file mode 100644
index 000000000..f983bd0dd
--- /dev/null
+++ b/tests/086-null-super/info.txt
@@ -0,0 +1,7 @@
+ClassLoader.loadClass() is expected to throw an exception, usually
+ClassNotFound, if it can't find the given Class, and not return null.
+
+This is a regression test for a defect in Dalvik, which was assuming
+that if there was no exception, the value returned would be non-null.
+
+This test is not expected to work for the reference implementation.
diff --git a/tests/086-null-super/src/Main.java b/tests/086-null-super/src/Main.java
new file mode 100644
index 000000000..6decb20cc
--- /dev/null
+++ b/tests/086-null-super/src/Main.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Class loader test.
+ */
+public class Main {
+ /**
+ * Thrown when an unexpected Exception is caught internally.
+ */
+ static class TestFailed extends Exception {
+ public TestFailed(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ /**
+ * A class loader which loads classes from the dex file
+ * "test.jar". However, it will return null when asked to load the
+ * class InaccessibleSuper.
+ *
+ * When testing code calls BrokenDexLoader's findBrokenClass(),
+ * a BrokenDexLoader will be the defining loader for the class
+ * Inaccessible. The VM will call the defining loader for
+ * "InaccessibleSuper", which will return null, which the VM
+ * should be able to deal with gracefully.
+ *
+ * Note that this depends heavily on the Dalvik test harness.
+ */
+ static class BrokenDexLoader extends ClassLoader {
+
+ /** We return null when asked to load InaccessibleSuper. */
+ private static class InaccessibleSuper {}
+ private static class Inaccessible extends InaccessibleSuper {}
+
+ private static final String SUPERCLASS_NAME =
+ "Main$BrokenDexLoader$InaccessibleSuper";
+ private static final String CLASS_NAME =
+ "Main$BrokenDexLoader$Inaccessible";
+
+ private static final String DEX_FILE = "test.jar";
+
+ public BrokenDexLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ /**
+ * Finds the class with the specified binary name, from DEX_FILE.
+ *
+ * If we don't find a match, we throw an exception.
+ */
+ private Class<?> findDexClass(String name)
+ throws TestFailed, InvocationTargetException
+ {
+
+ try {
+ /*
+ * Find the DexFile class, and construct a DexFile object
+ * through reflection, then call loadCLass on it.
+ */
+ Class mDexClass = ClassLoader.getSystemClassLoader().
+ loadClass("dalvik/system/DexFile");
+ Constructor ctor = mDexClass.
+ getConstructor(new Class[] {String.class});
+ Object mDexFile = ctor.newInstance(DEX_FILE);
+ Method meth = mDexClass.
+ getMethod("loadClass",
+ new Class[] { String.class, ClassLoader.class });
+ /*
+ * Invoking loadClass on CLASS_NAME is expected to
+ * throw an InvocationTargetException. Anything else
+ * is an error we can't recover from.
+ */
+ meth.invoke(mDexFile, name, this);
+ } catch (NoSuchMethodException nsme) {
+ throw new TestFailed(nsme);
+ } catch (InstantiationException ie) {
+ throw new TestFailed(ie);
+ } catch (IllegalAccessException iae) {
+ throw new TestFailed(iae);
+ } catch (ClassNotFoundException cnfe) {
+ throw new TestFailed(cnfe);
+ }
+
+ return null;
+ }
+
+ /**
+ * Load a class.
+ *
+ * Return null if the class's name is SUPERCLASS_NAME;
+ * otherwise invoke the super's loadClass method.
+ */
+ public Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ if (SUPERCLASS_NAME.equals(name)) {
+ return null;
+ }
+
+ return super.loadClass(name, resolve);
+ }
+
+ /**
+ * Attempt to find the class with the superclass we refuse to
+ * load. This is expected to throw an
+ * InvocationTargetException, with a NullPointerException as
+ * its cause.
+ */
+ public void findBrokenClass()
+ throws TestFailed, InvocationTargetException
+ {
+ findDexClass(CLASS_NAME);
+ }
+ }
+
+ /**
+ * Main entry point.
+ */
+ public static void main(String[] args)
+ throws TestFailed, ClassNotFoundException {
+ /*
+ * Run test.
+ */
+ testFailLoadAndGc();
+ }
+
+ /**
+ * See if we can GC after a failed load.
+ */
+ static void testFailLoadAndGc() throws TestFailed {
+ try {
+ BrokenDexLoader loader;
+
+ loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader());
+ loader.findBrokenClass();
+ System.err.println("ERROR: Inaccessible was accessible");
+ } catch (InvocationTargetException ite) {
+ Throwable cause = ite.getCause();
+ if (cause instanceof NullPointerException) {
+ System.err.println("Got expected ITE/NPE");
+ } else {
+ System.err.println("Got unexpected ITE");
+ ite.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/tests/087-gc-after-link/expected.txt b/tests/087-gc-after-link/expected.txt
new file mode 100644
index 000000000..3b2d33a6b
--- /dev/null
+++ b/tests/087-gc-after-link/expected.txt
@@ -0,0 +1,2 @@
+Got expected ITE/NPE
+GC complete.
diff --git a/tests/087-gc-after-link/info.txt b/tests/087-gc-after-link/info.txt
new file mode 100644
index 000000000..948383815
--- /dev/null
+++ b/tests/087-gc-after-link/info.txt
@@ -0,0 +1,8 @@
+This test causes a linkage error, which calls dvmFreeClassInnards on
+the unlinked Class.
+
+This is a regression test for a defect in Dalvik, which was assuming
+that dvmFreeClassInnards could be called twice on the same class.
+
+This test is a modified version of test 086.
+This test is not expected to work for the reference implementation.
diff --git a/tests/087-gc-after-link/src/Main.java b/tests/087-gc-after-link/src/Main.java
new file mode 100644
index 000000000..24adaf708
--- /dev/null
+++ b/tests/087-gc-after-link/src/Main.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Class loader test.
+ */
+public class Main {
+ /**
+ * Thrown when an unexpected Exception is caught internally.
+ */
+ static class TestFailed extends Exception {
+ public TestFailed(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ /**
+ * A class loader which loads classes from the dex file
+ * "test.jar". However, it will return null when asked to load the
+ * class InaccessibleSuper.
+ *
+ * When testing code calls BrokenDexLoader's findBrokenClass(),
+ * a BrokenDexLoader will be the defining loader for the class
+ * Inaccessible. The VM will call the defining loader for
+ * "InaccessibleSuper", which will return null, which the VM
+ * should be able to deal with gracefully.
+ *
+ * Note that this depends heavily on the Dalvik test harness.
+ */
+ static class BrokenDexLoader extends ClassLoader {
+
+ /** We return null when asked to load InaccessibleSuper. */
+ private static class InaccessibleSuper {}
+ private static class Inaccessible extends InaccessibleSuper {}
+
+ private static final String SUPERCLASS_NAME =
+ "Main$BrokenDexLoader$InaccessibleSuper";
+ private static final String CLASS_NAME =
+ "Main$BrokenDexLoader$Inaccessible";
+
+ private static final String DEX_FILE = "test.jar";
+
+ public BrokenDexLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ /**
+ * Finds the class with the specified binary name, from DEX_FILE.
+ *
+ * If we don't find a match, we throw an exception.
+ */
+ private Class<?> findDexClass(String name)
+ throws TestFailed, InvocationTargetException
+ {
+
+ try {
+ /*
+ * Find the DexFile class, and construct a DexFile object
+ * through reflection, then call loadCLass on it.
+ */
+ Class mDexClass = ClassLoader.getSystemClassLoader().
+ loadClass("dalvik/system/DexFile");
+ Constructor ctor = mDexClass.
+ getConstructor(new Class[] {String.class});
+ Object mDexFile = ctor.newInstance(DEX_FILE);
+ Method meth = mDexClass.
+ getMethod("loadClass",
+ new Class[] { String.class, ClassLoader.class });
+ /*
+ * Invoking loadClass on CLASS_NAME is expected to
+ * throw an InvocationTargetException. Anything else
+ * is an error we can't recover from.
+ */
+ meth.invoke(mDexFile, name, this);
+ } catch (NoSuchMethodException nsme) {
+ throw new TestFailed(nsme);
+ } catch (InstantiationException ie) {
+ throw new TestFailed(ie);
+ } catch (IllegalAccessException iae) {
+ throw new TestFailed(iae);
+ } catch (ClassNotFoundException cnfe) {
+ throw new TestFailed(cnfe);
+ }
+
+ return null;
+ }
+
+ /**
+ * Load a class.
+ *
+ * Return null if the class's name is SUPERCLASS_NAME;
+ * otherwise invoke the super's loadClass method.
+ */
+ public Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ if (SUPERCLASS_NAME.equals(name)) {
+ return null;
+ }
+
+ return super.loadClass(name, resolve);
+ }
+
+ /**
+ * Attempt to find the class with the superclass we refuse to
+ * load. This is expected to throw an
+ * InvocationTargetException, with a NullPointerException as
+ * its cause.
+ */
+ public void findBrokenClass()
+ throws TestFailed, InvocationTargetException
+ {
+ findDexClass(CLASS_NAME);
+ }
+ }
+
+ /**
+ * Main entry point.
+ */
+ public static void main(String[] args)
+ throws TestFailed, ClassNotFoundException {
+ /*
+ * Run test.
+ */
+ testFailLoadAndGc();
+ }
+
+ /**
+ * See if we can GC after a failed load.
+ */
+ static void testFailLoadAndGc() throws TestFailed {
+ try {
+ BrokenDexLoader loader;
+
+ loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader());
+ loader.findBrokenClass();
+ System.err.println("ERROR: Inaccessible was accessible");
+ } catch (InvocationTargetException ite) {
+ Throwable cause = ite.getCause();
+ if (cause instanceof NullPointerException) {
+ System.err.println("Got expected ITE/NPE");
+ } else {
+ System.err.println("Got unexpected ITE");
+ ite.printStackTrace();
+ }
+ }
+ System.gc();
+ System.out.println("GC complete.");
+ }
+}
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);
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
index 3214e3196..ba580d05b 100644
--- a/vm/Dvm.mk
+++ b/vm/Dvm.mk
@@ -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.
*/
diff --git a/vm/Init.c b/vm/Init.c
index e29687d44..d73bec789 100644
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -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);
}
}
diff --git a/vm/Jni.c b/vm/Jni.c
index 99735087d..77aeb69d4 100644
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -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;
}
/*
diff --git a/vm/Misc.c b/vm/Misc.c
index 21b7fcef6..2a1e2444f 100644
--- a/vm/Misc.c
+++ b/vm/Misc.c
@@ -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;
+}
diff --git a/vm/Misc.h b/vm/Misc.h
index 3e04f84de..358a97bfd 100644
--- a/vm/Misc.h
+++ b/vm/Misc.h
@@ -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
}
/*
diff --git a/vm/Sync.c b/vm/Sync.c
index 1f0b2c2b9..2223c4838 100644
--- a/vm/Sync.c
+++ b/vm/Sync.c
@@ -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;