Revert "Revert "Optimize code generation of check-cast and instance-of.""

This reverts commit 7537437c6a2f89249a48e30effcc27d4e7c5a04f.

Change-Id: If759cb08646e47b62829bebc3c5b1e2f2969cf84
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 289ef64..32a1db5 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -300,8 +300,8 @@
 
 class TypeCheckSlowPathX86_64 : public SlowPathCode {
  public:
-  explicit TypeCheckSlowPathX86_64(HInstruction* instruction)
-      : instruction_(instruction) {}
+  TypeCheckSlowPathX86_64(HInstruction* instruction, bool is_fatal)
+      : instruction_(instruction), is_fatal_(is_fatal) {}
 
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     LocationSummary* locations = instruction_->GetLocations();
@@ -313,7 +313,19 @@
 
     CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
+
+    if (instruction_->IsCheckCast()) {
+      // The codegen for the instruction overwrites `temp`, so put it back in place.
+      CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
+      CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+      uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+      __ movl(temp, Address(obj, class_offset));
+      __ MaybeUnpoisonHeapReference(temp);
+    }
+
+    if (!is_fatal_) {
+      SaveLiveRegisters(codegen, locations);
+    }
 
     // We're moving two locations to locations that could overlap, so we need a parallel
     // move resolver.
@@ -339,18 +351,23 @@
                                  this);
     }
 
-    if (instruction_->IsInstanceOf()) {
-      x64_codegen->Move(locations->Out(), Location::RegisterLocation(RAX));
-    }
+    if (!is_fatal_) {
+      if (instruction_->IsInstanceOf()) {
+        x64_codegen->Move(locations->Out(), Location::RegisterLocation(RAX));
+      }
 
-    RestoreLiveRegisters(codegen, locations);
-    __ jmp(GetExitLabel());
+      RestoreLiveRegisters(codegen, locations);
+      __ jmp(GetExitLabel());
+    }
   }
 
   const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathX86_64"; }
 
+  bool IsFatal() const OVERRIDE { return is_fatal_; }
+
  private:
   HInstruction* const instruction_;
+  const bool is_fatal_;
 
   DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathX86_64);
 };
@@ -4684,14 +4701,33 @@
 }
 
 void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
-  LocationSummary::CallKind call_kind = instruction->IsClassFinal()
-      ? LocationSummary::kNoCall
-      : LocationSummary::kCallOnSlowPath;
+  LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
+  switch (instruction->GetTypeCheckKind()) {
+    case TypeCheckKind::kExactCheck:
+    case TypeCheckKind::kAbstractClassCheck:
+    case TypeCheckKind::kClassHierarchyCheck:
+    case TypeCheckKind::kArrayObjectCheck:
+      call_kind = LocationSummary::kNoCall;
+      break;
+    case TypeCheckKind::kInterfaceCheck:
+      call_kind = LocationSummary::kCall;
+      break;
+    case TypeCheckKind::kArrayCheck:
+      call_kind = LocationSummary::kCallOnSlowPath;
+      break;
+  }
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::Any());
-  // Note that TypeCheckSlowPathX86_64 uses this register too.
-  locations->SetOut(Location::RequiresRegister());
+  if (call_kind != LocationSummary::kCall) {
+    locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetInAt(1, Location::Any());
+    // Note that TypeCheckSlowPathX86_64 uses this register too.
+    locations->SetOut(Location::RequiresRegister());
+  } else {
+    InvokeRuntimeCallingConvention calling_convention;
+    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+    locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+    locations->SetOut(Location::RegisterLocation(RAX));
+  }
 }
 
 void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
@@ -4700,8 +4736,11 @@
   Location cls = locations->InAt(1);
   CpuRegister out = locations->Out().AsRegister<CpuRegister>();
   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  NearLabel done, zero;
+  uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
+  uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
+  uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
   SlowPathCode* slow_path = nullptr;
+  NearLabel done, zero;
 
   // Return 0 if `obj` is null.
   // Avoid null check if we know obj is not null.
@@ -4709,77 +4748,282 @@
     __ testl(obj, obj);
     __ j(kEqual, &zero);
   }
-  // Compare the class of `obj` with `cls`.
-  __ movl(out, Address(obj, class_offset));
-  __ MaybeUnpoisonHeapReference(out);
-  if (cls.IsRegister()) {
-    __ cmpl(out, cls.AsRegister<CpuRegister>());
-  } else {
-    DCHECK(cls.IsStackSlot()) << cls;
-    __ cmpl(out, Address(CpuRegister(RSP), cls.GetStackIndex()));
-  }
-  if (instruction->IsClassFinal()) {
-    // Classes must be equal for the instanceof to succeed.
-    __ j(kNotEqual, &zero);
-    __ movl(out, Immediate(1));
-    __ jmp(&done);
-  } else {
-    // If the classes are not equal, we go into a slow path.
-    DCHECK(locations->OnlyCallsOnSlowPath());
-    slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathX86_64(instruction);
-    codegen_->AddSlowPath(slow_path);
-    __ j(kNotEqual, slow_path->GetEntryLabel());
-    __ movl(out, Immediate(1));
-    __ jmp(&done);
+
+  // In case of an interface check, we put the object class into the object register.
+  // This is safe, as the register is caller-save, and the object must be in another
+  // register if it survives the runtime call.
+  CpuRegister target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck)
+      ? obj
+      : out;
+  __ movl(target, Address(obj, class_offset));
+  __ MaybeUnpoisonHeapReference(target);
+
+  switch (instruction->GetTypeCheckKind()) {
+    case TypeCheckKind::kExactCheck: {
+      if (cls.IsRegister()) {
+        __ cmpl(out, cls.AsRegister<CpuRegister>());
+      } else {
+        DCHECK(cls.IsStackSlot()) << cls;
+        __ cmpl(out, Address(CpuRegister(RSP), cls.GetStackIndex()));
+      }
+      // Classes must be equal for the instanceof to succeed.
+      __ j(kNotEqual, &zero);
+      __ movl(out, Immediate(1));
+      __ jmp(&done);
+      break;
+    }
+    case TypeCheckKind::kAbstractClassCheck: {
+      // If the class is abstract, we eagerly fetch the super class of the
+      // object to avoid doing a comparison we know will fail.
+      NearLabel loop, success;
+      __ Bind(&loop);
+      __ movl(out, Address(out, super_offset));
+      __ MaybeUnpoisonHeapReference(out);
+      __ testl(out, out);
+      // If `out` is null, we use it for the result, and jump to `done`.
+      __ j(kEqual, &done);
+      if (cls.IsRegister()) {
+        __ cmpl(out, cls.AsRegister<CpuRegister>());
+      } else {
+        DCHECK(cls.IsStackSlot()) << cls;
+        __ cmpl(out, Address(CpuRegister(RSP), cls.GetStackIndex()));
+      }
+      __ j(kNotEqual, &loop);
+      __ movl(out, Immediate(1));
+      if (zero.IsLinked()) {
+        __ jmp(&done);
+      }
+      break;
+    }
+    case TypeCheckKind::kClassHierarchyCheck: {
+      // Walk over the class hierarchy to find a match.
+      NearLabel loop, success;
+      __ Bind(&loop);
+      if (cls.IsRegister()) {
+        __ cmpl(out, cls.AsRegister<CpuRegister>());
+      } else {
+        DCHECK(cls.IsStackSlot()) << cls;
+        __ cmpl(out, Address(CpuRegister(RSP), cls.GetStackIndex()));
+      }
+      __ j(kEqual, &success);
+      __ movl(out, Address(out, super_offset));
+      __ MaybeUnpoisonHeapReference(out);
+      __ testl(out, out);
+      __ j(kNotEqual, &loop);
+      // If `out` is null, we use it for the result, and jump to `done`.
+      __ jmp(&done);
+      __ Bind(&success);
+      __ movl(out, Immediate(1));
+      if (zero.IsLinked()) {
+        __ jmp(&done);
+      }
+      break;
+    }
+    case TypeCheckKind::kArrayObjectCheck: {
+      // Just need to check that the object's class is a non primitive array.
+      __ movl(out, Address(out, component_offset));
+      __ MaybeUnpoisonHeapReference(out);
+      __ testl(out, out);
+      // If `out` is null, we use it for the result, and jump to `done`.
+      __ j(kEqual, &done);
+      __ cmpw(Address(out, primitive_offset), Immediate(Primitive::kPrimNot));
+      __ j(kNotEqual, &zero);
+      __ movl(out, Immediate(1));
+      __ jmp(&done);
+      break;
+    }
+    case TypeCheckKind::kArrayCheck: {
+      if (cls.IsRegister()) {
+        __ cmpl(out, cls.AsRegister<CpuRegister>());
+      } else {
+        DCHECK(cls.IsStackSlot()) << cls;
+        __ cmpl(out, Address(CpuRegister(RSP), cls.GetStackIndex()));
+      }
+      DCHECK(locations->OnlyCallsOnSlowPath());
+      slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathX86_64(
+          instruction, /* is_fatal */ false);
+      codegen_->AddSlowPath(slow_path);
+      __ j(kNotEqual, slow_path->GetEntryLabel());
+      __ movl(out, Immediate(1));
+      if (zero.IsLinked()) {
+        __ jmp(&done);
+      }
+      break;
+    }
+
+    case TypeCheckKind::kInterfaceCheck:
+    default: {
+      codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial),
+                              instruction,
+                              instruction->GetDexPc(),
+                              nullptr);
+      if (zero.IsLinked()) {
+        __ jmp(&done);
+      }
+      break;
+    }
   }
 
-  if (instruction->MustDoNullCheck() || instruction->IsClassFinal()) {
+  if (zero.IsLinked()) {
     __ Bind(&zero);
-    __ movl(out, Immediate(0));
+    __ xorl(out, out);
+  }
+
+  if (done.IsLinked()) {
+    __ Bind(&done);
   }
 
   if (slow_path != nullptr) {
     __ Bind(slow_path->GetExitLabel());
   }
-  __ Bind(&done);
 }
 
 void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) {
+  LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
+  bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
+
+  switch (instruction->GetTypeCheckKind()) {
+    case TypeCheckKind::kExactCheck:
+    case TypeCheckKind::kAbstractClassCheck:
+    case TypeCheckKind::kClassHierarchyCheck:
+    case TypeCheckKind::kArrayObjectCheck:
+      call_kind = throws_into_catch
+          ? LocationSummary::kCallOnSlowPath
+          : LocationSummary::kNoCall;
+      break;
+    case TypeCheckKind::kInterfaceCheck:
+      call_kind = LocationSummary::kCall;
+      break;
+    case TypeCheckKind::kArrayCheck:
+      call_kind = LocationSummary::kCallOnSlowPath;
+      break;
+  }
+
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
-      instruction, LocationSummary::kCallOnSlowPath);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::Any());
-  // Note that TypeCheckSlowPathX86_64 uses this register too.
-  locations->AddTemp(Location::RequiresRegister());
+      instruction, call_kind);
+  if (call_kind != LocationSummary::kCall) {
+    locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetInAt(1, Location::Any());
+    // Note that TypeCheckSlowPathX86_64 uses this register too.
+    locations->AddTemp(Location::RequiresRegister());
+  } else {
+    InvokeRuntimeCallingConvention calling_convention;
+    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+    locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  }
 }
 
 void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
   Location cls = locations->InAt(1);
-  CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
-  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathX86_64(instruction);
-  codegen_->AddSlowPath(slow_path);
+  CpuRegister temp = locations->WillCall()
+      ? CpuRegister(kNoRegister)
+      : locations->GetTemp(0).AsRegister<CpuRegister>();
 
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+  uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
+  uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
+  uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
+  SlowPathCode* slow_path = nullptr;
+
+  if (!locations->WillCall()) {
+    slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathX86_64(
+        instruction, !locations->CanCall());
+    codegen_->AddSlowPath(slow_path);
+  }
+
+  NearLabel done;
   // Avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
     __ testl(obj, obj);
-    __ j(kEqual, slow_path->GetExitLabel());
+    __ j(kEqual, &done);
   }
-  // Compare the class of `obj` with `cls`.
-  __ movl(temp, Address(obj, class_offset));
-  __ MaybeUnpoisonHeapReference(temp);
-  if (cls.IsRegister()) {
-    __ cmpl(temp, cls.AsRegister<CpuRegister>());
+
+  if (locations->WillCall()) {
+    __ movl(obj, Address(obj, class_offset));
+    __ MaybeUnpoisonHeapReference(obj);
   } else {
-    DCHECK(cls.IsStackSlot()) << cls;
-    __ cmpl(temp, Address(CpuRegister(RSP), cls.GetStackIndex()));
+    __ movl(temp, Address(obj, class_offset));
+    __ MaybeUnpoisonHeapReference(temp);
   }
-  // The checkcast succeeds if the classes are equal (fast path).
-  // Otherwise, we need to go into the slow path to check the types.
-  __ j(kNotEqual, slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+
+  switch (instruction->GetTypeCheckKind()) {
+    case TypeCheckKind::kExactCheck:
+    case TypeCheckKind::kArrayCheck: {
+      if (cls.IsRegister()) {
+        __ cmpl(temp, cls.AsRegister<CpuRegister>());
+      } else {
+        DCHECK(cls.IsStackSlot()) << cls;
+        __ cmpl(temp, Address(CpuRegister(RSP), cls.GetStackIndex()));
+      }
+      // Jump to slow path for throwing the exception or doing a
+      // more involved array check.
+      __ j(kNotEqual, slow_path->GetEntryLabel());
+      break;
+    }
+    case TypeCheckKind::kAbstractClassCheck: {
+      // If the class is abstract, we eagerly fetch the super class of the
+      // object to avoid doing a comparison we know will fail.
+      NearLabel loop;
+      __ Bind(&loop);
+      __ movl(temp, Address(temp, super_offset));
+      __ MaybeUnpoisonHeapReference(temp);
+      __ testl(temp, temp);
+      // Jump to the slow path to throw the exception.
+      __ j(kEqual, slow_path->GetEntryLabel());
+      if (cls.IsRegister()) {
+        __ cmpl(temp, cls.AsRegister<CpuRegister>());
+      } else {
+        DCHECK(cls.IsStackSlot()) << cls;
+        __ cmpl(temp, Address(CpuRegister(RSP), cls.GetStackIndex()));
+      }
+      __ j(kNotEqual, &loop);
+      break;
+    }
+    case TypeCheckKind::kClassHierarchyCheck: {
+      // Walk over the class hierarchy to find a match.
+      NearLabel loop, success;
+      __ Bind(&loop);
+      if (cls.IsRegister()) {
+        __ cmpl(temp, cls.AsRegister<CpuRegister>());
+      } else {
+        DCHECK(cls.IsStackSlot()) << cls;
+        __ cmpl(temp, Address(CpuRegister(RSP), cls.GetStackIndex()));
+      }
+      __ j(kEqual, &success);
+      __ movl(temp, Address(temp, super_offset));
+      __ MaybeUnpoisonHeapReference(temp);
+      __ testl(temp, temp);
+      __ j(kNotEqual, &loop);
+      // Jump to the slow path to throw the exception.
+      __ jmp(slow_path->GetEntryLabel());
+      __ Bind(&success);
+      break;
+    }
+    case TypeCheckKind::kArrayObjectCheck: {
+      // Just need to check that the object's class is a non primitive array.
+      __ movl(temp, Address(temp, component_offset));
+      __ MaybeUnpoisonHeapReference(temp);
+      __ testl(temp, temp);
+      __ j(kEqual, slow_path->GetEntryLabel());
+      __ cmpw(Address(temp, primitive_offset), Immediate(Primitive::kPrimNot));
+      __ j(kNotEqual, slow_path->GetEntryLabel());
+      break;
+    }
+    case TypeCheckKind::kInterfaceCheck:
+    default:
+      codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast),
+                              instruction,
+                              instruction->GetDexPc(),
+                              nullptr);
+      break;
+  }
+  __ Bind(&done);
+
+  if (slow_path != nullptr) {
+    __ Bind(slow_path->GetExitLabel());
+  }
 }
 
 void LocationsBuilderX86_64::VisitMonitorOperation(HMonitorOperation* instruction) {