Backport Math/StrictMath.multiplyHigh

Test: tools/test.py --dex_vm all --no-internal -v *Backport*Test*
Test: tools/test.py --no-internal -v *GenerateBackportMethods*
Change-Id: Icf3d4400ab3e5be6fd6c9ac977ac0f8e840794f5
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 8610eca..8b44470 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -1214,6 +1214,15 @@
                 method,
                 BackportedMethods::MathMethods_multiplyFull));
 
+        // long {Math,StrictMath}.multiplyHigh(long, long)
+        name = factory.createString("multiplyHigh");
+        proto = factory.createProto(factory.longType, factory.longType, factory.longType);
+        method = factory.createMethod(type, proto, name);
+        addProvider(
+            new MethodGenerator(
+                method,
+                BackportedMethods::MathMethods_multiplyHigh));
+
         // long {Math,StrictMath}.floorDiv(long, int)
         name = factory.createString("floorDiv");
         proto = factory.createProto(factory.longType, factory.longType, factory.intType);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 80f34d99..5ca3dbd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -3175,6 +3175,110 @@
         ImmutableList.of());
   }
 
+  public static CfCode MathMethods_multiplyHigh(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    CfLabel label14 = new CfLabel();
+    CfLabel label15 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        32,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.LONG, 0),
+            new CfConstNumber(4294967295L, ValueType.LONG),
+            new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
+            new CfStore(ValueType.LONG, 4),
+            label1,
+            new CfLoad(ValueType.LONG, 0),
+            new CfConstNumber(32, ValueType.INT),
+            new CfLogicalBinop(CfLogicalBinop.Opcode.Shr, NumericType.LONG),
+            new CfStore(ValueType.LONG, 6),
+            label2,
+            new CfLoad(ValueType.LONG, 2),
+            new CfConstNumber(4294967295L, ValueType.LONG),
+            new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
+            new CfStore(ValueType.LONG, 8),
+            label3,
+            new CfLoad(ValueType.LONG, 2),
+            new CfConstNumber(32, ValueType.INT),
+            new CfLogicalBinop(CfLogicalBinop.Opcode.Shr, NumericType.LONG),
+            new CfStore(ValueType.LONG, 10),
+            label4,
+            new CfLoad(ValueType.LONG, 4),
+            new CfLoad(ValueType.LONG, 8),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Mul, NumericType.LONG),
+            new CfStore(ValueType.LONG, 12),
+            label5,
+            new CfLoad(ValueType.LONG, 12),
+            new CfConstNumber(32, ValueType.INT),
+            new CfLogicalBinop(CfLogicalBinop.Opcode.Ushr, NumericType.LONG),
+            new CfStore(ValueType.LONG, 14),
+            label6,
+            new CfLoad(ValueType.LONG, 6),
+            new CfLoad(ValueType.LONG, 8),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Mul, NumericType.LONG),
+            new CfStore(ValueType.LONG, 16),
+            label7,
+            new CfLoad(ValueType.LONG, 16),
+            new CfLoad(ValueType.LONG, 14),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
+            new CfStore(ValueType.LONG, 18),
+            label8,
+            new CfLoad(ValueType.LONG, 18),
+            new CfConstNumber(4294967295L, ValueType.LONG),
+            new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
+            new CfStore(ValueType.LONG, 20),
+            label9,
+            new CfLoad(ValueType.LONG, 18),
+            new CfConstNumber(32, ValueType.INT),
+            new CfLogicalBinop(CfLogicalBinop.Opcode.Shr, NumericType.LONG),
+            new CfStore(ValueType.LONG, 22),
+            label10,
+            new CfLoad(ValueType.LONG, 4),
+            new CfLoad(ValueType.LONG, 10),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Mul, NumericType.LONG),
+            new CfStore(ValueType.LONG, 24),
+            label11,
+            new CfLoad(ValueType.LONG, 24),
+            new CfLoad(ValueType.LONG, 20),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
+            new CfStore(ValueType.LONG, 26),
+            label12,
+            new CfLoad(ValueType.LONG, 26),
+            new CfConstNumber(32, ValueType.INT),
+            new CfLogicalBinop(CfLogicalBinop.Opcode.Shr, NumericType.LONG),
+            new CfStore(ValueType.LONG, 28),
+            label13,
+            new CfLoad(ValueType.LONG, 6),
+            new CfLoad(ValueType.LONG, 10),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Mul, NumericType.LONG),
+            new CfStore(ValueType.LONG, 30),
+            label14,
+            new CfLoad(ValueType.LONG, 30),
+            new CfLoad(ValueType.LONG, 22),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
+            new CfLoad(ValueType.LONG, 28),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
+            new CfReturn(ValueType.LONG),
+            label15),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode MathMethods_negateExactInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
diff --git a/src/test/examplesJava9/backport/MathBackportJava9Main.java b/src/test/examplesJava9/backport/MathBackportJava9Main.java
index c465af1..9030283 100644
--- a/src/test/examplesJava9/backport/MathBackportJava9Main.java
+++ b/src/test/examplesJava9/backport/MathBackportJava9Main.java
@@ -4,11 +4,14 @@
 
 package backport;
 
+import java.math.BigInteger;
+
 public class MathBackportJava9Main {
 
   public static void main(String[] args) {
     testMultiplyExactLongInt();
     testMultiplyFull();
+    testMultiplyHigh();
     testFloorDivLongInt();
     testFloorModLongInt();
   }
@@ -37,6 +40,27 @@
         Math.multiplyFull(Integer.MIN_VALUE, Integer.MIN_VALUE));
   }
 
+  public static void testMultiplyHigh() {
+    long[] interestingValues = {
+        Long.MIN_VALUE, Long.MAX_VALUE,
+        Integer.MIN_VALUE, Integer.MAX_VALUE,
+        Short.MIN_VALUE, Short.MAX_VALUE,
+        Byte.MIN_VALUE, Byte.MAX_VALUE,
+        0L,
+        -1L, 1L,
+        -42L, 42L
+    };
+    for (long x : interestingValues) {
+      for (long y : interestingValues) {
+        long expected = BigInteger.valueOf(x)
+            .multiply(BigInteger.valueOf(y))
+            .shiftRight(64)
+            .longValue();
+        assertEquals(expected, Math.multiplyHigh(x, y));
+      }
+    }
+  }
+
   public static void testFloorDivLongInt() {
     assertEquals(1L, Math.floorDiv(4L, 4));
     assertEquals(1L, Math.floorDiv(-4L, -4));
diff --git a/src/test/examplesJava9/backport/StrictMathBackportJava9Main.java b/src/test/examplesJava9/backport/StrictMathBackportJava9Main.java
index e0e45fb..e9c33ca 100644
--- a/src/test/examplesJava9/backport/StrictMathBackportJava9Main.java
+++ b/src/test/examplesJava9/backport/StrictMathBackportJava9Main.java
@@ -4,6 +4,8 @@
 
 package backport;
 
+import java.math.BigInteger;
+
 public class StrictMathBackportJava9Main {
 
   public static void main(String[] args) {
@@ -37,6 +39,27 @@
         StrictMath.multiplyFull(Integer.MIN_VALUE, Integer.MIN_VALUE));
   }
 
+  public static void testMultiplyHigh() {
+    long[] interestingValues = {
+        Long.MIN_VALUE, Long.MAX_VALUE,
+        Integer.MIN_VALUE, Integer.MAX_VALUE,
+        Short.MIN_VALUE, Short.MAX_VALUE,
+        Byte.MIN_VALUE, Byte.MAX_VALUE,
+        0L,
+        -1L, 1L,
+        -42L, 42L
+    };
+    for (long x : interestingValues) {
+      for (long y : interestingValues) {
+        long expected = BigInteger.valueOf(x)
+            .multiply(BigInteger.valueOf(y))
+            .shiftRight(64)
+            .longValue();
+        assertEquals(expected, StrictMath.multiplyHigh(x, y));
+      }
+    }
+  }
+
   public static void testFloorDivLongInt() {
     assertEquals(1L, StrictMath.floorDiv(4L, 4));
     assertEquals(1L, StrictMath.floorDiv(-4L, -4));
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/MathMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/MathMethods.java
index d3379c8..a41259d 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/MathMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/MathMethods.java
@@ -158,6 +158,29 @@
     return (long) x * y;
   }
 
+  public static long multiplyHigh(long x, long y) {
+    // Adapted from Hacker's Delight (2nd ed), 8-2.
+    long xLow = x & 0xFFFFFFFFL;
+    long xHigh = x >> 32;
+    long yLow = y & 0xFFFFFFFFL;
+    long yHigh = y >> 32;
+
+    long lowLow = xLow * yLow;
+    long lowLowCarry = lowLow >>> 32;
+
+    long highLow = xHigh * yLow;
+    long mid1 = highLow + lowLowCarry;
+    long mid1Low = mid1 & 0xFFFFFFFFL;
+    long mid1High = mid1 >> 32;
+
+    long lowHigh = xLow * yHigh;
+    long mid2 = lowHigh + mid1Low;
+    long mid2High = mid2 >> 32;
+
+    long highHigh = xHigh * yHigh;
+    return highHigh + mid1High + mid2High;
+  }
+
   public static int negateExactInt(int value) {
     if (value == Integer.MIN_VALUE) {
       throw new ArithmeticException();