Add support for intrinsic optimizations.

Change-Id: Ib5a4224022f9360e60c09a19ac8642270a7f3b64
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 3287a0a..973bfbb 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -16,15 +16,16 @@
 
 #include "instruction_simplifier.h"
 
+#include "intrinsics.h"
 #include "mirror/class-inl.h"
 #include "scoped_thread_state_change.h"
 
 namespace art {
 
-class InstructionSimplifierVisitor : public HGraphVisitor {
+class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
  public:
   InstructionSimplifierVisitor(HGraph* graph, OptimizingCompilerStats* stats)
-      : HGraphVisitor(graph),
+      : HGraphDelegateVisitor(graph),
         stats_(stats) {}
 
   void Run();
@@ -71,6 +72,7 @@
   void VisitXor(HXor* instruction) OVERRIDE;
   void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE;
   void VisitFakeString(HFakeString* fake_string) OVERRIDE;
+  void VisitInvoke(HInvoke* invoke) OVERRIDE;
 
   bool CanEnsureNotNullAt(HInstruction* instr, HInstruction* at) const;
 
@@ -1033,4 +1035,29 @@
   instruction->GetBlock()->RemoveInstruction(instruction);
 }
 
+void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
+  if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) {
+    HInstruction* argument = instruction->InputAt(1);
+    HInstruction* receiver = instruction->InputAt(0);
+    if (receiver == argument) {
+      // Because String.equals is an instance call, the receiver is
+      // a null check if we don't know it's null. The argument however, will
+      // be the actual object. So we cannot end up in a situation where both
+      // are equal but could be null.
+      DCHECK(CanEnsureNotNullAt(argument, instruction));
+      instruction->ReplaceWith(GetGraph()->GetIntConstant(1));
+      instruction->GetBlock()->RemoveInstruction(instruction);
+    } else {
+      StringEqualsOptimizations optimizations(instruction);
+      if (CanEnsureNotNullAt(argument, instruction)) {
+        optimizations.SetArgumentNotNull();
+      }
+      ScopedObjectAccess soa(Thread::Current());
+      if (argument->GetReferenceTypeInfo().IsStringClass()) {
+        optimizations.SetArgumentIsString();
+      }
+    }
+  }
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index d1a17b6..12e79fa 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -116,6 +116,59 @@
   DISALLOW_COPY_AND_ASSIGN(IntrinsicVisitor);
 };
 
+#define GENERIC_OPTIMIZATION(name, bit)                \
+ public:                                               \
+  void Set##name() { SetBit(k##name); }                \
+  bool Get##name() const { return IsBitSet(k##name); } \
+ private:                                              \
+  static constexpr int k##name = bit
+
+class IntrinsicOptimizations : public ValueObject {
+ public:
+  IntrinsicOptimizations(HInvoke* invoke) : value_(invoke->GetIntrinsicOptimizations()) {}
+  IntrinsicOptimizations(const HInvoke& invoke) : value_(invoke.GetIntrinsicOptimizations()) {}
+
+  static constexpr int kNumberOfGenericOptimizations = 2;
+  GENERIC_OPTIMIZATION(DoesNotNeedDexCache, 0);
+  GENERIC_OPTIMIZATION(DoesNotNeedEnvironment, 1);
+
+ protected:
+  bool IsBitSet(uint32_t bit) const {
+    return (*value_ & (1 << bit)) != 0u;
+  }
+
+  void SetBit(uint32_t bit) {
+    *(const_cast<uint32_t*>(value_)) |= (1 << bit);
+  }
+
+ private:
+  const uint32_t *value_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicOptimizations);
+};
+
+#undef GENERIC_OPTIMIZATION
+
+#define INTRINSIC_OPTIMIZATION(name, bit)                             \
+ public:                                                              \
+  void Set##name() { SetBit(k##name); }                               \
+  bool Get##name() const { return IsBitSet(k##name); }                \
+ private:                                                             \
+  static constexpr int k##name = bit + kNumberOfGenericOptimizations
+
+class StringEqualsOptimizations : public IntrinsicOptimizations {
+ public:
+  StringEqualsOptimizations(HInvoke* invoke) : IntrinsicOptimizations(invoke) {}
+
+  INTRINSIC_OPTIMIZATION(ArgumentNotNull, 0);
+  INTRINSIC_OPTIMIZATION(ArgumentIsString, 1);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StringEqualsOptimizations);
+};
+
+#undef INTRISIC_OPTIMIZATION
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_INTRINSICS_H_
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 318d3a6..263c375 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1054,17 +1054,22 @@
   // Note that the null check must have been done earlier.
   DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
 
-  // Check if input is null, return false if it is.
-  __ testl(arg, arg);
-  __ j(kEqual, &return_false);
+  StringEqualsOptimizations optimizations(invoke);
+  if (!optimizations.GetArgumentNotNull()) {
+    // Check if input is null, return false if it is.
+    __ testl(arg, arg);
+    __ j(kEqual, &return_false);
+  }
 
   // Instanceof check for the argument by comparing class fields.
   // All string objects must have the same type since String cannot be subclassed.
   // Receiver must be a string object, so its class field is equal to all strings' class fields.
   // If the argument is a string object, its class field must be equal to receiver's class field.
-  __ movl(ecx, Address(str, class_offset));
-  __ cmpl(ecx, Address(arg, class_offset));
-  __ j(kNotEqual, &return_false);
+  if (!optimizations.GetArgumentIsString()) {
+    __ movl(ecx, Address(str, class_offset));
+    __ cmpl(ecx, Address(arg, class_offset));
+    __ j(kNotEqual, &return_false);
+  }
 
   // Reference equality check, return true if same reference.
   __ cmpl(str, arg);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 989970f..d35db19 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -21,6 +21,7 @@
 #include "base/bit_vector-inl.h"
 #include "base/bit_utils.h"
 #include "base/stl_util.h"
+#include "intrinsics.h"
 #include "mirror/class-inl.h"
 #include "scoped_thread_state_change.h"
 
@@ -1873,6 +1874,35 @@
   return false;
 }
 
+void HInvoke::SetIntrinsic(Intrinsics intrinsic,
+                           IntrinsicNeedsEnvironmentOrCache needs_env_or_cache) {
+  intrinsic_ = intrinsic;
+  IntrinsicOptimizations opt(this);
+  if (needs_env_or_cache == kNoEnvironmentOrCache) {
+    opt.SetDoesNotNeedDexCache();
+    opt.SetDoesNotNeedEnvironment();
+  }
+}
+
+bool HInvoke::NeedsEnvironment() const {
+  if (!IsIntrinsic()) {
+    return true;
+  }
+  IntrinsicOptimizations opt(*this);
+  return !opt.GetDoesNotNeedEnvironment();
+}
+
+bool HInvokeStaticOrDirect::NeedsDexCache() const {
+  if (IsRecursive() || IsStringInit()) {
+    return false;
+  }
+  if (!IsIntrinsic()) {
+    return true;
+  }
+  IntrinsicOptimizations opt(*this);
+  return !opt.GetDoesNotNeedDexCache();
+}
+
 void HInstruction::RemoveEnvironmentUsers() {
   for (HUseIterator<HEnvironment*> use_it(GetEnvUses()); !use_it.Done(); use_it.Advance()) {
     HUseListNode<HEnvironment*>* user_node = use_it.Current();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 849f876..b8b9851 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1656,6 +1656,10 @@
     return GetTypeHandle()->IsObjectClass();
   }
 
+  bool IsStringClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
+    return IsValid() && GetTypeHandle()->IsStringClass();
+  }
+
   bool IsObjectArray() const SHARED_REQUIRES(Locks::mutator_lock_) {
     DCHECK(IsValid());
     return IsArrayClass() && GetTypeHandle()->GetComponentType()->IsObjectClass();
@@ -3034,11 +3038,7 @@
  public:
   size_t InputCount() const OVERRIDE { return inputs_.size(); }
 
-  // Runtime needs to walk the stack, so Dex -> Dex calls need to
-  // know their environment.
-  bool NeedsEnvironment() const OVERRIDE {
-    return needs_environment_or_cache_ == kNeedsEnvironmentOrCache;
-  }
+  bool NeedsEnvironment() const OVERRIDE;
 
   void SetArgumentAt(size_t index, HInstruction* argument) {
     SetRawInputAt(index, argument);
@@ -3062,10 +3062,7 @@
     return intrinsic_;
   }
 
-  void SetIntrinsic(Intrinsics intrinsic, IntrinsicNeedsEnvironmentOrCache needs_env_or_cache) {
-    intrinsic_ = intrinsic;
-    needs_environment_or_cache_ = needs_env_or_cache;
-  }
+  void SetIntrinsic(Intrinsics intrinsic, IntrinsicNeedsEnvironmentOrCache needs_env_or_cache);
 
   bool IsFromInlinedInvoke() const {
     return GetEnvironment()->GetParent() != nullptr;
@@ -3073,6 +3070,16 @@
 
   bool CanThrow() const OVERRIDE { return true; }
 
+  uint32_t* GetIntrinsicOptimizations() {
+    return &intrinsic_optimizations_;
+  }
+
+  const uint32_t* GetIntrinsicOptimizations() const {
+    return &intrinsic_optimizations_;
+  }
+
+  bool IsIntrinsic() const { return intrinsic_ != Intrinsics::kNone; }
+
   DECLARE_INSTRUCTION(Invoke);
 
  protected:
@@ -3092,7 +3099,7 @@
       dex_method_index_(dex_method_index),
       original_invoke_type_(original_invoke_type),
       intrinsic_(Intrinsics::kNone),
-      needs_environment_or_cache_(kNeedsEnvironmentOrCache) {
+      intrinsic_optimizations_(0) {
   }
 
   const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
@@ -3111,7 +3118,9 @@
   const uint32_t dex_method_index_;
   const InvokeType original_invoke_type_;
   Intrinsics intrinsic_;
-  IntrinsicNeedsEnvironmentOrCache needs_environment_or_cache_;
+
+  // A magic word holding optimizations for intrinsics. See intrinsics.h.
+  uint32_t intrinsic_optimizations_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(HInvoke);
@@ -3259,10 +3268,7 @@
   MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; }
   CodePtrLocation GetCodePtrLocation() const { return dispatch_info_.code_ptr_location; }
   bool IsRecursive() const { return GetMethodLoadKind() == MethodLoadKind::kRecursive; }
-  bool NeedsDexCache() const OVERRIDE {
-    if (intrinsic_ != Intrinsics::kNone) { return needs_environment_or_cache_; }
-    return !IsRecursive() && !IsStringInit();
-  }
+  bool NeedsDexCache() const OVERRIDE;
   bool IsStringInit() const { return GetMethodLoadKind() == MethodLoadKind::kStringInit; }
   uint32_t GetCurrentMethodInputIndex() const { return GetNumberOfArguments(); }
   bool HasMethodAddress() const { return GetMethodLoadKind() == MethodLoadKind::kDirectAddress; }
diff --git a/test/536-checker-intrinsic-optimization/expected.txt b/test/536-checker-intrinsic-optimization/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/536-checker-intrinsic-optimization/expected.txt
diff --git a/test/536-checker-intrinsic-optimization/info.txt b/test/536-checker-intrinsic-optimization/info.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/536-checker-intrinsic-optimization/info.txt
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
new file mode 100644
index 0000000..1b784ae
--- /dev/null
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+public class Main {
+  public static void main(String[] args) {
+    stringEqualsSame();
+    stringArgumentNotNull("Foo");
+  }
+
+  /// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before)
+  /// CHECK:      InvokeStaticOrDirect
+
+  /// CHECK-START: boolean Main.stringEqualsSame() register (before)
+  /// CHECK:      <<Const1:i\d+>> IntConstant 1
+  /// CHECK:      Return [<<Const1>>]
+
+  /// CHECK-START: boolean Main.stringEqualsSame() register (before)
+  /// CHECK-NOT:  InvokeStaticOrDirect
+  public static boolean stringEqualsSame() {
+    return $inline$callStringEquals("obj", "obj");
+  }
+
+  /// CHECK-START: boolean Main.stringEqualsNull() register (after)
+  /// CHECK:      <<Invoke:z\d+>> InvokeStaticOrDirect
+  /// CHECK:      Return [<<Invoke>>]
+  public static boolean stringEqualsNull() {
+    String o = (String)myObject;
+    return $inline$callStringEquals(o, o);
+  }
+
+  public static boolean $inline$callStringEquals(String a, String b) {
+    return a.equals(b);
+  }
+
+  /// CHECK-START-X86: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
+  /// CHECK:          InvokeStaticOrDirect
+  /// CHECK-NOT:      test
+  public static boolean stringArgumentNotNull(Object obj) {
+    obj.getClass();
+    return "foo".equals(obj);
+  }
+
+  // Test is very brittle as it depends on the order we emit instructions.
+  /// CHECK-START-X86: boolean Main.stringArgumentIsString() disassembly (after)
+  /// CHECK:      InvokeStaticOrDirect
+  /// CHECK:      test
+  /// CHECK:      jz/eq
+  // Check that we don't try to compare the classes.
+  /// CHECK-NOT:  mov
+  /// CHECK:      cmp
+  public static boolean stringArgumentIsString() {
+    return "foo".equals(myString);
+  }
+
+  static String myString;
+  static Object myObject;
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index a103eac..a18147d 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -468,6 +468,7 @@
     530-checker-regression-reftype-final \
     532-checker-nonnull-arrayset \
     534-checker-bce-deoptimization \
+    536-checker-intrinsic-optimization \
 
 ifeq (mips,$(TARGET_ARCH))
   ifneq (,$(filter optimizing,$(COMPILER_TYPES)))