Version: 2.1.60

Cherry-pick: Handle no-exceptions case when inlining synchronized methods.
CL: https://r8-review.googlesource.com/c/r8/+/52747
Bug: 161154276
Change-Id: I13e5d0b7566f2fa514be6a78056f849126b459d6
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index ea76867..1f7c9d4 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "2.1.59";
+  public static final String LABEL = "2.1.60";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 17cea8b..8f07681 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -652,29 +652,32 @@
           moveExceptionBlocks.add(moveExceptionBlock);
         }
 
-        // Create a phi for the exception values such that we can rethrow the exception if needed.
-        Value exceptionValue;
-        if (moveExceptionBlocks.size() == 1) {
-          exceptionValue =
-              ListUtils.first(moveExceptionBlocks).getInstructions().getFirst().outValue();
-        } else {
-          Phi phi = code.createPhi(monitorExitBlock, throwableType);
-          List<Value> operands =
-              ListUtils.map(
-                  moveExceptionBlocks, block -> block.getInstructions().getFirst().outValue());
-          phi.addOperands(operands);
-          exceptionValue = phi;
+        InstructionListIterator monitorExitBlockIterator = null;
+        if (!moveExceptionBlocks.isEmpty()) {
+          // Create a phi for the exception values such that we can rethrow the exception if needed.
+          Value exceptionValue;
+          if (moveExceptionBlocks.size() == 1) {
+            exceptionValue =
+                ListUtils.first(moveExceptionBlocks).getInstructions().getFirst().outValue();
+          } else {
+            Phi phi = code.createPhi(monitorExitBlock, throwableType);
+            List<Value> operands =
+                ListUtils.map(
+                    moveExceptionBlocks, block -> block.getInstructions().getFirst().outValue());
+            phi.addOperands(operands);
+            exceptionValue = phi;
+          }
+
+          monitorExitBlockIterator = monitorExitBlock.listIterator(code);
+          monitorExitBlockIterator.setInsertionPosition(Position.syntheticNone());
+          monitorExitBlockIterator.add(new Throw(exceptionValue));
+          monitorExitBlock.getMutablePredecessors().addAll(moveExceptionBlocks);
+
+          // Insert the newly created blocks.
+          code.blocks.addAll(moveExceptionBlocks);
+          code.blocks.add(monitorExitBlock);
         }
 
-        InstructionListIterator monitorExitBlockIterator = monitorExitBlock.listIterator(code);
-        monitorExitBlockIterator.setInsertionPosition(Position.syntheticNone());
-        monitorExitBlockIterator.add(new Throw(exceptionValue));
-        monitorExitBlock.getMutablePredecessors().addAll(moveExceptionBlocks);
-
-        // Insert the newly created blocks.
-        code.blocks.addAll(moveExceptionBlocks);
-        code.blocks.add(monitorExitBlock);
-
         // Create a block for holding the monitor-enter instruction. Note that, since this block
         // is created after we attach catch-all handlers to the code, this block will not have any
         // catch handlers.
@@ -702,9 +705,11 @@
 
         // Insert the monitor-enter and monitor-exit instructions.
         monitorEnterBlockIterator.add(new Monitor(Monitor.Type.ENTER, lockValue));
-        monitorExitBlockIterator.previous();
-        monitorExitBlockIterator.add(new Monitor(Monitor.Type.EXIT, lockValue));
-        monitorExitBlock.close(null);
+        if (monitorExitBlockIterator != null) {
+          monitorExitBlockIterator.previous();
+          monitorExitBlockIterator.add(new Monitor(Monitor.Type.EXIT, lockValue));
+          monitorExitBlock.close(null);
+        }
 
         for (BasicBlock block : code.blocks) {
           if (block.exit().isReturn()) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynchronizedMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynchronizedMethodTest.java
new file mode 100644
index 0000000..45997a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynchronizedMethodTest.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2020, 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.ir.optimize.inliner;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InlineSynchronizedMethodTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public InlineSynchronizedMethodTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(InlineSynchronizedMethodTest.class)
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(
+            inspector -> {
+              // TODO(b/163007704): Identify and remove trivial/unobservable monitor enter/exit
+              // pairs.
+              assertEquals(
+                  2,
+                  inspector
+                      .clazz(TestClass.class)
+                      .uniqueMethodWithName("main")
+                      .streamInstructions()
+                      .filter(i -> i.isMonitorEnter() || i.isMonitorExit())
+                      .count());
+            });
+  }
+
+  static class TestClass {
+
+    public static synchronized String foo(String msg) {
+      // No throwing instructions, so no need to join exceptions on exit.
+      return msg;
+    }
+
+    public static void main(String[] args) {
+      System.out.println(foo(args.length == 1 ? args[0] : "Hello, world"));
+    }
+  }
+}