ART: Redundant AND operation removal optimization

In the following code:

    b[i + length] = (byte) ((value & 0xff00) >> 8);

The AND operation is redundant because the lowest 8 bits will be
removed by the 8 bits right shift.

This optimization covers cases where a subsequent SHR and
Type Conversion (from Int and Short --> Byte make an AND redundant.
In such cases, the AND operation is removed. Patch also includes tests.
This patch covers only signed types.
Unsigned types will be supported in a future patch.

This optimization brings 3.3% perf increase on FFTBench workload.

Test: ./scripts/tests/test_art_target.sh --single-test\
458-checker-instruct-simplification

Can also use test_art_host.sh with same option as above.

Author: Aditya Deshpande.
Committer: Artem Serov.

Change-Id: I50b87576a3f998100feefc0e5df65b7b343d87e6
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index a75639f..6ff71f2 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -24,6 +24,7 @@
 #include "intrinsics.h"
 #include "intrinsics_utils.h"
 #include "mirror/class-inl.h"
+#include "optimizing/data_type.h"
 #include "optimizing/nodes.h"
 #include "scoped_thread_state_change-inl.h"
 #include "sharpening.h"
@@ -112,7 +113,6 @@
   void VisitDeoptimize(HDeoptimize* deoptimize) override;
   void VisitVecMul(HVecMul* instruction) override;
   void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* instruction) override;
-
   void SimplifySystemArrayCopy(HInvoke* invoke);
   void SimplifyStringEquals(HInvoke* invoke);
   void SimplifyFP2Int(HInvoke* invoke);
@@ -1185,6 +1185,33 @@
       !(result_type == DataType::Type::kInt64 && input_type == DataType::Type::kFloat32);
 }
 
+static bool CanRemoveRedundantAnd(HConstant* and_right,
+                                  HConstant* shr_right,
+                                  DataType::Type result_type) {
+  int64_t and_cst = Int64FromConstant(and_right);
+  int64_t shr_cst = Int64FromConstant(shr_right);
+
+  // In the following sequence A is the input value, D is the result:
+  // B := A & x
+  // C := B >> r
+  // D := TypeConv(n-bit type) C
+
+  // The value of D is entirely dependent on the bits [n-1:0] of C, which in turn are dependent
+  // on bits [r+n-1:r] of B.
+  // Therefore, if the AND does not change bits [r+n-1:r] of A then it will not affect D.
+  // This can be checked by ensuring that bits [r+n-1:r] of the AND Constant are 1.
+
+  // For example: return (byte) ((value & 0xff00) >> 8)
+  //              return (byte) ((value & 0xff000000) >> 31)
+
+  // The mask sets bits [r+n-1:r] to 1, and all others to 0.
+  int64_t mask = DataType::MaxValueOfIntegralType(DataType::ToUnsigned(result_type)) << shr_cst;
+
+  // If the result of a bitwise AND between the mask and the AND constant is the original mask, then
+  // the AND does not change bits [r+n-1:r], meaning that it is redundant and can be removed.
+  return ((and_cst & mask) == mask);
+}
+
 static inline bool TryReplaceFieldOrArrayGetType(HInstruction* maybe_get, DataType::Type new_type) {
   if (maybe_get->IsInstanceFieldGet()) {
     maybe_get->AsInstanceFieldGet()->SetType(new_type);
@@ -1305,6 +1332,36 @@
         return;
       }
     }
+  } else if (input->IsShr() && DataType::IsIntegralType(result_type) &&
+            // Optimization only applies to lossy Type Conversions.
+            !IsTypeConversionLossless(input_type, result_type)) {
+    DCHECK(DataType::IsIntegralType(input_type));
+    HShr* shr_op = input->AsShr();
+    HConstant* shr_right = shr_op->GetConstantRight();
+    HInstruction* shr_left = shr_op->GetLeastConstantLeft();
+    if (shr_right != nullptr && shr_left->IsAnd()) {
+      // Optimization needs AND -> SHR -> TypeConversion pattern.
+      HAnd* and_op = shr_left->AsAnd();
+      HConstant* and_right = and_op->GetConstantRight();
+      HInstruction* and_left = and_op->GetLeastConstantLeft();
+      DataType::Type and_left_type = and_left->GetType();
+      if (!DataType::IsUnsignedType(and_left_type) &&
+          !DataType::IsUnsignedType(result_type) &&
+          !DataType::IsUnsignedType(and_right->GetType()) &&
+          (DataType::Size(and_left_type) < 8) &&
+          (DataType::Size(result_type) == 1)) {
+        // TODO: Support Unsigned Types.
+        // TODO: Support Long Types.
+        // TODO: Support result types other than byte.
+        if (and_right != nullptr && and_op->HasOnlyOneNonEnvironmentUse() &&
+            CanRemoveRedundantAnd(and_right, shr_right, result_type)) {
+          and_op->ReplaceWith(and_left);
+          and_op->GetBlock()->RemoveInstruction(and_op);
+          RecordSimplification();
+          return;
+        }
+      }
+    }
   } else if (input->IsAnd() && DataType::IsIntegralType(result_type)) {
     DCHECK(DataType::IsIntegralType(input_type));
     HAnd* input_and = input->AsAnd();
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 5ffb75f..557f126 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -2529,6 +2529,219 @@
     return "x".indexOf(ch, fromIndex);  // Not simplified.
   }
 
+  /// CHECK-START: byte Main.$noinline$redundantAndNotRedundant(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndNotRedundant(int) instruction_simplifier (after)
+  /// CHECK-DAG:                       And
+  public static byte $noinline$redundantAndNotRedundant(int value) {
+    // Here the AND is not redundant. This test checks that it is not removed.
+    // 0xf500 = 1111 0101 0000 0000 - bits [11:8] of value will be changed by
+    // the AND with 0101. These bits are kept following the SHR and TypeConversion.
+    return (byte) ((value & 0xf500) >> 8);
+  }
+
+  /// CHECK-START:  byte Main.$noinline$redundantAndOtherUse(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START:  byte Main.$noinline$redundantAndOtherUse(int) instruction_simplifier (after)
+  /// CHECK-DAG:                       And
+  public static byte $noinline$redundantAndOtherUse(int value){
+    int v1 = value & 0xff00;
+    // Above AND is redundant in the context of calculating v2.
+    byte v2 = (byte) (v1 >> 8);
+    byte v3 = (byte) (v1 - 0x6eef);
+    // Use of AND result (v1) in calculating v3 means AND must not be removed.
+    return (byte) (v2 - v3);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift2(short) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift2(short) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndShortToByteShift2(short value) {
+    // & 0xfff only changes bits higher than bit number 12 inclusive.
+    // These bits are discarded during Type Conversion to byte.
+    // Therefore AND is redundant and should be removed.
+    return (byte) ((value & 0xfff) >> 2);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift5(short) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift5(short) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndShortToByteShift5(short value) {
+    // & 0xffe0 changes bits [4:0] and bits higher than 16 inclusive.
+    // These bits are discarded by the right shift and type conversion.
+    // Therefore AND is redundant and should be removed.
+    return (byte) ((value & 0xffe0) >> 5);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift8(short) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift8(short) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndShortToByteShift8(short value) {
+    return (byte) ((value & 0xff00) >> 8);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift9NotRedundant(short) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift9NotRedundant(short) instruction_simplifier (after)
+  /// CHECK-DAG:                       And
+  public static byte $noinline$redundantAndShortToByteShift9NotRedundant(short value) {
+    // Byte and Short operands for bitwise operations (e.g. &,>>) are promoted to Int32 prior to operation.
+    // Negative operands will be sign extended accordingly: (short: 0xff45) --> (int: 0xffffff45)
+    // & fe00 will clear bits [31:16]. For negative 'value', this will clear sign bits.
+    // Without the AND, the right shift will move the sign extended ones into the result instead of zeros.
+    // Therefore AND is not redundant and should not be removed.
+    return (byte) ((value & 0xfe00) >> 9);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift10NotRedundant(short) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift10NotRedundant(short) instruction_simplifier (after)
+  /// CHECK-DAG:                       And
+  public static byte $noinline$redundantAndShortToByteShift10NotRedundant(short value) {
+    return (byte) ((value & 0x1ffff) >> 10);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift12(short) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift12(short) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndShortToByteShift12(short value) {
+    // & fff00 preserves bits [19:8] and clears all others.
+    // Here the AND preserves enough ones; such that zeros are moved into the result.
+    // Therefore AND is redundant and can be removed.
+    return (byte) ((value & 0xfff00) >> 12);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift15(short) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift15(short) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndShortToByteShift15(short value) {
+    return (byte) ((value & 0xffff00) >> 15);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift16(short) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShift16(short) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndShortToByteShift16(short value) {
+    return (byte) ((value & 0xf0ffff00) >> 16);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift2(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift2(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShift2(int value) {
+    return (byte) ((value & 0xfff) >> 2);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift5(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift5(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShift5(int value) {
+    return (byte) ((value & 0xffe0) >> 5);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift8(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift8(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShift8(int value) {
+    return (byte) ((value & 0xffffff00) >> 8);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift9(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift9(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShift9(int value) {
+    return (byte) ((value & 0xf001fe00) >> 9);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift16(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift16(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShift16(int value) {
+    return (byte) ((value & 0xf0ff0000) >> 16);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift20(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift20(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShift20(int value) {
+    return (byte) ((value & 0xfff00000) >> 20);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift25(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift25(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShift25(int value) {
+    return (byte) ((value & 0xfe000000) >> 25);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift24(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift24(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShift24(int value) {
+    return (byte) ((value & 0xff000000) >> 24);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift31(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShift31(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShift31(int value) {
+    return (byte) ((value & 0xff000000) >> 31);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShortAndConstant(short) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndShortToByteShortAndConstant(short) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndShortToByteShortAndConstant(short value) {
+    short and_constant = (short) 0xff00;
+    return (byte) ((value & and_constant) >> 16);
+  }
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShortAndConstant(int) instruction_simplifier (before)
+  /// CHECK-DAG:                       And
+
+  /// CHECK-START: byte Main.$noinline$redundantAndIntToByteShortAndConstant(int) instruction_simplifier (after)
+  /// CHECK-NOT:                       And
+  public static byte $noinline$redundantAndIntToByteShortAndConstant(int value) {
+    short and_constant = (short) 0xff00;
+    return (byte) ((value & and_constant) >> 16);
+  }
+
   public static void main(String[] args) throws Exception {
     Class smaliTests2 = Class.forName("SmaliTests2");
     Method $noinline$XorAllOnes = smaliTests2.getMethod("$noinline$XorAllOnes", int.class);
@@ -2793,6 +3006,48 @@
     assertIntEquals(0, $noinline$singleCharStringIndexOfAfter('x', -1));
     assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('x', 1));
     assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('Z', -1));
+
+    assertIntEquals(0x65,$noinline$redundantAndNotRedundant(0x7fff6f45));
+    assertIntEquals(0x5e,$noinline$redundantAndOtherUse(0x7fff6f45));
+
+    assertIntEquals(0x79, $noinline$redundantAndShortToByteShift2((short) 0x6de7));
+    assertIntEquals(0x79, $noinline$redundantAndShortToByteShift2((short) 0xfde7));
+    assertIntEquals(0x7a, $noinline$redundantAndShortToByteShift5((short) 0x6f45));
+    assertIntEquals(-6, $noinline$redundantAndShortToByteShift5((short) 0xff45));
+    assertIntEquals(0x6f, $noinline$redundantAndShortToByteShift8((short) 0x6f45));
+    assertIntEquals(-1, $noinline$redundantAndShortToByteShift8((short) 0xff45));
+    assertIntEquals(0x37, $noinline$redundantAndShortToByteShift9NotRedundant((short) 0x6f45));
+    assertIntEquals(127, $noinline$redundantAndShortToByteShift9NotRedundant((short) 0xff45));
+    assertIntEquals(127, $noinline$redundantAndShortToByteShift10NotRedundant((short) 0xffff));
+    assertIntEquals(6, $noinline$redundantAndShortToByteShift12((short) 0x6f45));
+    assertIntEquals(-1, $noinline$redundantAndShortToByteShift12((short) 0xff45));
+    assertIntEquals(0, $noinline$redundantAndShortToByteShift15((short) 0x6f45));
+    assertIntEquals(-1, $noinline$redundantAndShortToByteShift15((short) 0xff45));
+    assertIntEquals(0, $noinline$redundantAndShortToByteShift16((short) 0x6f45));
+    assertIntEquals(-1, $noinline$redundantAndShortToByteShift16((short) 0xff45));
+    assertIntEquals(0, $noinline$redundantAndShortToByteShortAndConstant((short) 0x6f45));
+    assertIntEquals(-1, $noinline$redundantAndShortToByteShortAndConstant((short) 0xff45));
+
+    assertIntEquals(0x79, $noinline$redundantAndIntToByteShift2(0x7fff6de7));
+    assertIntEquals(0x79, $noinline$redundantAndIntToByteShift2(0xfffffde7));
+    assertIntEquals(0x7a, $noinline$redundantAndIntToByteShift5(0x7fff6f45));
+    assertIntEquals(-6, $noinline$redundantAndIntToByteShift5(0xffffff45));
+    assertIntEquals(0x6f, $noinline$redundantAndIntToByteShift8(0x7fff6f45));
+    assertIntEquals(-1, $noinline$redundantAndIntToByteShift8(0xffffff45));
+    assertIntEquals(-73, $noinline$redundantAndIntToByteShift9(0x7fff6f45));
+    assertIntEquals(-1, $noinline$redundantAndIntToByteShift9(0xffffff45));
+    assertIntEquals(0x6f, $noinline$redundantAndIntToByteShift16(0x7f6fffff));
+    assertIntEquals(0x6f, $noinline$redundantAndIntToByteShift16(0xff6fff45));
+    assertIntEquals(0x6f, $noinline$redundantAndIntToByteShift20(0x76ffffff));
+    assertIntEquals(0x6f, $noinline$redundantAndIntToByteShift20(0xf6ffff45));
+    assertIntEquals(0x7f, $noinline$redundantAndIntToByteShift24(0x7fffffff));
+    assertIntEquals(-1, $noinline$redundantAndIntToByteShift24(0xffffff45));
+    assertIntEquals(0x3f, $noinline$redundantAndIntToByteShift25(0x7fffffff));
+    assertIntEquals(-1, $noinline$redundantAndIntToByteShift25(0xffffff45));
+    assertIntEquals(0, $noinline$redundantAndIntToByteShift31(0x7fffffff));
+    assertIntEquals(-1, $noinline$redundantAndIntToByteShift31(0xffffff45));
+    assertIntEquals(-1, $noinline$redundantAndIntToByteShortAndConstant(0x7fffff45));
+    assertIntEquals(-1, $noinline$redundantAndIntToByteShortAndConstant(0xffffff45));
   }
 
   private static boolean $inline$true() { return true; }