Simplify boxing followed by unboxing.

Also mark boxing `valueOf()` intrinsics as never null
to avoid creating unnecessary `HNullCheck` instructions.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Change-Id: I86e7721e3af6c59407aa2ddfc1bd11bd2fdac83c
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index fb6d53b..3328f3b 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -22,6 +22,7 @@
 #include "data_type-inl.h"
 #include "driver/compiler_options.h"
 #include "escape.h"
+#include "intrinsic_objects.h"
 #include "intrinsics.h"
 #include "intrinsics_utils.h"
 #include "mirror/class-inl.h"
@@ -30,6 +31,7 @@
 #include "scoped_thread_state_change-inl.h"
 #include "sharpening.h"
 #include "string_builder_append.h"
+#include "well_known_classes.h"
 
 namespace art HIDDEN {
 
@@ -114,6 +116,7 @@
   void VisitDeoptimize(HDeoptimize* deoptimize) override;
   void VisitVecMul(HVecMul* instruction) override;
   void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* instruction) override;
+  void SimplifyBoxUnbox(HInvoke* instruction, ArtField* field, DataType::Type type);
   void SimplifySystemArrayCopy(HInvoke* invoke);
   void SimplifyStringEquals(HInvoke* invoke);
   void SimplifyFP2Int(HInvoke* invoke);
@@ -2407,6 +2410,29 @@
   TryHandleAssociativeAndCommutativeOperation(instruction);
 }
 
+void InstructionSimplifierVisitor::SimplifyBoxUnbox(
+    HInvoke* instruction, ArtField* field, DataType::Type type) {
+  DCHECK(instruction->GetIntrinsic() == Intrinsics::kByteValueOf ||
+         instruction->GetIntrinsic() == Intrinsics::kShortValueOf ||
+         instruction->GetIntrinsic() == Intrinsics::kCharacterValueOf ||
+         instruction->GetIntrinsic() == Intrinsics::kIntegerValueOf);
+  const HUseList<HInstruction*>& uses = instruction->GetUses();
+  for (auto it = uses.begin(), end = uses.end(); it != end;) {
+    HInstruction* user = it->GetUser();
+    ++it;  // Increment the iterator before we potentially remove the node from the list.
+    if (user->IsInstanceFieldGet() &&
+        user->AsInstanceFieldGet()->GetFieldInfo().GetField() == field &&
+        // Note: Due to other simplifications, we may have an `HInstanceFieldGet` with
+        // a different type (Int8 vs. Uint8, Int16 vs. Uint16) for the same field.
+        // Do not optimize that case for now. (We would need to insert a `HTypeConversion`.)
+        user->GetType() == type) {
+      user->ReplaceWith(instruction->InputAt(0));
+      RecordSimplification();
+      // Do not remove `user` while we're iterating over the block's instructions. Let DCE do it.
+    }
+  }
+}
+
 void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) {
   HInstruction* argument = instruction->InputAt(1);
   HInstruction* receiver = instruction->InputAt(0);
@@ -3058,6 +3084,12 @@
 
 void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
   switch (instruction->GetIntrinsic()) {
+#define SIMPLIFY_BOX_UNBOX(name, low, high, type, start_index) \
+    case Intrinsics::k ## name ## ValueOf: \
+      SimplifyBoxUnbox(instruction, WellKnownClasses::java_lang_##name##_value, type); \
+      break;
+    BOXED_TYPES(SIMPLIFY_BOX_UNBOX)
+#undef SIMPLIFY_BOX_UNBOX
     case Intrinsics::kStringEquals:
       SimplifyStringEquals(instruction);
       break;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 53ed49a..2cfe5b3 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -35,6 +35,7 @@
 #include "class_root-inl.h"
 #include "code_generator.h"
 #include "common_dominator.h"
+#include "intrinsic_objects.h"
 #include "intrinsics.h"
 #include "intrinsics_list.h"
 #include "mirror/class-inl.h"
@@ -3363,6 +3364,21 @@
   }
 }
 
+bool HInvokeStaticOrDirect::CanBeNull() const {
+  if (GetType() != DataType::Type::kReference || IsStringInit()) {
+    return false;
+  }
+  switch (GetIntrinsic()) {
+#define DEFINE_BOXED_CASE(name, unused1, unused2, unused3, unused4) \
+    case Intrinsics::k##name##ValueOf: \
+      return false;
+    BOXED_TYPES(DEFINE_BOXED_CASE)
+#undef DEFINE_BOXED_CASE
+    default:
+      return true;
+  }
+}
+
 bool HInvokeVirtual::CanDoImplicitNullCheckOn(HInstruction* obj) const {
   if (obj != InputAt(0)) {
     return false;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 04d5132..4db72a6 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -5065,9 +5065,7 @@
     return false;
   }
 
-  bool CanBeNull() const override {
-    return GetType() == DataType::Type::kReference && !IsStringInit();
-  }
+  bool CanBeNull() const override;
 
   MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; }
   CodePtrLocation GetCodePtrLocation() const {
diff --git a/test/640-checker-integer-valueof/src/Main.java b/test/640-checker-integer-valueof/src/Main.java
index 0837fd1..cf0d7d8 100644
--- a/test/640-checker-integer-valueof/src/Main.java
+++ b/test/640-checker-integer-valueof/src/Main.java
@@ -48,6 +48,178 @@
     return Integer.valueOf(55555);
   }
 
+  /// CHECK-START: byte Main.$noinline$boxUnboxByte(byte) builder (after)
+  /// CHECK: <<Input:b\d+>>       ParameterValue
+  /// CHECK: <<Boxed:l\d+>>       InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf
+  /// CHECK-NOT:                  NullCheck [<<Boxed>>]
+  /// CHECK: <<Unboxed:b\d+>>     InvokeVirtual [<<Boxed>>] method_name:java.lang.Byte.byteValue
+  /// CHECK:                      Return [<<Unboxed>>]
+
+  /// CHECK-START: byte Main.$noinline$boxUnboxByte(byte) inliner (after)
+  /// CHECK: <<Input:b\d+>>       ParameterValue
+  /// CHECK: <<Boxed:l\d+>>       InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf
+  /// CHECK: <<Unboxed:b\d+>>     InstanceFieldGet [<<Boxed>>] field_name:java.lang.Byte.value
+  /// CHECK:                      Return [<<Unboxed>>]
+
+  /// CHECK-START: byte Main.$noinline$boxUnboxByte(byte) instruction_simplifier$after_inlining (after)
+  /// CHECK: <<Input:b\d+>>       ParameterValue
+  /// CHECK:                      Return [<<Input>>]
+
+  /// CHECK-START: byte Main.$noinline$boxUnboxByte(byte) dead_code_elimination$after_inlining (after)
+  /// CHECK-NOT:                  InvokeStaticOrDirect
+  /// CHECK-NOT:                  InstanceFieldGet
+
+  public static byte $noinline$boxUnboxByte(byte value) {
+    return Byte.valueOf(value).byteValue();
+  }
+
+  /// CHECK-START: short Main.$noinline$boxUnboxShort(short) builder (after)
+  /// CHECK: <<Input:s\d+>>       ParameterValue
+  /// CHECK: <<Boxed:l\d+>>       InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Short.valueOf intrinsic:ShortValueOf
+  /// CHECK-NOT:                  NullCheck [<<Boxed>>]
+  /// CHECK: <<Unboxed:s\d+>>     InvokeVirtual [<<Boxed>>] method_name:java.lang.Short.shortValue
+  /// CHECK:                      Return [<<Unboxed>>]
+
+  /// CHECK-START: short Main.$noinline$boxUnboxShort(short) inliner (after)
+  /// CHECK: <<Input:s\d+>>       ParameterValue
+  /// CHECK: <<Boxed:l\d+>>       InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Short.valueOf intrinsic:ShortValueOf
+  /// CHECK: <<Unboxed:s\d+>>     InstanceFieldGet [<<Boxed>>] field_name:java.lang.Short.value
+  /// CHECK:                      Return [<<Unboxed>>]
+
+  /// CHECK-START: short Main.$noinline$boxUnboxShort(short) instruction_simplifier$after_inlining (after)
+  /// CHECK: <<Input:s\d+>>       ParameterValue
+  /// CHECK:                      Return [<<Input>>]
+
+  /// CHECK-START: short Main.$noinline$boxUnboxShort(short) dead_code_elimination$after_inlining (after)
+  /// CHECK-NOT:                  InvokeStaticOrDirect
+  /// CHECK-NOT:                  InstanceFieldGet
+
+  public static short $noinline$boxUnboxShort(short value) {
+    return Short.valueOf(value).shortValue();
+  }
+
+  /// CHECK-START: char Main.$noinline$boxUnboxCharacter(char) builder (after)
+  /// CHECK: <<Input:c\d+>>       ParameterValue
+  /// CHECK: <<Boxed:l\d+>>       InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Character.valueOf intrinsic:CharacterValueOf
+  /// CHECK-NOT:                  NullCheck [<<Boxed>>]
+  /// CHECK: <<Unboxed:c\d+>>     InvokeVirtual [<<Boxed>>] method_name:java.lang.Character.charValue
+  /// CHECK:                      Return [<<Unboxed>>]
+
+  /// CHECK-START: char Main.$noinline$boxUnboxCharacter(char) inliner (after)
+  /// CHECK: <<Input:c\d+>>       ParameterValue
+  /// CHECK: <<Boxed:l\d+>>       InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Character.valueOf intrinsic:CharacterValueOf
+  /// CHECK: <<Unboxed:c\d+>>     InstanceFieldGet [<<Boxed>>] field_name:java.lang.Character.value
+  /// CHECK:                      Return [<<Unboxed>>]
+
+  /// CHECK-START: char Main.$noinline$boxUnboxCharacter(char) instruction_simplifier$after_inlining (after)
+  /// CHECK: <<Input:c\d+>>       ParameterValue
+  /// CHECK:                      Return [<<Input>>]
+
+  /// CHECK-START: char Main.$noinline$boxUnboxCharacter(char) dead_code_elimination$after_inlining (after)
+  /// CHECK-NOT:                  InvokeStaticOrDirect
+  /// CHECK-NOT:                  InstanceFieldGet
+
+  public static char $noinline$boxUnboxCharacter(char value) {
+    return Character.valueOf(value).charValue();
+  }
+
+  /// CHECK-START: int Main.$noinline$boxUnboxInteger(int) builder (after)
+  /// CHECK: <<Input:i\d+>>       ParameterValue
+  /// CHECK: <<Boxed:l\d+>>       InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Integer.valueOf intrinsic:IntegerValueOf
+  /// CHECK-NOT:                  NullCheck [<<Boxed>>]
+  /// CHECK: <<Unboxed:i\d+>>     InvokeVirtual [<<Boxed>>] method_name:java.lang.Integer.intValue
+  /// CHECK:                      Return [<<Unboxed>>]
+
+  /// CHECK-START: int Main.$noinline$boxUnboxInteger(int) inliner (after)
+  /// CHECK: <<Input:i\d+>>       ParameterValue
+  /// CHECK: <<Boxed:l\d+>>       InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Integer.valueOf intrinsic:IntegerValueOf
+  /// CHECK: <<Unboxed:i\d+>>     InstanceFieldGet [<<Boxed>>] field_name:java.lang.Integer.value
+  /// CHECK:                      Return [<<Unboxed>>]
+
+  /// CHECK-START: int Main.$noinline$boxUnboxInteger(int) instruction_simplifier$after_inlining (after)
+  /// CHECK: <<Input:i\d+>>       ParameterValue
+  /// CHECK:                      Return [<<Input>>]
+
+  /// CHECK-START: int Main.$noinline$boxUnboxInteger(int) dead_code_elimination$after_inlining (after)
+  /// CHECK-NOT:                  InvokeStaticOrDirect
+  /// CHECK-NOT:                  InstanceFieldGet
+
+  public static int $noinline$boxUnboxInteger(int value) {
+    return Integer.valueOf(value).intValue();
+  }
+
+  /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) builder (after)
+  /// CHECK-DAG: <<Input:b\d+>>   ParameterValue
+  /// CHECK-DAG: <<Boxed:l\d+>>   InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf
+  /// CHECK-DAG: <<Phi:l\d+>>     Phi
+  /// CHECK-DAG: <<NC:l\d+>>      NullCheck [<<Phi>>]
+  /// CHECK-DAG: <<Unboxed:b\d+>> InvokeVirtual [<<NC>>] method_name:java.lang.Byte.byteValue
+  /// CHECK-DAG: <<And:i\d+>>     And
+  /// CHECK-DAG:                  Return [<<And>>]
+
+  /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) instruction_simplifier (after)
+  /// CHECK-DAG: <<Input:b\d+>>   ParameterValue
+  /// CHECK-DAG: <<Boxed:l\d+>>   InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf
+  /// CHECK-DAG: <<Phi:l\d+>>     Phi
+  /// CHECK-DAG: <<NC:l\d+>>      NullCheck [<<Phi>>]
+  /// CHECK-DAG: <<Unboxed:b\d+>> InvokeVirtual [<<NC>>] method_name:java.lang.Byte.byteValue
+  /// CHECK-DAG: <<Conv:a\d+>>    TypeConversion [<<Unboxed>>]
+  /// CHECK-DAG:                  Return [<<Conv>>]
+
+  /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) inliner (after)
+  /// CHECK-DAG: <<Input:b\d+>>   ParameterValue
+  /// CHECK-DAG: <<Boxed:l\d+>>   InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf
+  /// CHECK-DAG: <<Phi:l\d+>>     Phi
+  /// CHECK-DAG: <<NC:l\d+>>      NullCheck [<<Phi>>]
+  /// CHECK-DAG: <<Unboxed:b\d+>> InstanceFieldGet [<<NC>>] field_name:java.lang.Byte.value
+  /// CHECK-DAG: <<Conv:a\d+>>    TypeConversion [<<Unboxed>>]
+  /// CHECK-DAG:                  Return [<<Conv>>]
+
+  /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Input:b\d+>>   ParameterValue
+  /// CHECK-DAG: <<Boxed:l\d+>>   InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf
+  /// CHECK-DAG: <<Phi:l\d+>>     Phi
+  /// CHECK-DAG: <<NC:l\d+>>      NullCheck [<<Phi>>]
+  /// CHECK-DAG: <<Conv:a\d+>>    InstanceFieldGet [<<NC>>] field_name:java.lang.Byte.value
+  /// CHECK-DAG:                  Return [<<Conv>>]
+
+  /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) dead_code_elimination$after_inlining (after)
+  /// CHECK-DAG: <<Input:b\d+>>   ParameterValue
+  /// CHECK-DAG: <<Boxed:l\d+>>   InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf
+  /// CHECK-DAG: <<NC:l\d+>>      NullCheck [<<Boxed>>]
+  /// CHECK-DAG: <<Conv:a\d+>>    InstanceFieldGet [<<NC>>] field_name:java.lang.Byte.value
+  /// CHECK-DAG:                  Return [<<Conv>>]
+
+  /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) instruction_simplifier$after_gvn (after)
+  /// CHECK-DAG: <<Input:b\d+>>   ParameterValue
+  /// CHECK-DAG: <<Boxed:l\d+>>   InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf
+  /// CHECK-DAG: <<Conv:a\d+>>    InstanceFieldGet [<<Boxed>>] field_name:java.lang.Byte.value
+  /// CHECK-DAG:                  Return [<<Conv>>]
+
+  /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) disassembly (after)
+  /// CHECK-DAG: <<Input:b\d+>>   ParameterValue
+  /// CHECK-DAG: <<Boxed:l\d+>>   InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf
+  /// CHECK-DAG: <<Conv:a\d+>>    InstanceFieldGet [<<Boxed>>] field_name:java.lang.Byte.value
+  /// CHECK-DAG:                  Return [<<Conv>>]
+
+  public static int $noinline$boxUnboxByteAsUint8(byte value) {
+    Byte boxed = Byte.valueOf(value);
+
+    // Hide the unboxing from the initial instruction simplifier pass or the one after inlining,
+    // so that after inlining we can merge the `TypeConversion` into `InstanceFieldGet` with
+    // a modified type. The Phi shall be eliminated by `dead_code_elimination$after_inlining`.
+    // The null check that shall be eliminated by `instruction_simplifier$after_gvn (after)`
+    Byte merged = $inline$returnTrue() ? boxed : null;
+
+    // Due to the type mismatch, we shall not simplify the unboxing. The boxing and unboxing
+    // shall be kept during subsequent simplifier passes all the way to the disassembly.
+    return merged.byteValue() & 0xff;
+  }
+
+  public static boolean $inline$returnTrue() {
+    return true;
+  }
+
   public static void main(String[] args) {
     assertEqual("42", foo(intField));
     assertEqual(foo(intField), foo(intField2));
@@ -65,6 +237,18 @@
     assertEqual("127", foo(intField127));
     assertEqual(foo(intField127), foo(intField127));
     assertEqual("128", foo(intField128));
+
+    assertEqual(42, (int) $noinline$boxUnboxByte((byte) 42));
+    assertEqual(-42, (int) $noinline$boxUnboxByte((byte) -42));
+    assertEqual(42, (int) $noinline$boxUnboxShort((short) 42));
+    assertEqual(-42, (int) $noinline$boxUnboxShort((short) -42));
+    assertEqual((int) (char) 42, (int) $noinline$boxUnboxCharacter((char) 42));
+    assertEqual((int) (char) -42, (int) $noinline$boxUnboxCharacter((char) -42));
+    assertEqual(42, $noinline$boxUnboxInteger(42));
+    assertEqual(-42, $noinline$boxUnboxInteger(-42));
+
+    assertEqual(42, $noinline$boxUnboxByteAsUint8((byte) 42));
+    assertEqual(-42 & 0xff, $noinline$boxUnboxByteAsUint8((byte) -42));
   }
 
   static void assertEqual(String a, Integer b) {
@@ -79,6 +263,12 @@
     }
   }
 
+  static void assertEqual(int a, int b) {
+    if (a != b) {
+      throw new Error("Expected " + a + ", got " + b);
+    }
+  }
+
   static int intField = 42;
   static int intField2 = 42;
   static int intField3 = 55555;