type conversion elimination for store value only uses.

Some type conversions can be eliminated if the converted values are used
as store values only. Code generation can handle the conversion
implicitly.

Test: run-test on host. 711-checker-type-conversion.
Change-Id: I60a402cf4610683acc5ca2ceba19045b4c17b843
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index a65d7b1..a42a85d 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1081,6 +1081,58 @@
   }
 }
 
+// The type conversion is only used for storing into a field/element of the
+// same/narrower size.
+static bool IsTypeConversionForStoringIntoNoWiderFieldOnly(HTypeConversion* type_conversion) {
+  if (type_conversion->HasEnvironmentUses()) {
+    return false;
+  }
+  DataType::Type input_type = type_conversion->GetInputType();
+  DataType::Type result_type = type_conversion->GetResultType();
+  if (!DataType::IsIntegralType(input_type) ||
+      !DataType::IsIntegralType(result_type) ||
+      input_type == DataType::Type::kInt64 ||
+      result_type == DataType::Type::kInt64) {
+    // Type conversion is needed if non-integer types are involved, or 64-bit
+    // types are involved, which may use different number of registers.
+    return false;
+  }
+  if (DataType::Size(input_type) >= DataType::Size(result_type)) {
+    // Type conversion is not necessary when storing to a field/element of the
+    // same/smaller size.
+  } else {
+    // We do not handle this case here.
+    return false;
+  }
+
+  // Check if the converted value is only used for storing into heap.
+  for (const HUseListNode<HInstruction*>& use : type_conversion->GetUses()) {
+    HInstruction* instruction = use.GetUser();
+    if (instruction->IsInstanceFieldSet() &&
+        instruction->AsInstanceFieldSet()->GetFieldType() == result_type) {
+      DCHECK_EQ(instruction->AsInstanceFieldSet()->GetValue(), type_conversion);
+      continue;
+    }
+    if (instruction->IsStaticFieldSet() &&
+        instruction->AsStaticFieldSet()->GetFieldType() == result_type) {
+      DCHECK_EQ(instruction->AsStaticFieldSet()->GetValue(), type_conversion);
+      continue;
+    }
+    if (instruction->IsArraySet() &&
+        instruction->AsArraySet()->GetComponentType() == result_type &&
+        // not index use.
+        instruction->AsArraySet()->GetIndex() != type_conversion) {
+      DCHECK_EQ(instruction->AsArraySet()->GetValue(), type_conversion);
+      continue;
+    }
+    // The use is not as a store value, or the field/element type is not the
+    // same as the result_type, keep the type conversion.
+    return false;
+  }
+  // Codegen automatically handles the type conversion during the store.
+  return true;
+}
+
 void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) {
   HInstruction* input = instruction->GetInput();
   DataType::Type input_type = input->GetType();
@@ -1169,6 +1221,13 @@
       return;
     }
   }
+
+  if (IsTypeConversionForStoringIntoNoWiderFieldOnly(instruction)) {
+    instruction->ReplaceWith(input);
+    instruction->GetBlock()->RemoveInstruction(instruction);
+    RecordSimplification();
+    return;
+  }
 }
 
 void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) {
diff --git a/test/711-checker-type-conversion/src/Main.java b/test/711-checker-type-conversion/src/Main.java
index 2c9c3a1..ae58200 100644
--- a/test/711-checker-type-conversion/src/Main.java
+++ b/test/711-checker-type-conversion/src/Main.java
@@ -22,6 +22,32 @@
     }
   }
 
+  public static void assertShortEquals(short expected, short result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void assertIntEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void assertLongEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void assertCharEquals(char expected, char result) {
+    if (expected != result) {
+      // Values are cast to int to display numeric values instead of
+      // (UTF-16 encoded) characters.
+      throw new Error("Expected: " + (int)expected + ", found: " + (int)result);
+    }
+  }
+
   /// CHECK-START: byte Main.getByte1() constant_folding (before)
   /// CHECK: TypeConversion
   /// CHECK: TypeConversion
@@ -69,9 +95,170 @@
     return (byte)((byte)i + (byte)i);
   }
 
+  static byte byteVal = -1;
+  static short shortVal = -1;
+  static char charVal = 0xffff;
+  static int intVal = -1;
+
+  static byte[] byteArr = { 0 };
+  static short[] shortArr = { 0 };
+  static char[] charArr = { 0 };
+  static int[] intArr = { 0 };
+
+  static byte $noinline$getByte() {
+    return byteVal;
+  }
+
+  static short $noinline$getShort() {
+    return shortVal;
+  }
+
+  static char $noinline$getChar() {
+    return charVal;
+  }
+
+  static int $noinline$getInt() {
+    return intVal;
+  }
+
+  static boolean sFlag = true;
+
+  /// CHECK-START: void Main.byteToShort() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void byteToShort() {
+    shortArr[0] = 0;
+    if (sFlag) {
+      shortArr[0] = $noinline$getByte();
+    }
+  }
+
+  /// CHECK-START: void Main.byteToChar() instruction_simplifier$before_codegen (after)
+  /// CHECK: TypeConversion
+  private static void byteToChar() {
+    charArr[0] = 0;
+    if (sFlag) {
+      charArr[0] = (char)$noinline$getByte();
+    }
+  }
+
+  /// CHECK-START: void Main.byteToInt() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void byteToInt() {
+    intArr[0] = 0;
+    if (sFlag) {
+      intArr[0] = $noinline$getByte();
+    }
+  }
+
+  /// CHECK-START: void Main.charToByte() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void charToByte() {
+    byteArr[0] = 0;
+    if (sFlag) {
+      byteArr[0] = (byte)$noinline$getChar();
+    }
+  }
+
+  /// CHECK-START: void Main.charToShort() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void charToShort() {
+    shortArr[0] = 0;
+    if (sFlag) {
+      shortArr[0] = (short)$noinline$getChar();
+    }
+  }
+
+  /// CHECK-START: void Main.charToInt() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void charToInt() {
+    intArr[0] = 0;
+    if (sFlag) {
+      intArr[0] = $noinline$getChar();
+    }
+  }
+
+  /// CHECK-START: void Main.shortToByte() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void shortToByte() {
+    byteArr[0] = 0;
+    if (sFlag) {
+      byteArr[0] = (byte)$noinline$getShort();
+    }
+  }
+
+  /// CHECK-START: void Main.shortToChar() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void shortToChar() {
+    charArr[0] = 0;
+    if (sFlag) {
+      charArr[0] = (char)$noinline$getShort();
+    }
+  }
+
+  /// CHECK-START: void Main.shortToInt() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void shortToInt() {
+    intArr[0] = 0;
+    if (sFlag) {
+      intArr[0] = $noinline$getShort();
+    }
+  }
+
+  /// CHECK-START: void Main.intToByte() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void intToByte() {
+    byteArr[0] = 0;
+    if (sFlag) {
+      byteArr[0] = (byte)$noinline$getInt();
+    }
+  }
+
+  /// CHECK-START: void Main.intToShort() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void intToShort() {
+    shortArr[0] = 0;
+    if (sFlag) {
+      shortArr[0] = (short)$noinline$getInt();
+    }
+  }
+
+  /// CHECK-START: void Main.intToChar() instruction_simplifier$before_codegen (after)
+  /// CHECK-NOT: TypeConversion
+  private static void intToChar() {
+    charArr[0] = 0;
+    if (sFlag) {
+      charArr[0] = (char)$noinline$getInt();
+    }
+  }
+
   public static void main(String[] args) {
     assertByteEquals(getByte1(), (byte)-5);
     assertByteEquals(getByte2(), (byte)(-201));
     assertByteEquals(getByte3(), (byte)(0xcd + 0xcd));
+
+    byteToShort();
+    assertShortEquals(shortArr[0], (short)-1);
+    byteToChar();
+    assertCharEquals(charArr[0], (char)-1);
+    byteToInt();
+    assertIntEquals(intArr[0], -1);
+    charToByte();
+    assertByteEquals(byteArr[0], (byte)-1);
+    charToShort();
+    assertShortEquals(shortArr[0], (short)-1);
+    charToInt();
+    assertIntEquals(intArr[0], 0xffff);
+    shortToByte();
+    assertByteEquals(byteArr[0], (byte)-1);
+    shortToChar();
+    assertCharEquals(charArr[0], (char)-1);
+    shortToInt();
+    assertIntEquals(intArr[0], -1);
+    intToByte();
+    assertByteEquals(byteArr[0], (byte)-1);
+    intToShort();
+    assertShortEquals(shortArr[0], (short)-1);
+    intToChar();
+    assertCharEquals(charArr[0], (char)-1);
   }
 }