Merge "Insert moves for exception after other in-moves"
diff --git a/src/test/sampleApks/split/AndroidManifest.xml b/src/test/sampleApks/split/AndroidManifest.xml
new file mode 100644
index 0000000..e46cd1e
--- /dev/null
+++ b/src/test/sampleApks/split/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+for details. All rights reserved. Use of this source code is governed by a
+BSD-style license that can be found in the LICENSE file.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tools.r8.sample.split"
+    android:versionCode="1"
+    android:versionName="0.1" >
+
+  <uses-sdk android:minSdkVersion="21" />
+
+  <application
+      android:icon="@drawable/icon"
+      android:label="@string/app_name" >
+    <activity
+        android:name=".R8Activity"
+        android:label="@string/app_name" >
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+  </application>
+</manifest>
diff --git a/src/test/sampleApks/split/assets/README.txt b/src/test/sampleApks/split/assets/README.txt
new file mode 100644
index 0000000..ecb060c
--- /dev/null
+++ b/src/test/sampleApks/split/assets/README.txt
@@ -0,0 +1 @@
+Sample split app from R8 project
diff --git a/src/test/sampleApks/split/res/drawable-mdpi/icon.png b/src/test/sampleApks/split/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..0799d58
--- /dev/null
+++ b/src/test/sampleApks/split/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/src/test/sampleApks/split/res/layout/main.xml b/src/test/sampleApks/split/res/layout/main.xml
new file mode 100644
index 0000000..7859435
--- /dev/null
+++ b/src/test/sampleApks/split/res/layout/main.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+for details. All rights reserved. Use of this source code is governed by a
+BSD-style license that can be found in the LICENSE file.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="fill_parent"
+    android:layout_height="fill_parent" android:id="@+id/MainLayout"
+    android:background="@android:color/background_light">
+
+  <Button
+      android:layout_height="wrap_content"
+      android:layout_width="match_parent" android:id="@+id/PressButton"
+      android:layout_margin="2dip"
+      android:text="Do something"/>
+</LinearLayout>
diff --git a/src/test/sampleApks/split/res/values/strings.xml b/src/test/sampleApks/split/res/values/strings.xml
new file mode 100644
index 0000000..ecac20f
--- /dev/null
+++ b/src/test/sampleApks/split/res/values/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+for details. All rights reserved. Use of this source code is governed by a
+BSD-style license that can be found in the LICENSE file.
+-->
+<resources>
+  <string name="app_name">R8 split app</string>
+</resources>
diff --git a/src/test/sampleApks/split/split.spec b/src/test/sampleApks/split/split.spec
new file mode 100644
index 0000000..a06e259
--- /dev/null
+++ b/src/test/sampleApks/split/split.spec
@@ -0,0 +1 @@
+com.android.tools.r8.sample.split.SplitClass:split
diff --git a/src/test/sampleApks/split/split_manifest/AndroidManifest.xml b/src/test/sampleApks/split/split_manifest/AndroidManifest.xml
new file mode 100644
index 0000000..21ea9f3
--- /dev/null
+++ b/src/test/sampleApks/split/split_manifest/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+for details. All rights reserved. Use of this source code is governed by a
+BSD-style license that can be found in the LICENSE file.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tools.r8.sample.split"
+    split="featuresplit"
+    android:versionCode="1"
+    android:versionName="0.1" >
+
+  <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/R8Activity.java b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/R8Activity.java
new file mode 100644
index 0000000..a80cf55
--- /dev/null
+++ b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/R8Activity.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.sample.split;
+
+import android.app.Activity;
+import android.os.Bundle;
+import com.android.tools.r8.sample.split.R;
+import com.android.tools.r8.sample.split.SplitClass;
+
+public class R8Activity extends Activity {
+  private int res = 0;
+
+  public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setTheme(android.R.style.Theme_Light);
+    setContentView(R.layout.main);
+    // Currently this is split up into 100 iterations to be able to better see
+    // the impact of the jit on later versions of art.
+    long total = 0;
+    for (int i = 0; i < 100; i++) {
+      total += benchmarkCall();
+    }
+    System.out.println("Total: " + total);
+  }
+
+  public long benchmarkCall() {
+    SplitClass split = new SplitClass(3);
+    long start = System.nanoTime();
+    for (int i = 0; i < 1000; i++) {
+      // Ensure no dead code elimination.
+      res = split.calculate(i);
+    }
+    long finish = System.nanoTime();
+    long timeElapsed = finish - start;
+    System.out.println("Took: " + timeElapsed);
+    return timeElapsed;
+  }
+}
diff --git a/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitClass.java b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitClass.java
new file mode 100644
index 0000000..9b6990d
--- /dev/null
+++ b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitClass.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.sample.split;
+
+public class SplitClass {
+  final int initialValue;
+
+  public SplitClass(int initialValue) {
+    this.initialValue = initialValue;
+  }
+
+  public int calculate(int x) {
+    int result = 2;
+    for (int i = 0; i < 42; i++) {
+      result += initialValue + x;
+    }
+    return result;
+  }
+}
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py
index e1b2deb..fca92ca 100755
--- a/tools/build_sample_apk.py
+++ b/tools/build_sample_apk.py
@@ -18,12 +18,14 @@
 ANDROID_JAR = 'third_party/android_jar/lib-v{api}/android.jar'
 DEFAULT_AAPT = 'aapt' # Assume in path.
 DEFAULT_D8 = os.path.join(utils.REPO_ROOT, 'tools', 'd8.py')
+DEFAULT_DEXSPLITTER = os.path.join(utils.REPO_ROOT, 'tools', 'dexsplitter.py')
 DEFAULT_JAVAC = 'javac'
 SRC_LOCATION = 'src/com/android/tools/r8/sample/{app}/*.java'
 DEFAULT_KEYSTORE = os.path.join(os.getenv('HOME'), '.android', 'debug.keystore')
 
 SAMPLE_APKS = [
-    'simple'
+    'simple',
+    'split'
 ]
 
 def parse_options():
@@ -38,6 +40,9 @@
   result.add_option('--keystore',
                     help='Keystore used for signing',
                     default=DEFAULT_KEYSTORE)
+  result.add_option('--split',
+                    help='Split the app using the split.spec file',
+                    default=False, action='store_true')
   result.add_option('--app',
                     help='Which app to build',
                     default='simple',
@@ -72,6 +77,12 @@
 def get_src_path(app):
   return os.path.join(get_sample_dir(app), 'src')
 
+def get_dex_path(app):
+  return os.path.join(get_bin_path(app), 'classes.dex')
+
+def get_split_path(app, split):
+  return os.path.join(get_bin_path(app), split, 'classes.dex')
+
 def run_aapt_pack(aapt, api, app):
   with utils.ChangedWorkingDirectory(get_sample_dir(app)):
     args = ['package',
@@ -86,6 +97,16 @@
             '-G', os.path.join(get_build_dir(app), 'proguard_options')]
     run_aapt(aapt, args)
 
+def run_aapt_split_pack(aapt, api, app):
+  with utils.ChangedWorkingDirectory(get_sample_dir(app)):
+    args = ['package',
+            '-v', '-f',
+            '-I', get_android_jar(api),
+            '-M', 'split_manifest/AndroidManifest.xml',
+            '-S', 'res',
+            '-F', os.path.join(get_bin_path(app), 'split_resources.ap_')]
+    run_aapt(aapt, args)
+
 def compile_with_javac(api, app):
   with utils.ChangedWorkingDirectory(get_sample_dir(app)):
     files = glob.glob(SRC_LOCATION.format(app=app))
@@ -110,28 +131,57 @@
   utils.PrintCmd(command)
   subprocess.check_call(command)
 
-def create_temp_apk(app):
+def split(app):
+  split_spec = os.path.join(get_sample_dir(app), 'split.spec')
+  command = [DEFAULT_DEXSPLITTER,
+             '--input', get_dex_path(app),
+             '--output', get_bin_path(app),
+             '--feature-splits', split_spec]
+  utils.PrintCmd(command)
+  subprocess.check_call(command)
+
+def create_temp_apk(app, prefix):
   temp_apk_path = os.path.join(get_bin_path(app), '%s.ap_' % app)
-  shutil.move(os.path.join(get_bin_path(app), 'resources.ap_'),
-              temp_apk_path)
+  shutil.copyfile(os.path.join(get_bin_path(app), '%sresources.ap_' % prefix),
+                  temp_apk_path)
   return temp_apk_path
 
-def aapt_add_dex(aapt, app, temp_apk_path):
+def aapt_add_dex(aapt, dex, temp_apk_path):
   args = ['add',
           '-k', temp_apk_path,
-          os.path.join(get_bin_path(app), 'classes.dex')]
+          dex]
   run_aapt(aapt, args)
 
 def Main():
   (options, args) = parse_options()
+  is_split = options.split
   run_aapt_pack(options.aapt, options.api, options.app)
+  if is_split:
+    run_aapt_split_pack(options.aapt, options.api, options.app)
   compile_with_javac(options.api, options.app)
   dex(options.app, options.api)
-  temp_apk_path = create_temp_apk(options.app)
-  aapt_add_dex(options.aapt, options.app, temp_apk_path)
+  dex_files = { options.app: get_dex_path(options.app)}
+  dex_path = get_dex_path(options.app)
+  if is_split:
+    split(options.app)
+    dex_path = get_split_path(options.app, 'base')
+
+  temp_apk_path = create_temp_apk(options.app, '')
+  aapt_add_dex(options.aapt, dex_path, temp_apk_path)
   apk_path = os.path.join(get_bin_path(options.app), '%s.apk' % options.app)
   apk_utils.sign(temp_apk_path, apk_path,  options.keystore)
   print('Apk available at: %s' % apk_path)
 
+  if split:
+    split_temp_apk_path = create_temp_apk(options.app, 'split_')
+    aapt_add_dex(options.aapt,
+                 get_split_path(options.app, 'split'),
+                 temp_apk_path)
+    split_apk_path = os.path.join(get_bin_path(options.app), 'featuresplit.apk')
+    apk_utils.sign(temp_apk_path, split_apk_path,  options.keystore)
+    print('Feature split available at: %s' % split_apk_path)
+
+
+
 if __name__ == '__main__':
   sys.exit(Main())