Version 1.4.87

Cherry pick: Do a workaround for dominator tree calculation crash on mediatek 4.4.2/4.4.4
CL: https://r8-review.googlesource.com/c/r8/+/36818

Bug: 117907456, 119895393, 121355317, 128926846
Change-Id: I6112322b27d9c9a89b4bb53e17d755dbe0806aea
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 99999c6..4cf066b 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 = "1.4.86";
+  public static final String LABEL = "1.4.87";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index d989d07..262b3ac 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -32,7 +32,6 @@
 import com.android.tools.r8.code.MoveWide16;
 import com.android.tools.r8.code.MoveWideFrom16;
 import com.android.tools.r8.code.Nop;
-import com.android.tools.r8.code.ReturnVoid;
 import com.android.tools.r8.code.Throw;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
@@ -238,19 +237,45 @@
 
     // Workaround dalvik tracing bug, where the dalvik tracing JIT can end up tracing
     // past the end of the instruction stream if the instruction streams ends with a throw.
+    // See: b/117907456
     // We could have also changed the block order, however, moving the throwing block higher
     // led to larger code in all experiments (multiple gmscore version and R8 run on itself).
     if (options.canHaveTracingPastInstructionsStreamBug()
         && dexInstructions.get(dexInstructions.size() - 1) instanceof Throw
         && hasBackwardsBranch) {
-      // Generating a throw with the right type makes some Art constant propagation
-      // implementations crash. Therefore, since this is dead code anyway, we do not
-      // generate a return of the right type. Instead we just generate an unreachable
-      // return-void. See b/121355317.
-      Instruction returnVoid = new ReturnVoid();
-      returnVoid.setOffset(offset);
-      offset += returnVoid.getSize();
-      dexInstructions.add(returnVoid);
+      // This is the last in a series of different workarounds tried out.
+      // Having an empty non reachable loop make some mediatek vms crash: b/119895393
+      // Having a (unreachable) return null/return-void (type correct) trips up the constant
+      // propagation in some vms: b/121355317
+      // Having always a (unreachable) return-void causes Mediatek 4.4.2/4.4.4 to crash trying
+      // to get a dominator for the unreachable code: b/128926846
+      // Current implementation generates code that jumps over the throw, and then back to the throw
+      // again.
+      // throw v10
+      // becomes:
+      // goto +2
+      // throw v10
+      // goto -1
+      // That way we have no unreachable code, and we never end in a throw. The tracer will still
+      // trace to the throw, but after moving to the second goto it will trace back again and see
+      // an instruction it has already seen.
+      Instruction throwInstruction = dexInstructions.get(dexInstructions.size() - 1);
+      offset = throwInstruction.getOffset();
+
+      // Generate the new forward and backward gotos, update offsets.
+      Instruction forward = new Goto(throwInstruction.getSize() + Goto.SIZE);
+      Instruction backward = new Goto(-throwInstruction.getSize());
+      forward.setOffset(offset);
+      offset += forward.getSize();
+      throwInstruction.setOffset(offset);
+      offset += throwInstruction.getSize();
+      backward.setOffset(offset);
+      offset += backward.getSize();
+      // Replace the throw in the instruction stream with goto(forward), throw, goto(backwards)
+      dexInstructions.remove(dexInstructions.size() - 1);
+      dexInstructions.add(forward);
+      dexInstructions.add(throwInstruction);
+      dexInstructions.add(backward);
     }
 
     // Compute switch payloads.
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index bf78270..112fa5f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -864,13 +864,14 @@
   // The Dalvik tracing JIT can trace past the end of the instruction stream and end up
   // parsing non-code bytes as code (typically leading to a crash). See b/117907456.
   //
-  // In order to workaround this we insert a return of the right type after the throw
-  // when the instruction stream ends with a throw and there are backwards branches
-  // in the code.
-  //
+  // In order to workaround this we insert a goto past the throw, and another goto after the throw
+  // jumping back to the throw.
+
   // We used to insert a empty loop at the end, however, mediatek has an optimizer
   // on lollipop devices that cannot deal with an unreachable infinite loop, so we
-  // couldn't do that. See b/119895393.2
+  // couldn't do that. See b/119895393.
+  // We also could not insert any dead code (e.g. a return) because that would make mediatek
+  // dominator calculations on 7.0.0 crash. See b/128926846.
   public boolean canHaveTracingPastInstructionsStreamBug() {
     return minApiLevel < AndroidApiLevel.L.getLevel();
   }
diff --git a/src/test/java/com/android/tools/r8/regress/b117907456/B117907456.java b/src/test/java/com/android/tools/r8/regress/b117907456/B117907456.java
index 2639fe8..a4bb99a 100644
--- a/src/test/java/com/android/tools/r8/regress/b117907456/B117907456.java
+++ b/src/test/java/com/android/tools/r8/regress/b117907456/B117907456.java
@@ -11,10 +11,8 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.code.Goto;
 import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.Return;
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.code.ReturnWide;
 import com.android.tools.r8.code.Throw;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -36,10 +34,8 @@
 
 public class B117907456 extends TestBase {
 
-  private boolean isReturn(Instruction lastInstruction) {
-    return lastInstruction instanceof ReturnVoid
-        || lastInstruction instanceof Return
-        || lastInstruction instanceof ReturnWide;
+  private boolean isGoto(Instruction lastInstruction) {
+    return lastInstruction instanceof Goto;
   }
 
   @Test
@@ -49,7 +45,7 @@
     Instruction[] instructions = method.getMethod().getCode().asDexCode().instructions;
     Instruction lastInstruction = instructions[instructions.length - 1];
     assertFalse(lastInstruction instanceof Throw);
-    assertTrue(isReturn(lastInstruction));
+    assertTrue(isGoto(lastInstruction));
   }
 
   @Test