OSDN Git Service

Recognize getClass() in RTP.
authorNicolas Geoffray <ngeoffray@google.com>
Mon, 19 Dec 2016 09:18:07 +0000 (09:18 +0000)
committerNicolas Geoffray <ngeoffray@google.com>
Tue, 10 Jan 2017 14:11:32 +0000 (14:11 +0000)
Also always keep around the resolved field
in related HInstructions to avoid resolving it again
and again.

Test: test-art-host, 631-checker-get-class

Change-Id: I3bc6be11f3eb175c635e746006f39865947e0669

compiler/optimizing/gvn_test.cc
compiler/optimizing/inliner.cc
compiler/optimizing/instruction_builder.cc
compiler/optimizing/instruction_simplifier.cc
compiler/optimizing/licm_test.cc
compiler/optimizing/nodes.h
compiler/optimizing/reference_type_propagation.cc
compiler/optimizing/register_allocator_test.cc
test/631-checker-get-class/expected.txt [new file with mode: 0644]
test/631-checker-get-class/info.txt [new file with mode: 0644]
test/631-checker-get-class/src/Main.java [new file with mode: 0644]

index 437d35c..f8d37bd 100644 (file)
@@ -28,7 +28,6 @@ class GVNTest : public CommonCompilerTest {};
 TEST_F(GVNTest, LocalFieldElimination) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
 
   HGraph* graph = CreateGraph(&allocator);
   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
@@ -45,53 +44,53 @@ TEST_F(GVNTest, LocalFieldElimination) {
   entry->AddSuccessor(block);
 
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   HInstruction* to_remove = block->GetLastInstruction();
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(43),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   HInstruction* different_offset = block->GetLastInstruction();
   // Kill the value.
   block->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
                                                            parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   HInstruction* use_after_kill = block->GetLastInstruction();
   block->AddInstruction(new (&allocator) HExit());
@@ -113,7 +112,6 @@ TEST_F(GVNTest, LocalFieldElimination) {
 TEST_F(GVNTest, GlobalFieldElimination) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
 
   HGraph* graph = CreateGraph(&allocator);
   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
@@ -129,13 +127,13 @@ TEST_F(GVNTest, GlobalFieldElimination) {
   graph->AddBlock(block);
   entry->AddSuccessor(block);
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimBoolean,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
 
   block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
@@ -152,33 +150,33 @@ TEST_F(GVNTest, GlobalFieldElimination) {
   else_->AddSuccessor(join);
 
   then->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                          nullptr,
                                                           Primitive::kPrimBoolean,
                                                           MemberOffset(42),
                                                           false,
                                                           kUnknownFieldIndex,
                                                           kUnknownClassDefIndex,
                                                           graph->GetDexFile(),
-                                                          dex_cache,
                                                           0));
   then->AddInstruction(new (&allocator) HGoto());
   else_->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimBoolean,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   else_->AddInstruction(new (&allocator) HGoto());
   join->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                          nullptr,
                                                           Primitive::kPrimBoolean,
                                                           MemberOffset(42),
                                                           false,
                                                           kUnknownFieldIndex,
                                                           kUnknownClassDefIndex,
                                                           graph->GetDexFile(),
-                                                          dex_cache,
                                                           0));
   join->AddInstruction(new (&allocator) HExit());
 
@@ -196,7 +194,6 @@ TEST_F(GVNTest, GlobalFieldElimination) {
 TEST_F(GVNTest, LoopFieldElimination) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
 
   HGraph* graph = CreateGraph(&allocator);
   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
@@ -213,13 +210,13 @@ TEST_F(GVNTest, LoopFieldElimination) {
   graph->AddBlock(block);
   entry->AddSuccessor(block);
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimBoolean,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   block->AddInstruction(new (&allocator) HGoto());
 
@@ -236,13 +233,13 @@ TEST_F(GVNTest, LoopFieldElimination) {
   loop_body->AddSuccessor(loop_header);
 
   loop_header->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                                 nullptr,
                                                                  Primitive::kPrimBoolean,
                                                                  MemberOffset(42),
                                                                  false,
                                                                  kUnknownFieldIndex,
                                                                  kUnknownClassDefIndex,
                                                                  graph->GetDexFile(),
-                                                                 dex_cache,
                                                                  0));
   HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
   loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
@@ -251,35 +248,35 @@ TEST_F(GVNTest, LoopFieldElimination) {
   // and the body to be GVN'ed.
   loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
                                                                parameter,
+                                                               nullptr,
                                                                Primitive::kPrimBoolean,
                                                                MemberOffset(42),
                                                                false,
                                                                kUnknownFieldIndex,
                                                                kUnknownClassDefIndex,
                                                                graph->GetDexFile(),
-                                                               dex_cache,
                                                                0));
   HInstruction* field_set = loop_body->GetLastInstruction();
   loop_body->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                               nullptr,
                                                                Primitive::kPrimBoolean,
                                                                MemberOffset(42),
                                                                false,
                                                                kUnknownFieldIndex,
                                                                kUnknownClassDefIndex,
                                                                graph->GetDexFile(),
-                                                               dex_cache,
                                                                0));
   HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
   loop_body->AddInstruction(new (&allocator) HGoto());
 
   exit->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                          nullptr,
                                                           Primitive::kPrimBoolean,
                                                           MemberOffset(42),
                                                           false,
                                                           kUnknownFieldIndex,
                                                           kUnknownClassDefIndex,
                                                           graph->GetDexFile(),
-                                                          dex_cache,
                                                           0));
   HInstruction* field_get_in_exit = exit->GetLastInstruction();
   exit->AddInstruction(new (&allocator) HExit());
@@ -319,7 +316,6 @@ TEST_F(GVNTest, LoopFieldElimination) {
 TEST_F(GVNTest, LoopSideEffects) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
 
   static const SideEffects kCanTriggerGC = SideEffects::CanTriggerGC();
 
@@ -376,13 +372,13 @@ TEST_F(GVNTest, LoopSideEffects) {
     // Make one block with a side effect.
     entry->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
                                                              parameter,
+                                                             nullptr,
                                                              Primitive::kPrimNot,
                                                              MemberOffset(42),
                                                              false,
                                                              kUnknownFieldIndex,
                                                              kUnknownClassDefIndex,
                                                              graph->GetDexFile(),
-                                                             dex_cache,
                                                              0));
 
     SideEffectsAnalysis side_effects(graph);
@@ -401,13 +397,13 @@ TEST_F(GVNTest, LoopSideEffects) {
     outer_loop_body->InsertInstructionBefore(
         new (&allocator) HInstanceFieldSet(parameter,
                                            parameter,
+                                           nullptr,
                                            Primitive::kPrimNot,
                                            MemberOffset(42),
                                            false,
                                            kUnknownFieldIndex,
                                            kUnknownClassDefIndex,
                                            graph->GetDexFile(),
-                                           dex_cache,
                                            0),
         outer_loop_body->GetLastInstruction());
 
@@ -427,13 +423,13 @@ TEST_F(GVNTest, LoopSideEffects) {
     inner_loop_body->InsertInstructionBefore(
         new (&allocator) HInstanceFieldSet(parameter,
                                            parameter,
+                                           nullptr,
                                            Primitive::kPrimNot,
                                            MemberOffset(42),
                                            false,
                                            kUnknownFieldIndex,
                                            kUnknownClassDefIndex,
                                            graph->GetDexFile(),
-                                           dex_cache,
                                            0),
         inner_loop_body->GetLastInstruction());
 
index d847879..1bf1fd4 100644 (file)
@@ -428,13 +428,13 @@ HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker,
   DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
   HInstanceFieldGet* result = new (graph_->GetArena()) HInstanceFieldGet(
       receiver,
+      field,
       Primitive::kPrimNot,
       field->GetOffset(),
       field->IsVolatile(),
       field->GetDexFieldIndex(),
       field->GetDeclaringClass()->GetDexClassDefIndex(),
       *field->GetDexFile(),
-      handles_->NewHandle(field->GetDexCache()),
       dex_pc);
   // The class of a field is effectively final, and does not have any memory dependencies.
   result->SetSideEffects(SideEffects::None());
@@ -613,6 +613,9 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
     } else {
       one_target_inlined = true;
 
+      VLOG(compiler) << "Polymorphic call to " << ArtMethod::PrettyMethod(resolved_method)
+                     << " has inlined " << ArtMethod::PrettyMethod(method);
+
       // If we have inlined all targets before, and this receiver is the last seen,
       // we deoptimize instead of keeping the original invoke instruction.
       bool deoptimize = all_targets_inlined &&
@@ -650,6 +653,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
                    << " of its targets could be inlined";
     return false;
   }
+
   MaybeRecordStat(kInlinedPolymorphicCall);
 
   // Run type propagation to get the guards typed.
@@ -1150,13 +1154,13 @@ HInstanceFieldGet* HInliner::CreateInstanceFieldGet(Handle<mirror::DexCache> dex
   DCHECK(resolved_field != nullptr);
   HInstanceFieldGet* iget = new (graph_->GetArena()) HInstanceFieldGet(
       obj,
+      resolved_field,
       resolved_field->GetTypeAsPrimitiveType(),
       resolved_field->GetOffset(),
       resolved_field->IsVolatile(),
       field_index,
       resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
       *dex_cache->GetDexFile(),
-      dex_cache,
       // Read barrier generates a runtime call in slow path and we need a valid
       // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
       /* dex_pc */ 0);
@@ -1179,13 +1183,13 @@ HInstanceFieldSet* HInliner::CreateInstanceFieldSet(Handle<mirror::DexCache> dex
   HInstanceFieldSet* iput = new (graph_->GetArena()) HInstanceFieldSet(
       obj,
       value,
+      resolved_field,
       resolved_field->GetTypeAsPrimitiveType(),
       resolved_field->GetOffset(),
       resolved_field->IsVolatile(),
       field_index,
       resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
       *dex_cache->GetDexFile(),
-      dex_cache,
       // Read barrier generates a runtime call in slow path and we need a valid
       // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
       /* dex_pc */ 0);
@@ -1559,6 +1563,13 @@ bool HInliner::ReturnTypeMoreSpecific(HInvoke* invoke_instruction,
                                     /* declared_can_be_null */ true,
                                     return_replacement)) {
         return true;
+      } else if (return_replacement->IsInstanceFieldGet()) {
+        HInstanceFieldGet* field_get = return_replacement->AsInstanceFieldGet();
+        ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+        if (field_get->GetFieldInfo().GetField() ==
+              class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0)) {
+          return true;
+        }
       }
     } else if (return_replacement->IsInstanceOf()) {
       // Inlining InstanceOf into an If may put a tighter bound on reference types.
index af8e2c8..768b1d8 100644 (file)
@@ -1235,13 +1235,13 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio
       uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
       field_set = new (arena_) HInstanceFieldSet(object,
                                                  value,
+                                                 resolved_field,
                                                  field_type,
                                                  resolved_field->GetOffset(),
                                                  resolved_field->IsVolatile(),
                                                  field_index,
                                                  class_def_index,
                                                  *dex_file_,
-                                                 dex_compilation_unit_->GetDexCache(),
                                                  dex_pc);
     }
     AppendInstruction(field_set);
@@ -1256,13 +1256,13 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio
     } else {
       uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
       field_get = new (arena_) HInstanceFieldGet(object,
+                                                 resolved_field,
                                                  field_type,
                                                  resolved_field->GetOffset(),
                                                  resolved_field->IsVolatile(),
                                                  field_index,
                                                  class_def_index,
                                                  *dex_file_,
-                                                 dex_compilation_unit_->GetDexCache(),
                                                  dex_pc);
     }
     AppendInstruction(field_get);
@@ -1311,9 +1311,9 @@ bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) c
 }
 
 void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
-                                                     uint32_t dex_pc,
-                                                     bool is_put,
-                                                     Primitive::Type field_type) {
+                                                           uint32_t dex_pc,
+                                                           bool is_put,
+                                                           Primitive::Type field_type) {
   uint32_t source_or_dest_reg = instruction.VRegA_21c();
   uint16_t field_index = instruction.VRegB_21c();
 
@@ -1400,23 +1400,23 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction,
     DCHECK_EQ(HPhi::ToPhiType(value->GetType()), HPhi::ToPhiType(field_type));
     AppendInstruction(new (arena_) HStaticFieldSet(cls,
                                                    value,
+                                                   resolved_field,
                                                    field_type,
                                                    resolved_field->GetOffset(),
                                                    resolved_field->IsVolatile(),
                                                    field_index,
                                                    class_def_index,
                                                    *dex_file_,
-                                                   dex_cache_,
                                                    dex_pc));
   } else {
     AppendInstruction(new (arena_) HStaticFieldGet(cls,
+                                                   resolved_field,
                                                    field_type,
                                                    resolved_field->GetOffset(),
                                                    resolved_field->IsVolatile(),
                                                    field_index,
                                                    class_def_index,
                                                    *dex_file_,
-                                                   dex_cache_,
                                                    dex_pc));
     UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
   }
index 439e3b6..1d3c5bf 100644 (file)
@@ -1118,7 +1118,66 @@ void InstructionSimplifierVisitor::VisitAboveOrEqual(HAboveOrEqual* condition) {
   VisitCondition(condition);
 }
 
+// Recognize the following pattern:
+// obj.getClass() ==/!= Foo.class
+// And replace it with a constant value if the type of `obj` is statically known.
+static bool RecognizeAndSimplifyClassCheck(HCondition* condition) {
+  HInstruction* input_one = condition->InputAt(0);
+  HInstruction* input_two = condition->InputAt(1);
+  HLoadClass* load_class = input_one->IsLoadClass()
+      ? input_one->AsLoadClass()
+      : input_two->AsLoadClass();
+  if (load_class == nullptr) {
+    return false;
+  }
+
+  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+  if (!class_rti.IsValid()) {
+    // Unresolved class.
+    return false;
+  }
+
+  HInstanceFieldGet* field_get = (load_class == input_one)
+      ? input_two->AsInstanceFieldGet()
+      : input_one->AsInstanceFieldGet();
+  if (field_get == nullptr) {
+    return false;
+  }
+
+  HInstruction* receiver = field_get->InputAt(0);
+  ReferenceTypeInfo receiver_type = receiver->GetReferenceTypeInfo();
+  if (!receiver_type.IsExact()) {
+    return false;
+  }
+
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
+    DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
+    if (field_get->GetFieldInfo().GetField() != field) {
+      return false;
+    }
+
+    // We can replace the compare.
+    int value = 0;
+    if (receiver_type.IsEqual(class_rti)) {
+      value = condition->IsEqual() ? 1 : 0;
+    } else {
+      value = condition->IsNotEqual() ? 1 : 0;
+    }
+    condition->ReplaceWith(condition->GetBlock()->GetGraph()->GetIntConstant(value));
+    return true;
+  }
+}
+
 void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
+  if (condition->IsEqual() || condition->IsNotEqual()) {
+    if (RecognizeAndSimplifyClassCheck(condition)) {
+      return;
+    }
+  }
+
   // Reverse condition if left is constant. Our code generators prefer constant
   // on the right hand side.
   if (condition->GetLeft()->IsConstant() && !condition->GetRight()->IsConstant()) {
index 8c34dc6..5bcfa4c 100644 (file)
@@ -111,20 +111,19 @@ TEST_F(LICMTest, FieldHoisting) {
   BuildLoop();
 
   // Populate the loop with instructions: set/get field with different types.
-  ScopedNullHandle<mirror::DexCache> dex_cache;
   HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_,
+                                                                nullptr,
                                                                 Primitive::kPrimLong,
                                                                 MemberOffset(10),
                                                                 false,
                                                                 kUnknownFieldIndex,
                                                                 kUnknownClassDefIndex,
                                                                 graph_->GetDexFile(),
-                                                                dex_cache,
                                                                 0);
   loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
   HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
-      parameter_, int_constant_, Primitive::kPrimInt, MemberOffset(20),
-      false, kUnknownFieldIndex, kUnknownClassDefIndex, graph_->GetDexFile(), dex_cache, 0);
+      parameter_, int_constant_, nullptr, Primitive::kPrimInt, MemberOffset(20),
+      false, kUnknownFieldIndex, kUnknownClassDefIndex, graph_->GetDexFile(), 0);
   loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
 
   EXPECT_EQ(get_field->GetBlock(), loop_body_);
@@ -140,24 +139,24 @@ TEST_F(LICMTest, NoFieldHoisting) {
   // Populate the loop with instructions: set/get field with same types.
   ScopedNullHandle<mirror::DexCache> dex_cache;
   HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_,
+                                                                nullptr,
                                                                 Primitive::kPrimLong,
                                                                 MemberOffset(10),
                                                                 false,
                                                                 kUnknownFieldIndex,
                                                                 kUnknownClassDefIndex,
                                                                 graph_->GetDexFile(),
-                                                                dex_cache,
                                                                 0);
   loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
   HInstruction* set_field = new (&allocator_) HInstanceFieldSet(parameter_,
                                                                 get_field,
+                                                                nullptr,
                                                                 Primitive::kPrimLong,
                                                                 MemberOffset(10),
                                                                 false,
                                                                 kUnknownFieldIndex,
                                                                 kUnknownClassDefIndex,
                                                                 graph_->GetDexFile(),
-                                                                dex_cache,
                                                                 0);
   loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
 
index afa17ce..6c4f3dd 100644 (file)
@@ -171,6 +171,7 @@ class HInstructionList : public ValueObject {
   friend class HGraph;
   friend class HInstruction;
   friend class HInstructionIterator;
+  friend class HInstructionIteratorHandleChanges;
   friend class HBackwardInstructionIterator;
 
   DISALLOW_COPY_AND_ASSIGN(HInstructionList);
@@ -2312,6 +2313,9 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
 };
 std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs);
 
+// Iterates over the instructions, while preserving the next instruction
+// in case the current instruction gets removed from the list by the user
+// of this iterator.
 class HInstructionIterator : public ValueObject {
  public:
   explicit HInstructionIterator(const HInstructionList& instructions)
@@ -2333,6 +2337,28 @@ class HInstructionIterator : public ValueObject {
   DISALLOW_COPY_AND_ASSIGN(HInstructionIterator);
 };
 
+// Iterates over the instructions without saving the next instruction,
+// therefore handling changes in the graph potentially made by the user
+// of this iterator.
+class HInstructionIteratorHandleChanges : public ValueObject {
+ public:
+  explicit HInstructionIteratorHandleChanges(const HInstructionList& instructions)
+      : instruction_(instructions.first_instruction_) {
+  }
+
+  bool Done() const { return instruction_ == nullptr; }
+  HInstruction* Current() const { return instruction_; }
+  void Advance() {
+    instruction_ = instruction_->GetNext();
+  }
+
+ private:
+  HInstruction* instruction_;
+
+  DISALLOW_COPY_AND_ASSIGN(HInstructionIteratorHandleChanges);
+};
+
+
 class HBackwardInstructionIterator : public ValueObject {
  public:
   explicit HBackwardInstructionIterator(const HInstructionList& instructions)
@@ -5056,60 +5082,62 @@ class HNullCheck FINAL : public HExpression<1> {
   DISALLOW_COPY_AND_ASSIGN(HNullCheck);
 };
 
+// Embeds an ArtField and all the information required by the compiler. We cache
+// that information to avoid requiring the mutator lock every time we need it.
 class FieldInfo : public ValueObject {
  public:
-  FieldInfo(MemberOffset field_offset,
+  FieldInfo(ArtField* field,
+            MemberOffset field_offset,
             Primitive::Type field_type,
             bool is_volatile,
             uint32_t index,
             uint16_t declaring_class_def_index,
-            const DexFile& dex_file,
-            Handle<mirror::DexCache> dex_cache)
-      : field_offset_(field_offset),
+            const DexFile& dex_file)
+      : field_(field),
+        field_offset_(field_offset),
         field_type_(field_type),
         is_volatile_(is_volatile),
         index_(index),
         declaring_class_def_index_(declaring_class_def_index),
-        dex_file_(dex_file),
-        dex_cache_(dex_cache) {}
+        dex_file_(dex_file) {}
 
+  ArtField* GetField() const { return field_; }
   MemberOffset GetFieldOffset() const { return field_offset_; }
   Primitive::Type GetFieldType() const { return field_type_; }
   uint32_t GetFieldIndex() const { return index_; }
   uint16_t GetDeclaringClassDefIndex() const { return declaring_class_def_index_;}
   const DexFile& GetDexFile() const { return dex_file_; }
   bool IsVolatile() const { return is_volatile_; }
-  Handle<mirror::DexCache> GetDexCache() const { return dex_cache_; }
 
  private:
+  ArtField* const field_;
   const MemberOffset field_offset_;
   const Primitive::Type field_type_;
   const bool is_volatile_;
   const uint32_t index_;
   const uint16_t declaring_class_def_index_;
   const DexFile& dex_file_;
-  const Handle<mirror::DexCache> dex_cache_;
 };
 
 class HInstanceFieldGet FINAL : public HExpression<1> {
  public:
   HInstanceFieldGet(HInstruction* value,
+                    ArtField* field,
                     Primitive::Type field_type,
                     MemberOffset field_offset,
                     bool is_volatile,
                     uint32_t field_idx,
                     uint16_t declaring_class_def_index,
                     const DexFile& dex_file,
-                    Handle<mirror::DexCache> dex_cache,
                     uint32_t dex_pc)
       : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
-        field_info_(field_offset,
+        field_info_(field,
+                    field_offset,
                     field_type,
                     is_volatile,
                     field_idx,
                     declaring_class_def_index,
-                    dex_file,
-                    dex_cache) {
+                    dex_file) {
     SetRawInputAt(0, value);
   }
 
@@ -5145,22 +5173,22 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> {
  public:
   HInstanceFieldSet(HInstruction* object,
                     HInstruction* value,
+                    ArtField* field,
                     Primitive::Type field_type,
                     MemberOffset field_offset,
                     bool is_volatile,
                     uint32_t field_idx,
                     uint16_t declaring_class_def_index,
                     const DexFile& dex_file,
-                    Handle<mirror::DexCache> dex_cache,
                     uint32_t dex_pc)
       : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
-        field_info_(field_offset,
+        field_info_(field,
+                    field_offset,
                     field_type,
                     is_volatile,
                     field_idx,
                     declaring_class_def_index,
-                    dex_file,
-                    dex_cache) {
+                    dex_file) {
     SetPackedFlag<kFlagValueCanBeNull>(true);
     SetRawInputAt(0, object);
     SetRawInputAt(1, value);
@@ -5949,22 +5977,22 @@ class HClinitCheck FINAL : public HExpression<1> {
 class HStaticFieldGet FINAL : public HExpression<1> {
  public:
   HStaticFieldGet(HInstruction* cls,
+                  ArtField* field,
                   Primitive::Type field_type,
                   MemberOffset field_offset,
                   bool is_volatile,
                   uint32_t field_idx,
                   uint16_t declaring_class_def_index,
                   const DexFile& dex_file,
-                  Handle<mirror::DexCache> dex_cache,
                   uint32_t dex_pc)
       : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
-        field_info_(field_offset,
+        field_info_(field,
+                    field_offset,
                     field_type,
                     is_volatile,
                     field_idx,
                     declaring_class_def_index,
-                    dex_file,
-                    dex_cache) {
+                    dex_file) {
     SetRawInputAt(0, cls);
   }
 
@@ -5997,22 +6025,22 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> {
  public:
   HStaticFieldSet(HInstruction* cls,
                   HInstruction* value,
+                  ArtField* field,
                   Primitive::Type field_type,
                   MemberOffset field_offset,
                   bool is_volatile,
                   uint32_t field_idx,
                   uint16_t declaring_class_def_index,
                   const DexFile& dex_file,
-                  Handle<mirror::DexCache> dex_cache,
                   uint32_t dex_pc)
       : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
-        field_info_(field_offset,
+        field_info_(field,
+                    field_offset,
                     field_type,
                     is_volatile,
                     field_idx,
                     declaring_class_def_index,
-                    dex_file,
-                    dex_cache) {
+                    dex_file) {
     SetPackedFlag<kFlagValueCanBeNull>(true);
     SetRawInputAt(0, cls);
     SetRawInputAt(1, value);
index 33b3875..f8a4469 100644 (file)
@@ -76,6 +76,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
       worklist_(worklist),
       is_first_run_(is_first_run) {}
 
+  void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE;
   void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
   void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
   void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
@@ -151,38 +152,6 @@ void ReferenceTypePropagation::Visit(HInstruction* instruction) {
   instruction->Accept(&visitor);
 }
 
-void ReferenceTypePropagation::Run() {
-  worklist_.reserve(kDefaultWorklistSize);
-
-  // To properly propagate type info we need to visit in the dominator-based order.
-  // Reverse post order guarantees a node's dominators are visited first.
-  // We take advantage of this order in `VisitBasicBlock`.
-  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
-    VisitBasicBlock(block);
-  }
-
-  ProcessWorklist();
-  ValidateTypes();
-}
-
-void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
-  RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
-  // Handle Phis first as there might be instructions in the same block who depend on them.
-  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
-    VisitPhi(it.Current()->AsPhi());
-  }
-
-  // Handle instructions.
-  for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
-    HInstruction* instr = it.Current();
-    instr->Accept(&visitor);
-  }
-
-  // Add extra nodes to bound types.
-  BoundTypeForIfNotNull(block);
-  BoundTypeForIfInstanceOf(block);
-}
-
 // Check if we should create a bound type for the given object at the specified
 // position. Because of inlining and the fact we run RTP more than once and we
 // might have a HBoundType already. If we do, we should not create a new one.
@@ -225,6 +194,153 @@ static bool ShouldCreateBoundType(HInstruction* position,
   return false;
 }
 
+// Helper method to bound the type of `receiver` for all instructions dominated
+// by `start_block`, or `start_instruction` if `start_block` is null. The new
+// bound type will have its upper bound be `class_rti`.
+static void BoundTypeIn(HInstruction* receiver,
+                        HBasicBlock* start_block,
+                        HInstruction* start_instruction,
+                        const ReferenceTypeInfo& class_rti) {
+  // We only need to bound the type if we have uses in the relevant block.
+  // So start with null and create the HBoundType lazily, only if it's needed.
+  HBoundType* bound_type = nullptr;
+  DCHECK(!receiver->IsLoadClass()) << "We should not replace HLoadClass instructions";
+  const HUseList<HInstruction*>& uses = receiver->GetUses();
+  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+    HInstruction* user = it->GetUser();
+    size_t index = it->GetIndex();
+    // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
+    ++it;
+    bool dominates = (start_instruction != nullptr)
+        ? start_instruction->StrictlyDominates(user)
+        : start_block->Dominates(user->GetBlock());
+    if (!dominates) {
+      continue;
+    }
+    if (bound_type == nullptr) {
+      ScopedObjectAccess soa(Thread::Current());
+      HInstruction* insert_point = (start_instruction != nullptr)
+          ? start_instruction->GetNext()
+          : start_block->GetFirstInstruction();
+      if (ShouldCreateBoundType(
+            insert_point, receiver, class_rti, start_instruction, start_block)) {
+        bound_type = new (receiver->GetBlock()->GetGraph()->GetArena()) HBoundType(receiver);
+        bound_type->SetUpperBound(class_rti, /* bound_can_be_null */ false);
+        start_block->InsertInstructionBefore(bound_type, insert_point);
+        // To comply with the RTP algorithm, don't type the bound type just yet, it will
+        // be handled in RTPVisitor::VisitBoundType.
+      } else {
+        // We already have a bound type on the position we would need to insert
+        // the new one. The existing bound type should dominate all the users
+        // (dchecked) so there's no need to continue.
+        break;
+      }
+    }
+    user->ReplaceInput(bound_type, index);
+  }
+  // If the receiver is a null check, also bound the type of the actual
+  // receiver.
+  if (receiver->IsNullCheck()) {
+    BoundTypeIn(receiver->InputAt(0), start_block, start_instruction, class_rti);
+  }
+}
+
+// Recognize the patterns:
+// if (obj.shadow$_klass_ == Foo.class) ...
+// deoptimize if (obj.shadow$_klass_ == Foo.class)
+static void BoundTypeForClassCheck(HInstruction* check) {
+  if (!check->IsIf() && !check->IsDeoptimize()) {
+    return;
+  }
+  HInstruction* compare = check->InputAt(0);
+  if (!compare->IsEqual() && !compare->IsNotEqual()) {
+    return;
+  }
+  HInstruction* input_one = compare->InputAt(0);
+  HInstruction* input_two = compare->InputAt(1);
+  HLoadClass* load_class = input_one->IsLoadClass()
+      ? input_one->AsLoadClass()
+      : input_two->AsLoadClass();
+  if (load_class == nullptr) {
+    return;
+  }
+
+  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+  if (!class_rti.IsValid()) {
+    // We have loaded an unresolved class. Don't bother bounding the type.
+    return;
+  }
+
+  HInstanceFieldGet* field_get = (load_class == input_one)
+      ? input_two->AsInstanceFieldGet()
+      : input_one->AsInstanceFieldGet();
+  if (field_get == nullptr) {
+    return;
+  }
+  HInstruction* receiver = field_get->InputAt(0);
+  ReferenceTypeInfo receiver_type = receiver->GetReferenceTypeInfo();
+  if (receiver_type.IsExact()) {
+    // If we already know the receiver type, don't bother updating its users.
+    return;
+  }
+
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
+    DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
+    if (field_get->GetFieldInfo().GetField() != field) {
+      return;
+    }
+  }
+
+  if (check->IsIf()) {
+    HBasicBlock* trueBlock = check->IsEqual()
+        ? check->AsIf()->IfTrueSuccessor()
+        : check->AsIf()->IfFalseSuccessor();
+    BoundTypeIn(receiver, trueBlock, /* start_instruction */ nullptr, class_rti);
+  } else {
+    DCHECK(check->IsDeoptimize());
+    if (check->IsEqual()) {
+      BoundTypeIn(receiver, check->GetBlock(), check, class_rti);
+    }
+  }
+}
+
+void ReferenceTypePropagation::Run() {
+  worklist_.reserve(kDefaultWorklistSize);
+
+  // To properly propagate type info we need to visit in the dominator-based order.
+  // Reverse post order guarantees a node's dominators are visited first.
+  // We take advantage of this order in `VisitBasicBlock`.
+  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+    VisitBasicBlock(block);
+  }
+
+  ProcessWorklist();
+  ValidateTypes();
+}
+
+void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+  RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
+  // Handle Phis first as there might be instructions in the same block who depend on them.
+  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+    VisitPhi(it.Current()->AsPhi());
+  }
+
+  // Handle instructions. Since RTP may add HBoundType instructions just after the
+  // last visited instruction, use `HInstructionIteratorHandleChanges` iterator.
+  for (HInstructionIteratorHandleChanges it(block->GetInstructions()); !it.Done(); it.Advance()) {
+    HInstruction* instr = it.Current();
+    instr->Accept(&visitor);
+  }
+
+  // Add extra nodes to bound types.
+  BoundTypeForIfNotNull(block);
+  BoundTypeForIfInstanceOf(block);
+  BoundTypeForClassCheck(block->GetLastInstruction());
+}
+
 void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
   HIf* ifInstruction = block->GetLastInstruction()->AsIf();
   if (ifInstruction == nullptr) {
@@ -254,40 +370,14 @@ void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
 
   // We only need to bound the type if we have uses in the relevant block.
   // So start with null and create the HBoundType lazily, only if it's needed.
-  HBoundType* bound_type = nullptr;
   HBasicBlock* notNullBlock = ifInput->IsNotEqual()
       ? ifInstruction->IfTrueSuccessor()
       : ifInstruction->IfFalseSuccessor();
 
-  const HUseList<HInstruction*>& uses = obj->GetUses();
-  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
-    HInstruction* user = it->GetUser();
-    size_t index = it->GetIndex();
-    // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
-    ++it;
-    if (notNullBlock->Dominates(user->GetBlock())) {
-      if (bound_type == nullptr) {
-        ScopedObjectAccess soa(Thread::Current());
-        HInstruction* insert_point = notNullBlock->GetFirstInstruction();
-        ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
-            handle_cache_.GetObjectClassHandle(), /* is_exact */ false);
-        if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) {
-          bound_type = new (graph_->GetArena()) HBoundType(obj);
-          bound_type->SetUpperBound(object_rti, /* bound_can_be_null */ false);
-          if (obj->GetReferenceTypeInfo().IsValid()) {
-            bound_type->SetReferenceTypeInfo(obj->GetReferenceTypeInfo());
-          }
-          notNullBlock->InsertInstructionBefore(bound_type, insert_point);
-        } else {
-          // We already have a bound type on the position we would need to insert
-          // the new one. The existing bound type should dominate all the users
-          // (dchecked) so there's no need to continue.
-          break;
-        }
-      }
-      user->ReplaceInput(bound_type, index);
-    }
-  }
+  ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
+      handle_cache_.GetObjectClassHandle(), /* is_exact */ false);
+
+  BoundTypeIn(obj, notNullBlock, /* start_instruction */ nullptr, object_rti);
 }
 
 // Returns true if one of the patterns below has been recognized. If so, the
@@ -378,15 +468,10 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) {
 
   HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
   ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
-  {
-    if (!class_rti.IsValid()) {
-      // He have loaded an unresolved class. Don't bother bounding the type.
-      return;
-    }
+  if (!class_rti.IsValid()) {
+    // He have loaded an unresolved class. Don't bother bounding the type.
+    return;
   }
-  // We only need to bound the type if we have uses in the relevant block.
-  // So start with null and create the HBoundType lazily, only if it's needed.
-  HBoundType* bound_type = nullptr;
 
   HInstruction* obj = instanceOf->InputAt(0);
   if (obj->GetReferenceTypeInfo().IsExact() && !obj->IsPhi()) {
@@ -398,33 +483,14 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) {
     // input.
     return;
   }
-  DCHECK(!obj->IsLoadClass()) << "We should not replace HLoadClass instructions";
-  const HUseList<HInstruction*>& uses = obj->GetUses();
-  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
-    HInstruction* user = it->GetUser();
-    size_t index = it->GetIndex();
-    // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
-    ++it;
-    if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
-      if (bound_type == nullptr) {
-        ScopedObjectAccess soa(Thread::Current());
-        HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
-        if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
-          bound_type = new (graph_->GetArena()) HBoundType(obj);
-          bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
-          bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact),
-                                    /* InstanceOf fails for null. */ false);
-          instanceOfTrueBlock->InsertInstructionBefore(bound_type, insert_point);
-        } else {
-          // We already have a bound type on the position we would need to insert
-          // the new one. The existing bound type should dominate all the users
-          // (dchecked) so there's no need to continue.
-          break;
-        }
-      }
-      user->ReplaceInput(bound_type, index);
+
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    if (!class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
+      class_rti = ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false);
     }
   }
+  BoundTypeIn(obj, instanceOfTrueBlock, /* start_instruction */ nullptr, class_rti);
 }
 
 void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* instr,
@@ -464,6 +530,10 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst
   }
 }
 
+void ReferenceTypePropagation::RTPVisitor::VisitDeoptimize(HDeoptimize* instr) {
+  BoundTypeForClassCheck(instr);
+}
+
 void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr,
                                                                    dex::TypeIndex type_idx,
                                                                    const DexFile& dex_file,
@@ -515,16 +585,9 @@ void ReferenceTypePropagation::RTPVisitor::UpdateFieldAccessTypeInfo(HInstructio
   ScopedObjectAccess soa(Thread::Current());
   ObjPtr<mirror::Class> klass;
 
-  // The field index is unknown only during tests.
-  if (info.GetFieldIndex() != kUnknownFieldIndex) {
-    ClassLinker* cl = Runtime::Current()->GetClassLinker();
-    ArtField* field = cl->GetResolvedField(info.GetFieldIndex(),
-                                           MakeObjPtr(info.GetDexCache().Get()));
-    // TODO: There are certain cases where we can't resolve the field.
-    // b/21914925 is open to keep track of a repro case for this issue.
-    if (field != nullptr) {
-      klass = field->GetType<false>();
-    }
+  // The field is unknown only during tests.
+  if (info.GetField() != nullptr) {
+    klass = info.GetField()->GetType<false>();
   }
 
   SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
index 559f409..2227872 100644 (file)
@@ -492,7 +492,6 @@ static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator,
                                   HInstruction** input2) {
   HGraph* graph = CreateGraph(allocator);
   HBasicBlock* entry = new (allocator) HBasicBlock(graph);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (allocator) HParameterValue(
@@ -504,13 +503,13 @@ static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator,
   entry->AddSuccessor(block);
 
   HInstruction* test = new (allocator) HInstanceFieldGet(parameter,
+                                                         nullptr,
                                                          Primitive::kPrimBoolean,
                                                          MemberOffset(22),
                                                          false,
                                                          kUnknownFieldIndex,
                                                          kUnknownClassDefIndex,
                                                          graph->GetDexFile(),
-                                                         dex_cache,
                                                          0);
   block->AddInstruction(test);
   block->AddInstruction(new (allocator) HIf(test));
@@ -531,22 +530,22 @@ static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator,
   *phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
   join->AddPhi(*phi);
   *input1 = new (allocator) HInstanceFieldGet(parameter,
+                                              nullptr,
                                               Primitive::kPrimInt,
                                               MemberOffset(42),
                                               false,
                                               kUnknownFieldIndex,
                                               kUnknownClassDefIndex,
                                               graph->GetDexFile(),
-                                              dex_cache,
                                               0);
   *input2 = new (allocator) HInstanceFieldGet(parameter,
+                                              nullptr,
                                               Primitive::kPrimInt,
                                               MemberOffset(42),
                                               false,
                                               kUnknownFieldIndex,
                                               kUnknownClassDefIndex,
                                               graph->GetDexFile(),
-                                              dex_cache,
                                               0);
   then->AddInstruction(*input1);
   else_->AddInstruction(*input2);
@@ -654,7 +653,6 @@ static HGraph* BuildFieldReturn(ArenaAllocator* allocator,
                                 HInstruction** field,
                                 HInstruction** ret) {
   HGraph* graph = CreateGraph(allocator);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
   HBasicBlock* entry = new (allocator) HBasicBlock(graph);
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
@@ -667,13 +665,13 @@ static HGraph* BuildFieldReturn(ArenaAllocator* allocator,
   entry->AddSuccessor(block);
 
   *field = new (allocator) HInstanceFieldGet(parameter,
+                                             nullptr,
                                              Primitive::kPrimInt,
                                              MemberOffset(42),
                                              false,
                                              kUnknownFieldIndex,
                                              kUnknownClassDefIndex,
                                              graph->GetDexFile(),
-                                             dex_cache,
                                              0);
   block->AddInstruction(*field);
   *ret = new (allocator) HReturn(*field);
diff --git a/test/631-checker-get-class/expected.txt b/test/631-checker-get-class/expected.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/631-checker-get-class/info.txt b/test/631-checker-get-class/info.txt
new file mode 100644 (file)
index 0000000..f236a22
--- /dev/null
@@ -0,0 +1,4 @@
+Checker test to make sure we recognize the pattern:
+if (foo.getClass() == Foo.class)
+
+For doing better type propagation.
diff --git a/test/631-checker-get-class/src/Main.java b/test/631-checker-get-class/src/Main.java
new file mode 100644 (file)
index 0000000..61c0adf
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 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 {
+
+  /// CHECK-START: int Main.bar(Main) inliner (before)
+  /// CHECK: InvokeVirtual
+  /// CHECK: InvokeVirtual
+
+  /// CHECK-START: int Main.bar(Main) inliner (after)
+  /// CHECK-NOT: InvokeVirtual
+  public static int bar(Main m) {
+    if (m.getClass() == Main.class) {
+      return m.foo();
+    }
+    return 4;
+  }
+
+  public int foo() {
+    return 42;
+  }
+
+  /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
+  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+  /// CHECK-DAG:                 Return [<<Select>>]
+
+  /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Constant:i\d+>> IntConstant 1
+  /// CHECK-DAG:                   Return [<<Constant>>]
+  public static boolean classEquality1() {
+    return new Main().getClass() == Main.class;
+  }
+
+  /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
+  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+  /// CHECK-DAG:                 Return [<<Select>>]
+
+  /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Constant:i\d+>> IntConstant 0
+  /// CHECK-DAG:                   Return [<<Constant>>]
+  public static boolean classEquality2() {
+    Object o = new SubMain();
+    return o.getClass() == Main.class;
+  }
+
+  /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
+  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+  /// CHECK-DAG:                 Return [<<Select>>]
+
+  /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Constant:i\d+>> IntConstant 0
+  /// CHECK-DAG:                   Return [<<Constant>>]
+  public static boolean classEquality3() {
+    return new Main().getClass() != Main.class;
+  }
+
+  /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
+  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+  /// CHECK-DAG:                 Return [<<Select>>]
+
+  /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Constant:i\d+>> IntConstant 1
+  /// CHECK-DAG:                   Return [<<Constant>>]
+  public static boolean classEquality4() {
+    Object o = new SubMain();
+    return o.getClass() != Main.class;
+  }
+
+  public static void main(String[] args) {
+    int actual = bar(new Main());
+    if (actual != 42) {
+      throw new Error("Expected 42, got " + actual);
+    }
+    actual = bar(new SubMain());
+    if (actual != 4) {
+      throw new Error("Expected 4, got " + actual);
+    }
+  }
+}
+
+class SubMain extends Main {
+}