OSDN Git Service

Make more DEX optimizations "essential"
authorAndy McFadden <fadden@android.com>
Fri, 28 Jan 2011 01:01:54 +0000 (17:01 -0800)
committerAndy McFadden <fadden@android.com>
Fri, 28 Jan 2011 23:40:49 +0000 (15:40 -0800)
This shifts two dexopt optimizations from the "non-essential" category
to "essential", which means they will be performed at class load time
for classes that did not successfully verify in dexopt.  (This has an
impact on memory and start time, but measurements have indicated that
it's negligible because dexopt usually succeeds.)

First, invoke-direct --> invoke-direct-empty.  This is part of the
work needed for bug 3342343, which needs to do a little extra work
when returning from Object.<init> in a finalizable class.

Second, invoke-* --> execute-inline.  We currently have three copies
of methods like String.length(): one in libcore, one in InlineNatives.c,
and one in the JIT's code generator.  If we guarantee inlining, we can
get rid of the copy in libcore.  We also ensure that certain libcore
tests (which are organized in a way that makes dexopt unhappy) are
using the version that will most likely be used on production.

Note there is currently no support for "jumbo" opcodes here.

Also, made the inline method lookup abort-on-failure.  Once upon a time
these were "best effort" optimizations, but now they're mandatory.  And
seriously, if you don't have String.length() and Math.min() you
shouldn't be trying to run anyway.  dvmInlineNativeCheck() is now
redundant and has been removed.

Change-Id: I4244e011839f77311fea0570195b3b0df4d84dcf

vm/Init.c
vm/InlineNative.c
vm/InlineNative.h
vm/analysis/DexPrepare.c
vm/analysis/DexPrepare.h
vm/analysis/Optimize.c
vm/analysis/Optimize.h

index 15c812c..7bbd467 100644 (file)
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -1285,6 +1285,13 @@ int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized,
     }
 
     /*
+     * Create a table of methods for which we will substitute an "inline"
+     * version for performance.
+     */
+    if (!dvmCreateInlineSubsTable())
+        goto fail;
+
+    /*
      * Miscellaneous class library validation.
      */
     if (!dvmValidateBoxClasses())
@@ -1343,9 +1350,6 @@ int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized,
     if (!dvmDebuggerStartup())
         goto fail;
 
-    if (!dvmInlineNativeCheck())
-        goto fail;
-
     /*
      * Init for either zygote mode or non-zygote mode.  The key difference
      * is that we don't start any additional threads in Zygote mode.
@@ -1702,6 +1706,8 @@ void dvmShutdown(void)
     dvmNativeShutdown();
     dvmInternalNativeShutdown();
 
+    dvmFreeInlineSubsTable();
+
     free(gDvm.bootClassPathStr);
     free(gDvm.classPathStr);
 
index c426d4d..d828393 100644 (file)
@@ -898,17 +898,3 @@ bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
     TRACE_METHOD_EXIT(self, method);
     return result;
 }
-
-/*
- * Check that we can resolve every inline native.
- */
-bool dvmInlineNativeCheck(void)
-{
-    int op;
-    for (op = 0; op < NELEM(gDvmInlineOpsTable); ++op) {
-        if (resolveInlineNative(op) == NULL) {
-            dvmAbort();
-        }
-    }
-    return true;
-}
index cb31f51..fc86027 100644 (file)
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 /*
  * Inlined native functions.
  */
@@ -21,7 +22,6 @@
 
 /* startup/shutdown */
 bool dvmInlineNativeStartup(void);
-bool dvmInlineNativeCheck(void);
 void dvmInlineNativeShutdown(void);
 
 Method* dvmFindInlinableMethod(const char* classDescriptor,
index fee31e5..8f96bc5 100644 (file)
@@ -758,6 +758,16 @@ static bool rewriteDex(u1* addr, int len, u4* pHeaderFlags,
     loadWhen = dvmGetRelativeTimeUsec();
 
     /*
+     * Create a data structure for use by the bytecode optimizer.
+     * We need to look up methods in a few classes, so this may cause
+     * a bit of class loading.  We usually do this during VM init, but
+     * for dexopt on core.jar the order of operations gets a bit tricky,
+     * so we defer it to here.
+     */
+    if (!dvmCreateInlineSubsTable())
+        goto bail;
+
+    /*
      * Verify and optimize all classes in the DEX file (command-line
      * options permitting).
      *
@@ -874,21 +884,6 @@ static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
     u4 count = pDexFile->pHeader->classDefsSize;
     u4 idx;
 
-    /*
-     * Create a data structure for use by the bytecode optimizer.  We
-     * stuff it into a global so we don't have to pass it around as
-     * a function argument.
-     *
-     * We could create this at VM startup, but there's no need to do so
-     * unless we're optimizing, which means we're in dexopt, and we're
-     * only going to call here once.
-     */
-    if (doOpt) {
-        gDvm.inlineSubs = dvmCreateInlineSubsTable();
-        if (gDvm.inlineSubs == NULL)
-            return;
-    }
-
     for (idx = 0; idx < count; idx++) {
         const DexClassDef* pClassDef;
         const char* classDescriptor;
@@ -909,11 +904,6 @@ static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
         }
     }
 
-    if (gDvm.inlineSubs != NULL) {
-        dvmFreeInlineSubsTable(gDvm.inlineSubs);
-        gDvm.inlineSubs = NULL;
-    }
-
 #ifdef VERIFIER_STATS
     LOGI("Verifier stats:\n");
     LOGI(" methods examined        : %u\n", gDvm.verifierStats.methodsExamined);
index bfa5fb5..f20aca5 100644 (file)
@@ -119,4 +119,10 @@ bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLen,
 bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
     const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
 
+/*
+ * Prep data structures.
+ */
+bool dvmCreateInlineSubsTable(void);
+void dvmFreeInlineSubsTable(void);
+
 #endif /*_DALVIK_DEXPREPARE*/
index dabc987..abcf990 100644 (file)
@@ -51,51 +51,59 @@ static bool needsReturnBarrier(Method* method);
 
 
 /*
- * Create a table of inline substitutions.
+ * Create a table of inline substitutions.  Sets gDvm.inlineSubs.
  *
  * TODO: this is currently just a linear array.  We will want to put this
  * into a hash table as the list size increases.
  */
-InlineSub* dvmCreateInlineSubsTable(void)
+bool dvmCreateInlineSubsTable(void)
 {
     const InlineOperation* ops = dvmGetInlineOpsTable();
     const int count = dvmGetInlineOpsTableLength();
     InlineSub* table;
     int i, tableIndex;
 
+    assert(gDvm.inlineSubs == NULL);
+
     /*
-     * Allocate for optimism: one slot per entry, plus an end-of-list marker.
+     * One slot per entry, plus an end-of-list marker.
      */
-    table = (InlineSub*)calloc(count + 1, sizeof(InlineSub));
+    table = (InlineSub*) calloc(count + 1, sizeof(InlineSub));
 
     tableIndex = 0;
     for (i = 0; i < count; i++) {
         Method* method = dvmFindInlinableMethod(ops[i].classDescriptor,
             ops[i].methodName, ops[i].methodSignature);
-        if (method != NULL) {
-            table[tableIndex].method = method;
-            table[tableIndex].inlineIdx = i;
-            tableIndex++;
-
-            LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
+        if (method == NULL) {
+            /*
+             * Not expected.  We only use this for key methods in core
+             * classes, so we should always be able to find them.
+             */
+            LOGE("Unable to find method for inlining: %s.%s:%s\n",
                 ops[i].classDescriptor, ops[i].methodName,
                 ops[i].methodSignature);
+            return false;
         }
+
+        table[tableIndex].method = method;
+        table[tableIndex].inlineIdx = i;
+        tableIndex++;
     }
 
     /* mark end of table */
     table[tableIndex].method = NULL;
-    LOGV("DexOpt: inline table has %d entries\n", tableIndex);
 
-    return table;
+    gDvm.inlineSubs = table;
+    return true;
 }
 
 /*
  * Release inline sub data structure.
  */
-void dvmFreeInlineSubsTable(InlineSub* inlineSubs)
+void dvmFreeInlineSubsTable(void)
 {
-    free(inlineSubs);
+    free(gDvm.inlineSubs);
+    gDvm.inlineSubs = NULL;
 }
 
 
@@ -161,7 +169,22 @@ static void optimizeMethod(Method* method, bool essentialOnly)
 
         inst = *insns & 0xff;
 
-        /* "essential" substitutions, always checked */
+        /*
+         * essential substitutions:
+         *  {iget,iput,sget,sput}-wide --> *-wide-volatile
+         *  invoke-{virtual,direct,static}[/range] --> execute-inline
+         *  invoke-direct --> invoke-direct-empty
+         *
+         * essential-on-SMP substitutions:
+         *  iget-* --> iget-*-volatile
+         *  iput-* --> iput-*-volatile
+         *
+         * non-essential substitutions:
+         *  iget-* --> iget-*-quick
+         *  iput-* --> iput-*-quick
+         *
+         * TODO: might be time to merge this with the other two switches
+         */
         switch (inst) {
         case OP_IGET:
         case OP_IGET_BOOLEAN:
@@ -200,7 +223,7 @@ static void optimizeMethod(Method* method, bool essentialOnly)
                 volatileOpc = OP_IPUT_OBJECT_VOLATILE;
 rewrite_inst_field:
             if (essentialOnly)
-                quickOpc = OP_NOP;
+                quickOpc = OP_NOP;      /* if not essential, no "-quick" sub */
             if (quickOpc != OP_NOP || volatileOpc != OP_NOP)
                 rewriteInstField(method, insns, quickOpc, volatileOpc);
             break;
@@ -213,13 +236,45 @@ rewrite_inst_field:
 rewrite_static_field:
             rewriteStaticField(method, insns, volatileOpc);
             break;
+
+        case OP_INVOKE_VIRTUAL:
+            if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
+                /* may want to try -quick, below */
+                notMatched = true;
+            }
+            break;
+        case OP_INVOKE_VIRTUAL_RANGE:
+            if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
+                /* may want to try -quick, below */
+                notMatched = true;
+            }
+            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:
             notMatched = true;
             break;
         }
 
+        /*
+         * essential-on-SMP substitutions:
+         *  {sget,sput}-* --> {sget,sput}-*-volatile
+         *  return-void --> return-void-barrier
+         */
         if (notMatched && gDvm.dexOptForSmp) {
-            /* additional "essential" substitutions for an SMP device */
             switch (inst) {
             case OP_SGET:
             case OP_SGET_BOOLEAN:
@@ -255,20 +310,18 @@ rewrite_static_field2:
             }
         }
 
-        /* non-essential substitutions */
+        /*
+         * non-essential substitutions:
+         *  invoke-{virtual,super}[/range] --> invoke-*-quick
+         */
         if (notMatched && !essentialOnly) {
             switch (inst) {
             case OP_INVOKE_VIRTUAL:
-                if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
-                    rewriteVirtualInvoke(method, insns,
-                            OP_INVOKE_VIRTUAL_QUICK);
-                }
+                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);
-                }
+                rewriteVirtualInvoke(method, insns,
+                    OP_INVOKE_VIRTUAL_QUICK_RANGE);
                 break;
             case OP_INVOKE_SUPER:
                 rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK);
@@ -277,22 +330,6 @@ rewrite_static_field2:
                 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 */
                 ;
@@ -1103,7 +1140,7 @@ static bool needsReturnBarrier(Method* method)
     /*
      * In theory, we only need to do this if the method actually modifies
      * a final field.  In practice, non-constructor methods are allowed
-     * to modify final fields by the VM, and there are tools that rely on
+     * by the VM to modify final fields, and there are tools that rely on
      * this behavior.  (The compiler does not allow it.)
      *
      * If we alter the verifier to restrict final-field updates to
index 30f7eef..3b3ba7e 100644 (file)
 #define _DALVIK_OPTIMIZE
 
 /*
- * Prep data structures.
- */
-InlineSub* dvmCreateInlineSubsTable(void);
-void dvmFreeInlineSubsTable(InlineSub* inlineSubs);
-
-/*
  * Entry point from DEX preparation.
  */
 void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly);