Revert "Compiler changes for bitstring based type checks."

Bug: 64692057
Bug: 71853552
Bug: 26687569

This reverts commit eb0ebed72432b3c6b8c7b38f8937d7ba736f4567.

Change-Id: I7daeaa077960ba41b2ed42bc47f17501621be4be
diff --git a/benchmark/type-check/info.txt b/benchmark/type-check/info.txt
deleted file mode 100644
index d14fb96..0000000
--- a/benchmark/type-check/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Benchmarks for repeating check-cast and instance-of instructions in a loop.
diff --git a/benchmark/type-check/src/TypeCheckBenchmark.java b/benchmark/type-check/src/TypeCheckBenchmark.java
deleted file mode 100644
index 96904d9..0000000
--- a/benchmark/type-check/src/TypeCheckBenchmark.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2018 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 TypeCheckBenchmark {
-    public void timeCheckCastLevel1ToLevel1(int count) {
-        Object[] arr = arr1;
-        for (int i = 0; i < count; ++i) {
-            Level1 l1 = (Level1) arr[i & 1023];
-        }
-    }
-
-    public void timeCheckCastLevel2ToLevel1(int count) {
-        Object[] arr = arr2;
-        for (int i = 0; i < count; ++i) {
-            Level1 l1 = (Level1) arr[i & 1023];
-        }
-    }
-
-    public void timeCheckCastLevel3ToLevel1(int count) {
-        Object[] arr = arr3;
-        for (int i = 0; i < count; ++i) {
-            Level1 l1 = (Level1) arr[i & 1023];
-        }
-    }
-
-    public void timeCheckCastLevel9ToLevel1(int count) {
-        Object[] arr = arr9;
-        for (int i = 0; i < count; ++i) {
-            Level1 l1 = (Level1) arr[i & 1023];
-        }
-    }
-
-    public void timeCheckCastLevel9ToLevel2(int count) {
-        Object[] arr = arr9;
-        for (int i = 0; i < count; ++i) {
-            Level2 l2 = (Level2) arr[i & 1023];
-        }
-    }
-
-    public void timeInstanceOfLevel1ToLevel1(int count) {
-        int sum = 0;
-        Object[] arr = arr1;
-        for (int i = 0; i < count; ++i) {
-            if (arr[i & 1023] instanceof Level1) {
-              ++sum;
-            }
-        }
-        result = sum;
-    }
-
-    public void timeInstanceOfLevel2ToLevel1(int count) {
-        int sum = 0;
-        Object[] arr = arr2;
-        for (int i = 0; i < count; ++i) {
-            if (arr[i & 1023] instanceof Level1) {
-              ++sum;
-            }
-        }
-        result = sum;
-    }
-
-    public void timeInstanceOfLevel3ToLevel1(int count) {
-        int sum = 0;
-        Object[] arr = arr3;
-        for (int i = 0; i < count; ++i) {
-            if (arr[i & 1023] instanceof Level1) {
-              ++sum;
-            }
-        }
-        result = sum;
-    }
-
-    public void timeInstanceOfLevel9ToLevel1(int count) {
-        int sum = 0;
-        Object[] arr = arr9;
-        for (int i = 0; i < count; ++i) {
-            if (arr[i & 1023] instanceof Level1) {
-              ++sum;
-            }
-        }
-        result = sum;
-    }
-
-    public void timeInstanceOfLevel9ToLevel2(int count) {
-        int sum = 0;
-        Object[] arr = arr9;
-        for (int i = 0; i < count; ++i) {
-            if (arr[i & 1023] instanceof Level2) {
-              ++sum;
-            }
-        }
-        result = sum;
-    }
-
-    public static Object[] createArray(int level) {
-        try {
-            Class<?>[] ls = {
-                    null,
-                    Level1.class,
-                    Level2.class,
-                    Level3.class,
-                    Level4.class,
-                    Level5.class,
-                    Level6.class,
-                    Level7.class,
-                    Level8.class,
-                    Level9.class,
-            };
-            Class<?> l = ls[level];
-            Object[] array = new Object[1024];
-            for (int i = 0; i < array.length; ++i) {
-                array[i] = l.newInstance();
-            }
-            return array;
-        } catch (Exception unexpected) {
-            throw new Error("Initialization failure!");
-        }
-    }
-    Object[] arr1 = createArray(1);
-    Object[] arr2 = createArray(2);
-    Object[] arr3 = createArray(3);
-    Object[] arr9 = createArray(9);
-    int result;
-}
-
-class Level1 { }
-class Level2 extends Level1 { }
-class Level3 extends Level2 { }
-class Level4 extends Level3 { }
-class Level5 extends Level4 { }
-class Level6 extends Level5 { }
-class Level7 extends Level6 { }
-class Level8 extends Level7 { }
-class Level9 extends Level8 { }
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 3720dda..60537fd 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -680,8 +680,7 @@
 // TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a
 //       stable order.
 
-static void ResolveConstStrings(ClassLinker* class_linker,
-                                Handle<mirror::DexCache> dex_cache,
+static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache,
                                 const DexFile& dex_file,
                                 const DexFile::CodeItem* code_item)
       REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -690,6 +689,7 @@
     return;
   }
 
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
   for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) {
     switch (inst->Opcode()) {
       case Instruction::CONST_STRING:
@@ -737,105 +737,22 @@
           dex_file->StringByTypeIdx(class_def.class_idx_));
       if (!compilation_enabled) {
         // Compilation is skipped, do not resolve const-string in code of this class.
-        // FIXME: Make sure that inlining honors this. b/26687569
+        // TODO: Make sure that inlining honors this.
         continue;
       }
 
       // Direct and virtual methods.
+      int64_t previous_method_idx = -1;
       while (it.HasNextMethod()) {
-        ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem());
-        it.Next();
-      }
-      DCHECK(!it.HasNext());
-    }
-  }
-}
-
-// Initialize type check bit strings for check-cast and instance-of in the code. Done to have
-// deterministic allocation behavior. Right now this is single-threaded for simplicity.
-// TODO: Collect the relevant type indices in parallel, then process them sequentially in a
-//       stable order.
-
-static void InitializeTypeCheckBitstrings(CompilerDriver* driver,
-                                          ClassLinker* class_linker,
-                                          Handle<mirror::DexCache> dex_cache,
-                                          const DexFile& dex_file,
-                                          const DexFile::CodeItem* code_item)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (code_item == nullptr) {
-    // Abstract or native method.
-    return;
-  }
-
-  for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) {
-    switch (inst->Opcode()) {
-      case Instruction::CHECK_CAST:
-      case Instruction::INSTANCE_OF: {
-        dex::TypeIndex type_index(
-            (inst->Opcode() == Instruction::CHECK_CAST) ? inst->VRegB_21c() : inst->VRegC_22c());
-        const char* descriptor = dex_file.StringByTypeIdx(type_index);
-        // We currently do not use the bitstring type check for array or final (including
-        // primitive) classes. We may reconsider this in future if it's deemed to be beneficial.
-        // And we cannot use it for classes outside the boot image as we do not know the runtime
-        // value of their bitstring when compiling (it may not even get assigned at runtime).
-        if (descriptor[0] == 'L' && driver->IsImageClass(descriptor)) {
-          ObjPtr<mirror::Class> klass =
-              class_linker->LookupResolvedType(type_index,
-                                               dex_cache.Get(),
-                                               /* class_loader */ nullptr);
-          CHECK(klass != nullptr) << descriptor << " should have been previously resolved.";
-          // Now assign the bitstring if the class is not final. Keep this in sync with sharpening.
-          if (!klass->IsFinal()) {
-            MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
-            SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass);
-          }
+        uint32_t method_idx = it.GetMemberIndex();
+        if (method_idx == previous_method_idx) {
+          // smali can create dex files with two encoded_methods sharing the same method_idx
+          // http://code.google.com/p/smali/issues/detail?id=119
+          it.Next();
+          continue;
         }
-        break;
-      }
-
-      default:
-        break;
-    }
-  }
-}
-
-static void InitializeTypeCheckBitstrings(CompilerDriver* driver,
-                                          const std::vector<const DexFile*>& dex_files,
-                                          TimingLogger* timings) {
-  ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<1> hs(soa.Self());
-  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
-  MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr));
-
-  for (const DexFile* dex_file : dex_files) {
-    dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file));
-    TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings);
-
-    size_t class_def_count = dex_file->NumClassDefs();
-    for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
-      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
-
-      const uint8_t* class_data = dex_file->GetClassData(class_def);
-      if (class_data == nullptr) {
-        // empty class, probably a marker interface
-        continue;
-      }
-
-      ClassDataItemIterator it(*dex_file, class_data);
-      it.SkipAllFields();
-
-      bool compilation_enabled = driver->IsClassToCompile(
-          dex_file->StringByTypeIdx(class_def.class_idx_));
-      if (!compilation_enabled) {
-        // Compilation is skipped, do not look for type checks in code of this class.
-        // FIXME: Make sure that inlining honors this. b/26687569
-        continue;
-      }
-
-      // Direct and virtual methods.
-      while (it.HasNextMethod()) {
-        InitializeTypeCheckBitstrings(
-            driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem());
+        previous_method_idx = method_idx;
+        ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem());
         it.Next();
       }
       DCHECK(!it.HasNext());
@@ -937,13 +854,6 @@
 
   UpdateImageClasses(timings);
   VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
-
-  if (GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) {
-    // Initialize type check bit string used by check-cast and instanceof.
-    // Do this now to have a deterministic image.
-    // Note: This is done after UpdateImageClasses() at it relies on the image classes to be final.
-    InitializeTypeCheckBitstrings(this, dex_files, timings);
-  }
 }
 
 bool CompilerDriver::IsImageClass(const char* descriptor) const {
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 18b1e0e..b51e0de 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -77,9 +77,6 @@
 class VerificationResults;
 class VerifiedMethod;
 
-// Compile-time flag to enable/disable bitstring type checks.
-static constexpr bool kUseBitstringTypeCheck = true;
-
 enum EntryPointCallingConvention {
   // ABI of invocations to a method's interpreter entry point.
   kInterpreterAbi,
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 2dafbf7..3c5a37f 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -438,8 +438,6 @@
       case TypeCheckKind::kArrayCheck:
       case TypeCheckKind::kUnresolvedCheck:
         return false;
-      case TypeCheckKind::kBitstringCheck:
-        return true;
     }
     LOG(FATAL) << "Unreachable";
     UNREACHABLE();
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index b47a5cf..13bbffa 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2112,26 +2112,6 @@
   __ Bind(slow_path->GetExitLabel());
 }
 
-void InstructionCodeGeneratorARM64::GenerateBitstringTypeCheckCompare(
-    HTypeCheckInstruction* check, vixl::aarch64::Register temp) {
-  uint32_t path_to_root = check->GetBitstringPathToRoot();
-  uint32_t mask = check->GetBitstringMask();
-  DCHECK(IsPowerOfTwo(mask + 1));
-  size_t mask_bits = WhichPowerOf2(mask + 1);
-
-  if (mask_bits == 16u) {
-    // Load only the bitstring part of the status word.
-    __ Ldrh(temp, HeapOperand(temp, mirror::Class::StatusOffset()));
-  } else {
-    // /* uint32_t */ temp = temp->status_
-    __ Ldr(temp, HeapOperand(temp, mirror::Class::StatusOffset()));
-    // Extract the bitstring bits.
-    __ Ubfx(temp, temp, 0, mask_bits);
-  }
-  // Compare the bitstring bits to `path_to_root`.
-  __ Cmp(temp, path_to_root);
-}
-
 void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) {
   BarrierType type = BarrierAll;
 
@@ -3860,8 +3840,6 @@
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCallOnSlowPath;
       break;
-    case TypeCheckKind::kBitstringCheck:
-      break;
   }
 
   LocationSummary* locations =
@@ -3870,13 +3848,7 @@
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
+  locations->SetInAt(1, Location::RequiresRegister());
   // The "out" register is used as a temporary, so it overlaps with the inputs.
   // Note that TypeCheckSlowPathARM64 uses this register too.
   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -3889,9 +3861,7 @@
   LocationSummary* locations = instruction->GetLocations();
   Location obj_loc = locations->InAt(0);
   Register obj = InputRegisterAt(instruction, 0);
-  Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
-      ? Register()
-      : InputRegisterAt(instruction, 1);
+  Register cls = InputRegisterAt(instruction, 1);
   Location out_loc = locations->Out();
   Register out = OutputRegister(instruction);
   const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -4077,23 +4047,6 @@
       }
       break;
     }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp_loc,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, out);
-      __ Cset(out, eq);
-      if (zero.IsLinked()) {
-        __ B(&done);
-      }
-      break;
-    }
   }
 
   if (zero.IsLinked()) {
@@ -4116,13 +4069,7 @@
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
+  locations->SetInAt(1, Location::RequiresRegister());
   // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64.
   locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
 }
@@ -4132,9 +4079,7 @@
   LocationSummary* locations = instruction->GetLocations();
   Location obj_loc = locations->InAt(0);
   Register obj = InputRegisterAt(instruction, 0);
-  Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
-      ? Register()
-      : InputRegisterAt(instruction, 1);
+  Register cls = InputRegisterAt(instruction, 1);
   const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
   DCHECK_GE(num_temps, 1u);
   DCHECK_LE(num_temps, 3u);
@@ -4315,20 +4260,6 @@
       __ B(ne, &start_loop);
       break;
     }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, temp);
-      __ B(ne, type_check_slow_path->GetEntryLabel());
-      break;
-    }
   }
   __ Bind(&done);
 
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index cc369de..f92c94f 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -264,8 +264,6 @@
  private:
   void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,
                                         vixl::aarch64::Register class_reg);
-  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
-                                         vixl::aarch64::Register temp);
   void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
   void HandleBinaryOp(HBinaryOperation* instr);
 
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 504c647..6cbde72 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -7195,67 +7195,6 @@
   __ Bind(slow_path->GetExitLabel());
 }
 
-void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare(
-    HTypeCheckInstruction* check,
-    vixl32::Register temp,
-    vixl32::FlagsUpdate flags_update) {
-  uint32_t path_to_root = check->GetBitstringPathToRoot();
-  uint32_t mask = check->GetBitstringMask();
-  DCHECK(IsPowerOfTwo(mask + 1));
-  size_t mask_bits = WhichPowerOf2(mask + 1);
-
-  // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs
-  // the Z flag for BNE. This is indicated by the `flags_update` parameter.
-  if (mask_bits == 16u) {
-    // Load only the bitstring part of the status word.
-    __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
-    // Check if the bitstring bits are equal to `path_to_root`.
-    if (flags_update == SetFlags) {
-      __ Cmp(temp, path_to_root);
-    } else {
-      __ Sub(temp, temp, path_to_root);
-    }
-  } else {
-    // /* uint32_t */ temp = temp->status_
-    __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
-    if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) {
-      // Compare the bitstring bits using SUB.
-      __ Sub(temp, temp, path_to_root);
-      // Shift out bits that do not contribute to the comparison.
-      __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
-    } else if (IsUint<16>(path_to_root)) {
-      if (temp.IsLow()) {
-        // Note: Optimized for size but contains one more dependent instruction than necessary.
-        //       MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the
-        //       macro assembler would use the high reg IP for the constant by default.
-        // Compare the bitstring bits using SUB.
-        __ Sub(temp, temp, path_to_root & 0x00ffu);  // 16-bit SUB (immediate) T2
-        __ Sub(temp, temp, path_to_root & 0xff00u);  // 32-bit SUB (immediate) T3
-        // Shift out bits that do not contribute to the comparison.
-        __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
-      } else {
-        // Extract the bitstring bits.
-        __ Ubfx(temp, temp, 0, mask_bits);
-        // Check if the bitstring bits are equal to `path_to_root`.
-        if (flags_update == SetFlags) {
-          __ Cmp(temp, path_to_root);
-        } else {
-          __ Sub(temp, temp, path_to_root);
-        }
-      }
-    } else {
-      // Shift out bits that do not contribute to the comparison.
-      __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
-      // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`.
-      if (flags_update == SetFlags) {
-        __ Cmp(temp, path_to_root << (32u - mask_bits));
-      } else {
-        __ Sub(temp, temp, path_to_root << (32u - mask_bits));
-      }
-    }
-  }
-}
-
 HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
     HLoadString::LoadKind desired_string_load_kind) {
   switch (desired_string_load_kind) {
@@ -7447,8 +7386,6 @@
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCallOnSlowPath;
       break;
-    case TypeCheckKind::kBitstringCheck:
-      break;
   }
 
   LocationSummary* locations =
@@ -7457,13 +7394,7 @@
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
+  locations->SetInAt(1, Location::RequiresRegister());
   // The "out" register is used as a temporary, so it overlaps with the inputs.
   // Note that TypeCheckSlowPathARM uses this register too.
   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -7478,9 +7409,7 @@
   LocationSummary* locations = instruction->GetLocations();
   Location obj_loc = locations->InAt(0);
   vixl32::Register obj = InputRegisterAt(instruction, 0);
-  vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
-      ? vixl32::Register()
-      : InputRegisterAt(instruction, 1);
+  vixl32::Register cls = InputRegisterAt(instruction, 1);
   Location out_loc = locations->Out();
   vixl32::Register out = OutputRegister(instruction);
   const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -7720,26 +7649,6 @@
       __ B(slow_path->GetEntryLabel());
       break;
     }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp_loc,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, out, DontCare);
-      // If `out` is a low reg and we would have another low reg temp, we could
-      // optimize this as RSBS+ADC, see GenerateConditionWithZero().
-      //
-      // Also, in some cases when `out` is a low reg and we're loading a constant to IP
-      // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size
-      // would be the same and we would have fewer direct data dependencies.
-      codegen_->GenerateConditionWithZero(kCondEQ, out, out);  // CLZ+LSR
-      break;
-    }
   }
 
   if (done.IsReferenced()) {
@@ -7757,13 +7666,7 @@
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
+  locations->SetInAt(1, Location::RequiresRegister());
   locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
 }
 
@@ -7772,9 +7675,7 @@
   LocationSummary* locations = instruction->GetLocations();
   Location obj_loc = locations->InAt(0);
   vixl32::Register obj = InputRegisterAt(instruction, 0);
-  vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
-      ? vixl32::Register()
-      : InputRegisterAt(instruction, 1);
+  vixl32::Register cls = InputRegisterAt(instruction, 1);
   Location temp_loc = locations->GetTemp(0);
   vixl32::Register temp = RegisterFrom(temp_loc);
   const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
@@ -7959,20 +7860,6 @@
       __ B(ne, &start_loop, /* far_target */ false);
       break;
     }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags);
-      __ B(ne, type_check_slow_path->GetEntryLabel());
-      break;
-    }
   }
   if (done.IsReferenced()) {
     __ Bind(&done);
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index bd815f4..38570bb 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -322,9 +322,6 @@
   void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
   void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path,
                                         vixl32::Register class_reg);
-  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
-                                         vixl::aarch32::Register temp,
-                                         vixl::aarch32::FlagsUpdate flags_update);
   void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
   void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
   void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 2ed0ab7..5c8e46e 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1929,34 +1929,6 @@
   __ Bind(slow_path->GetExitLabel());
 }
 
-void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
-                                                                     Register temp) {
-  uint32_t path_to_root = check->GetBitstringPathToRoot();
-  uint32_t mask = check->GetBitstringMask();
-  DCHECK(IsPowerOfTwo(mask + 1));
-  size_t mask_bits = WhichPowerOf2(mask + 1);
-
-  if (mask_bits == 16u) {
-    // Load only the bitstring part of the status word.
-    __ LoadFromOffset(
-        kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value());
-    // Compare the bitstring bits using XOR.
-    __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
-  } else {
-    // /* uint32_t */ temp = temp->status_
-    __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value());
-    // Compare the bitstring bits using XOR.
-    if (IsUint<16>(path_to_root)) {
-      __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
-    } else {
-      __ LoadConst32(TMP, path_to_root);
-      __ Xor(temp, temp, TMP);
-    }
-    // Shift out bits that do not contribute to the comparison.
-    __ Sll(temp, temp, 32 - mask_bits);
-  }
-}
-
 void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {
   __ Sync(0);  // Only stype 0 is supported.
 }
@@ -3317,20 +3289,12 @@
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCallOnSlowPath;
       break;
-    case TypeCheckKind::kBitstringCheck:
-      break;
   }
 
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
+  locations->SetInAt(1, Location::RequiresRegister());
   locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
 }
 
@@ -3339,7 +3303,7 @@
   LocationSummary* locations = instruction->GetLocations();
   Location obj_loc = locations->InAt(0);
   Register obj = obj_loc.AsRegister<Register>();
-  Location cls = locations->InAt(1);
+  Register cls = locations->InAt(1).AsRegister<Register>();
   Location temp_loc = locations->GetTemp(0);
   Register temp = temp_loc.AsRegister<Register>();
   const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
@@ -3389,7 +3353,7 @@
                                         kWithoutReadBarrier);
       // Jump to slow path for throwing the exception or doing a
       // more involved array check.
-      __ Bne(temp, cls.AsRegister<Register>(), slow_path->GetEntryLabel());
+      __ Bne(temp, cls, slow_path->GetEntryLabel());
       break;
     }
 
@@ -3415,7 +3379,7 @@
       // exception.
       __ Beqz(temp, slow_path->GetEntryLabel());
       // Otherwise, compare the classes.
-      __ Bne(temp, cls.AsRegister<Register>(), &loop);
+      __ Bne(temp, cls, &loop);
       break;
     }
 
@@ -3430,7 +3394,7 @@
       // Walk over the class hierarchy to find a match.
       MipsLabel loop;
       __ Bind(&loop);
-      __ Beq(temp, cls.AsRegister<Register>(), &done);
+      __ Beq(temp, cls, &done);
       // /* HeapReference<Class> */ temp = temp->super_class_
       GenerateReferenceLoadOneRegister(instruction,
                                        temp_loc,
@@ -3453,7 +3417,7 @@
                                         maybe_temp2_loc,
                                         kWithoutReadBarrier);
       // Do an exact check.
-      __ Beq(temp, cls.AsRegister<Register>(), &done);
+      __ Beq(temp, cls, &done);
       // Otherwise, we need to check that the object's class is a non-primitive array.
       // /* HeapReference<Class> */ temp = temp->component_type_
       GenerateReferenceLoadOneRegister(instruction,
@@ -3512,21 +3476,7 @@
       // Go to next interface.
       __ Addiu(TMP, TMP, -2);
       // Compare the classes and continue the loop if they do not match.
-      __ Bne(AT, cls.AsRegister<Register>(), &loop);
-      break;
-    }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, temp);
-      __ Bnez(temp, slow_path->GetEntryLabel());
+      __ Bne(AT, cls, &loop);
       break;
     }
   }
@@ -7257,8 +7207,6 @@
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCallOnSlowPath;
       break;
-    case TypeCheckKind::kBitstringCheck:
-      break;
   }
 
   LocationSummary* locations =
@@ -7267,13 +7215,7 @@
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
+  locations->SetInAt(1, Location::RequiresRegister());
   // The output does overlap inputs.
   // Note that TypeCheckSlowPathMIPS uses this register too.
   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -7285,7 +7227,7 @@
   LocationSummary* locations = instruction->GetLocations();
   Location obj_loc = locations->InAt(0);
   Register obj = obj_loc.AsRegister<Register>();
-  Location cls = locations->InAt(1);
+  Register cls = locations->InAt(1).AsRegister<Register>();
   Location out_loc = locations->Out();
   Register out = out_loc.AsRegister<Register>();
   const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -7315,7 +7257,7 @@
                                         maybe_temp_loc,
                                         kCompilerReadBarrierOption);
       // Classes must be equal for the instanceof to succeed.
-      __ Xor(out, out, cls.AsRegister<Register>());
+      __ Xor(out, out, cls);
       __ Sltiu(out, out, 1);
       break;
     }
@@ -7340,7 +7282,7 @@
                                        kCompilerReadBarrierOption);
       // If `out` is null, we use it for the result, and jump to `done`.
       __ Beqz(out, &done);
-      __ Bne(out, cls.AsRegister<Register>(), &loop);
+      __ Bne(out, cls, &loop);
       __ LoadConst32(out, 1);
       break;
     }
@@ -7356,7 +7298,7 @@
       // Walk over the class hierarchy to find a match.
       MipsLabel loop, success;
       __ Bind(&loop);
-      __ Beq(out, cls.AsRegister<Register>(), &success);
+      __ Beq(out, cls, &success);
       // /* HeapReference<Class> */ out = out->super_class_
       GenerateReferenceLoadOneRegister(instruction,
                                        out_loc,
@@ -7381,7 +7323,7 @@
                                         kCompilerReadBarrierOption);
       // Do an exact check.
       MipsLabel success;
-      __ Beq(out, cls.AsRegister<Register>(), &success);
+      __ Beq(out, cls, &success);
       // Otherwise, we need to check that the object's class is a non-primitive array.
       // /* HeapReference<Class> */ out = out->component_type_
       GenerateReferenceLoadOneRegister(instruction,
@@ -7413,7 +7355,7 @@
       slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(
           instruction, /* is_fatal */ false);
       codegen_->AddSlowPath(slow_path);
-      __ Bne(out, cls.AsRegister<Register>(), slow_path->GetEntryLabel());
+      __ Bne(out, cls, slow_path->GetEntryLabel());
       __ LoadConst32(out, 1);
       break;
     }
@@ -7445,20 +7387,6 @@
       __ B(slow_path->GetEntryLabel());
       break;
     }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp_loc,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, out);
-      __ Sltiu(out, out, 1);
-      break;
-    }
   }
 
   __ Bind(&done);
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index ffeb3b0..32b3e42 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -237,7 +237,6 @@
  private:
   void GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg);
   void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
-  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp);
   void HandleBinaryOp(HBinaryOperation* operation);
   void HandleCondition(HCondition* instruction);
   void HandleShift(HBinaryOperation* operation);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 3ae8a30..bcfe051 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1775,34 +1775,6 @@
   __ Bind(slow_path->GetExitLabel());
 }
 
-void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
-                                                                       GpuRegister temp) {
-  uint32_t path_to_root = check->GetBitstringPathToRoot();
-  uint32_t mask = check->GetBitstringMask();
-  DCHECK(IsPowerOfTwo(mask + 1));
-  size_t mask_bits = WhichPowerOf2(mask + 1);
-
-  if (mask_bits == 16u) {
-    // Load only the bitstring part of the status word.
-    __ LoadFromOffset(
-        kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value());
-    // Compare the bitstring bits using XOR.
-    __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
-  } else {
-    // /* uint32_t */ temp = temp->status_
-    __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value());
-    // Compare the bitstring bits using XOR.
-    if (IsUint<16>(path_to_root)) {
-      __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
-    } else {
-      __ LoadConst32(TMP, path_to_root);
-      __ Xor(temp, temp, TMP);
-    }
-    // Shift out bits that do not contribute to the comparison.
-    __ Sll(temp, temp, 32 - mask_bits);
-  }
-}
-
 void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {
   __ Sync(0);  // only stype 0 is supported
 }
@@ -2872,20 +2844,12 @@
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCallOnSlowPath;
       break;
-    case TypeCheckKind::kBitstringCheck:
-      break;
   }
 
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
+  locations->SetInAt(1, Location::RequiresRegister());
   locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
 }
 
@@ -2894,7 +2858,7 @@
   LocationSummary* locations = instruction->GetLocations();
   Location obj_loc = locations->InAt(0);
   GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
-  Location cls = locations->InAt(1);
+  GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>();
   Location temp_loc = locations->GetTemp(0);
   GpuRegister temp = temp_loc.AsRegister<GpuRegister>();
   const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
@@ -2944,7 +2908,7 @@
                                         kWithoutReadBarrier);
       // Jump to slow path for throwing the exception or doing a
       // more involved array check.
-      __ Bnec(temp, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
+      __ Bnec(temp, cls, slow_path->GetEntryLabel());
       break;
     }
 
@@ -2970,7 +2934,7 @@
       // exception.
       __ Beqzc(temp, slow_path->GetEntryLabel());
       // Otherwise, compare the classes.
-      __ Bnec(temp, cls.AsRegister<GpuRegister>(), &loop);
+      __ Bnec(temp, cls, &loop);
       break;
     }
 
@@ -2985,7 +2949,7 @@
       // Walk over the class hierarchy to find a match.
       Mips64Label loop;
       __ Bind(&loop);
-      __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);
+      __ Beqc(temp, cls, &done);
       // /* HeapReference<Class> */ temp = temp->super_class_
       GenerateReferenceLoadOneRegister(instruction,
                                        temp_loc,
@@ -3008,7 +2972,7 @@
                                         maybe_temp2_loc,
                                         kWithoutReadBarrier);
       // Do an exact check.
-      __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);
+      __ Beqc(temp, cls, &done);
       // Otherwise, we need to check that the object's class is a non-primitive array.
       // /* HeapReference<Class> */ temp = temp->component_type_
       GenerateReferenceLoadOneRegister(instruction,
@@ -3067,21 +3031,7 @@
       __ Daddiu(temp, temp, 2 * kHeapReferenceSize);
       __ Addiu(TMP, TMP, -2);
       // Compare the classes and continue the loop if they do not match.
-      __ Bnec(AT, cls.AsRegister<GpuRegister>(), &loop);
-      break;
-    }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, temp);
-      __ Bnezc(temp, slow_path->GetEntryLabel());
+      __ Bnec(AT, cls, &loop);
       break;
     }
   }
@@ -5574,8 +5524,6 @@
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCallOnSlowPath;
       break;
-    case TypeCheckKind::kBitstringCheck:
-      break;
   }
 
   LocationSummary* locations =
@@ -5584,13 +5532,7 @@
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
+  locations->SetInAt(1, Location::RequiresRegister());
   // The output does overlap inputs.
   // Note that TypeCheckSlowPathMIPS64 uses this register too.
   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -5602,7 +5544,7 @@
   LocationSummary* locations = instruction->GetLocations();
   Location obj_loc = locations->InAt(0);
   GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
-  Location cls = locations->InAt(1);
+  GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>();
   Location out_loc = locations->Out();
   GpuRegister out = out_loc.AsRegister<GpuRegister>();
   const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -5632,7 +5574,7 @@
                                         maybe_temp_loc,
                                         kCompilerReadBarrierOption);
       // Classes must be equal for the instanceof to succeed.
-      __ Xor(out, out, cls.AsRegister<GpuRegister>());
+      __ Xor(out, out, cls);
       __ Sltiu(out, out, 1);
       break;
     }
@@ -5657,7 +5599,7 @@
                                        kCompilerReadBarrierOption);
       // If `out` is null, we use it for the result, and jump to `done`.
       __ Beqzc(out, &done);
-      __ Bnec(out, cls.AsRegister<GpuRegister>(), &loop);
+      __ Bnec(out, cls, &loop);
       __ LoadConst32(out, 1);
       break;
     }
@@ -5673,7 +5615,7 @@
       // Walk over the class hierarchy to find a match.
       Mips64Label loop, success;
       __ Bind(&loop);
-      __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);
+      __ Beqc(out, cls, &success);
       // /* HeapReference<Class> */ out = out->super_class_
       GenerateReferenceLoadOneRegister(instruction,
                                        out_loc,
@@ -5698,7 +5640,7 @@
                                         kCompilerReadBarrierOption);
       // Do an exact check.
       Mips64Label success;
-      __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);
+      __ Beqc(out, cls, &success);
       // Otherwise, we need to check that the object's class is a non-primitive array.
       // /* HeapReference<Class> */ out = out->component_type_
       GenerateReferenceLoadOneRegister(instruction,
@@ -5730,7 +5672,7 @@
       slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64(
           instruction, /* is_fatal */ false);
       codegen_->AddSlowPath(slow_path);
-      __ Bnec(out, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
+      __ Bnec(out, cls, slow_path->GetEntryLabel());
       __ LoadConst32(out, 1);
       break;
     }
@@ -5762,20 +5704,6 @@
       __ Bc(slow_path->GetEntryLabel());
       break;
     }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp_loc,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, out);
-      __ Sltiu(out, out, 1);
-      break;
-    }
   }
 
   __ Bind(&done);
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 87d5a9c..d479410 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -233,7 +233,6 @@
 
  private:
   void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg);
-  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, GpuRegister temp);
   void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
   void HandleBinaryOp(HBinaryOperation* operation);
   void HandleCondition(HCondition* instruction);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index e85f900..cbe9e0a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6234,27 +6234,6 @@
   // No need for memory fence, thanks to the X86 memory model.
 }
 
-void InstructionCodeGeneratorX86::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
-                                                                    Register temp) {
-  uint32_t path_to_root = check->GetBitstringPathToRoot();
-  uint32_t mask = check->GetBitstringMask();
-  DCHECK(IsPowerOfTwo(mask + 1));
-  size_t mask_bits = WhichPowerOf2(mask + 1);
-
-  if ((false) && mask_bits == 16u) {
-    // FIXME: cmpw() erroneously emits the constant as 32 bits instead of 16 bits. b/71853552
-    // Compare the bitstring in memory.
-    __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root));
-  } else {
-    // /* uint32_t */ temp = temp->status_
-    __ movl(temp, Address(temp, mirror::Class::StatusOffset()));
-    // Compare the bitstring bits using SUB.
-    __ subl(temp, Immediate(path_to_root));
-    // Shift out bits that do not contribute to the comparison.
-    __ shll(temp, Immediate(32u - mask_bits));
-  }
-}
-
 HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(
     HLoadString::LoadKind desired_string_load_kind) {
   switch (desired_string_load_kind) {
@@ -6447,8 +6426,6 @@
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCallOnSlowPath;
       break;
-    case TypeCheckKind::kBitstringCheck:
-      break;
   }
 
   LocationSummary* locations =
@@ -6457,13 +6434,7 @@
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::Any());
-  }
+  locations->SetInAt(1, Location::Any());
   // Note that TypeCheckSlowPathX86 uses this "out" register too.
   locations->SetOut(Location::RequiresRegister());
   // When read barriers are enabled, we need a temporary register for some cases.
@@ -6684,21 +6655,6 @@
       }
       break;
     }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, out);
-      __ j(kNotEqual, &zero);
-      __ movl(out, Immediate(1));
-      __ jmp(&done);
-      break;
-    }
   }
 
   if (zero.IsLinked()) {
@@ -6725,10 +6681,6 @@
     // Require a register for the interface check since there is a loop that compares the class to
     // a memory address.
     locations->SetInAt(1, Location::RequiresRegister());
-  } else if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
   } else {
     locations->SetInAt(1, Location::Any());
   }
@@ -6948,19 +6900,6 @@
       __ MaybeUnpoisonHeapReference(cls.AsRegister<Register>());
       break;
     }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, temp);
-      __ j(kNotEqual, type_check_slow_path->GetEntryLabel());
-      break;
-    }
   }
   __ Bind(&done);
 
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 2d14d4c..0082853 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -211,7 +211,6 @@
   // the suspend call.
   void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
   void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg);
-  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp);
   void HandleBitwiseOperation(HBinaryOperation* instruction);
   void GenerateDivRemIntegral(HBinaryOperation* instruction);
   void DivRemOneOrMinusOne(HBinaryOperation* instruction);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 9f8b1bb..510eec4 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5440,27 +5440,6 @@
   // No need for memory fence, thanks to the x86-64 memory model.
 }
 
-void InstructionCodeGeneratorX86_64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
-                                                                       CpuRegister temp) {
-  uint32_t path_to_root = check->GetBitstringPathToRoot();
-  uint32_t mask = check->GetBitstringMask();
-  DCHECK(IsPowerOfTwo(mask + 1));
-  size_t mask_bits = WhichPowerOf2(mask + 1);
-
-  if ((false) && mask_bits == 16u) {
-    // FIXME: cmpw() erroneously emits the constant as 32 bits instead of 16 bits. b/71853552
-    // Compare the bitstring in memory.
-    __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root));
-  } else {
-    // /* uint32_t */ temp = temp->status_
-    __ movl(temp, Address(temp, mirror::Class::StatusOffset()));
-    // Compare the bitstring bits using SUB.
-    __ subl(temp, Immediate(path_to_root));
-    // Shift out bits that do not contribute to the comparison.
-    __ shll(temp, Immediate(32u - mask_bits));
-  }
-}
-
 HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
   switch (desired_class_load_kind) {
@@ -5833,8 +5812,6 @@
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCallOnSlowPath;
       break;
-    case TypeCheckKind::kBitstringCheck:
-      break;
   }
 
   LocationSummary* locations =
@@ -5843,13 +5820,7 @@
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
-  if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
-  } else {
-    locations->SetInAt(1, Location::Any());
-  }
+  locations->SetInAt(1, Location::Any());
   // Note that TypeCheckSlowPathX86_64 uses this "out" register too.
   locations->SetOut(Location::RequiresRegister());
   // When read barriers are enabled, we need a temporary register for
@@ -6078,27 +6049,6 @@
       }
       break;
     }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, out);
-      if (zero.IsLinked()) {
-        __ j(kNotEqual, &zero);
-        __ movl(out, Immediate(1));
-        __ jmp(&done);
-      } else {
-        __ setcc(kEqual, out);
-        // setcc only sets the low byte.
-        __ andl(out, Immediate(1));
-      }
-      break;
-    }
   }
 
   if (zero.IsLinked()) {
@@ -6125,10 +6075,6 @@
     // Require a register for the interface check since there is a loop that compares the class to
     // a memory address.
     locations->SetInAt(1, Location::RequiresRegister());
-  } else if (type_check_kind == TypeCheckKind::kBitstringCheck) {
-    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
-    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
-    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
   } else {
     locations->SetInAt(1, Location::Any());
   }
@@ -6315,7 +6261,7 @@
       break;
     }
 
-    case TypeCheckKind::kInterfaceCheck: {
+    case TypeCheckKind::kInterfaceCheck:
       // Fast path for the interface check. Try to avoid read barriers to improve the fast path.
       // We can not get false positives by doing this.
       // /* HeapReference<Class> */ temp = obj->klass_
@@ -6351,20 +6297,6 @@
       // If `cls` was poisoned above, unpoison it.
       __ MaybeUnpoisonHeapReference(cls.AsRegister<CpuRegister>());
       break;
-    }
-
-    case TypeCheckKind::kBitstringCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        kWithoutReadBarrier);
-
-      GenerateBitstringTypeCheckCompare(instruction, temp);
-      __ j(kNotEqual, type_check_slow_path->GetEntryLabel());
-      break;
-    }
   }
 
   if (done.IsLinked()) {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 97f8ec7..e86123e 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -208,7 +208,6 @@
   // the suspend call.
   void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
   void GenerateClassInitializationCheck(SlowPathCode* slow_path, CpuRegister class_reg);
-  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, CpuRegister temp);
   void HandleBitwiseOperation(HBinaryOperation* operation);
   void GenerateRemFP(HRem* rem);
   void DivRemOneOrMinusOne(HBinaryOperation* instruction);
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index fbcbe36..c88baa8 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -25,11 +25,6 @@
 #include "base/bit_vector-inl.h"
 #include "base/scoped_arena_allocator.h"
 #include "base/scoped_arena_containers.h"
-#include "handle.h"
-#include "mirror/class.h"
-#include "obj_ptr-inl.h"
-#include "scoped_thread_state_change-inl.h"
-#include "subtype_check.h"
 
 namespace art {
 
@@ -553,83 +548,28 @@
   }
 }
 
-void GraphChecker::CheckTypeCheckBitstringInput(HTypeCheckInstruction* check,
-                                                size_t input_pos,
-                                                bool check_value,
-                                                uint32_t expected_value,
-                                                const char* name) {
-  if (!check->InputAt(input_pos)->IsIntConstant()) {
-    AddError(StringPrintf("%s:%d (bitstring) expects a HIntConstant input %zu (%s), not %s:%d.",
-                          check->DebugName(),
-                          check->GetId(),
-                          input_pos,
-                          name,
-                          check->InputAt(2)->DebugName(),
-                          check->InputAt(2)->GetId()));
-  } else if (check_value) {
-    uint32_t actual_value =
-        static_cast<uint32_t>(check->InputAt(input_pos)->AsIntConstant()->GetValue());
-    if (actual_value != expected_value) {
-      AddError(StringPrintf("%s:%d (bitstring) has %s 0x%x, not 0x%x as expected.",
-                            check->DebugName(),
-                            check->GetId(),
-                            name,
-                            actual_value,
-                            expected_value));
-    }
-  }
-}
-
-void GraphChecker::HandleTypeCheckInstruction(HTypeCheckInstruction* check) {
+void GraphChecker::VisitCheckCast(HCheckCast* check) {
   VisitInstruction(check);
   HInstruction* input = check->InputAt(1);
-  if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
-    if (!input->IsNullConstant()) {
-      AddError(StringPrintf("%s:%d (bitstring) expects a HNullConstant as second input, not %s:%d.",
-                            check->DebugName(),
-                            check->GetId(),
-                            input->DebugName(),
-                            input->GetId()));
-    }
-    bool check_values = false;
-    BitString::StorageType expected_path_to_root = 0u;
-    BitString::StorageType expected_mask = 0u;
-    {
-      ScopedObjectAccess soa(Thread::Current());
-      ObjPtr<mirror::Class> klass = check->GetClass().Get();
-      MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
-      SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass);
-      if (state == SubtypeCheckInfo::kAssigned) {
-        expected_path_to_root =
-            SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass);
-        expected_mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass);
-        check_values = true;
-      } else {
-        AddError(StringPrintf("%s:%d (bitstring) references a class with unassigned bitstring.",
-                              check->DebugName(),
-                              check->GetId()));
-      }
-    }
-    CheckTypeCheckBitstringInput(
-        check, /* input_pos */ 2, check_values, expected_path_to_root, "path_to_root");
-    CheckTypeCheckBitstringInput(check, /* input_pos */ 3, check_values, expected_mask, "mask");
-  } else {
-    if (!input->IsLoadClass()) {
-      AddError(StringPrintf("%s:%d (classic) expects a HLoadClass as second input, not %s:%d.",
-                            check->DebugName(),
-                            check->GetId(),
-                            input->DebugName(),
-                            input->GetId()));
-    }
+  if (!input->IsLoadClass()) {
+    AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.",
+                          check->DebugName(),
+                          check->GetId(),
+                          input->DebugName(),
+                          input->GetId()));
   }
 }
 
-void GraphChecker::VisitCheckCast(HCheckCast* check) {
-  HandleTypeCheckInstruction(check);
-}
-
 void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) {
-  HandleTypeCheckInstruction(instruction);
+  VisitInstruction(instruction);
+  HInstruction* input = instruction->InputAt(1);
+  if (!input->IsLoadClass()) {
+    AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.",
+                          instruction->DebugName(),
+                          instruction->GetId(),
+                          input->DebugName(),
+                          input->GetId()));
+  }
 }
 
 void GraphChecker::HandleLoop(HBasicBlock* loop_header) {
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index dbedc40..0f0b49d 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -71,12 +71,6 @@
   void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE;
   void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
 
-  void CheckTypeCheckBitstringInput(HTypeCheckInstruction* check,
-                                    size_t input_pos,
-                                    bool check_value,
-                                    uint32_t expected_value,
-                                    const char* name);
-  void HandleTypeCheckInstruction(HTypeCheckInstruction* instruction);
   void HandleLoop(HBasicBlock* loop_header);
   void HandleBooleanInput(HInstruction* instruction, size_t input_index);
 
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 5519121..12c6988 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -389,23 +389,16 @@
     StartAttributeStream("load_kind") << load_string->GetLoadKind();
   }
 
-  void HandleTypeCheckInstruction(HTypeCheckInstruction* check) {
-    StartAttributeStream("check_kind") << check->GetTypeCheckKind();
-    StartAttributeStream("must_do_null_check") << std::boolalpha
-        << check->MustDoNullCheck() << std::noboolalpha;
-    if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
-      StartAttributeStream("path_to_root") << std::hex
-          << "0x" << check->GetBitstringPathToRoot() << std::dec;
-      StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec;
-    }
-  }
-
   void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
-    HandleTypeCheckInstruction(check_cast);
+    StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind();
+    StartAttributeStream("must_do_null_check") << std::boolalpha
+        << check_cast->MustDoNullCheck() << std::noboolalpha;
   }
 
   void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE {
-    HandleTypeCheckInstruction(instance_of);
+    StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind();
+    StartAttributeStream("must_do_null_check") << std::boolalpha
+        << instance_of->MustDoNullCheck() << std::noboolalpha;
   }
 
   void VisitArrayLength(HArrayLength* array_length) OVERRIDE {
@@ -655,32 +648,20 @@
           << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha;
     }
 
-    // For the builder and the inliner, we want to add extra information on HInstructions
-    // that have reference types, and also HInstanceOf/HCheckcast.
     if ((IsPass(HGraphBuilder::kBuilderPassName)
         || IsPass(HInliner::kInlinerPassName))
-        && (instruction->GetType() == DataType::Type::kReference ||
-            instruction->IsInstanceOf() ||
-            instruction->IsCheckCast())) {
-      ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference)
-          ? instruction->IsLoadClass()
-              ? instruction->AsLoadClass()->GetLoadedClassRTI()
-              : instruction->GetReferenceTypeInfo()
-          : instruction->IsInstanceOf()
-              ? instruction->AsInstanceOf()->GetTargetClassRTI()
-              : instruction->AsCheckCast()->GetTargetClassRTI();
+        && (instruction->GetType() == DataType::Type::kReference)) {
+      ReferenceTypeInfo info = instruction->IsLoadClass()
+        ? instruction->AsLoadClass()->GetLoadedClassRTI()
+        : instruction->GetReferenceTypeInfo();
       ScopedObjectAccess soa(Thread::Current());
       if (info.IsValid()) {
         StartAttributeStream("klass")
             << mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get());
-        if (instruction->GetType() == DataType::Type::kReference) {
-          StartAttributeStream("can_be_null")
-              << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
-        }
+        StartAttributeStream("can_be_null")
+            << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
         StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
-      } else if (instruction->IsLoadClass() ||
-                 instruction->IsInstanceOf() ||
-                 instruction->IsCheckCast()) {
+      } else if (instruction->IsLoadClass()) {
         StartAttributeStream("klass") << "unresolved";
       } else {
         // The NullConstant may be added to the graph during other passes that happen between
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 0205c6a..64a1ecc 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1811,6 +1811,29 @@
   }
 }
 
+static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (cls == nullptr) {
+    return TypeCheckKind::kUnresolvedCheck;
+  } else if (cls->IsInterface()) {
+    return TypeCheckKind::kInterfaceCheck;
+  } else if (cls->IsArrayClass()) {
+    if (cls->GetComponentType()->IsObjectClass()) {
+      return TypeCheckKind::kArrayObjectCheck;
+    } else if (cls->CannotBeAssignedFromOtherTypes()) {
+      return TypeCheckKind::kExactCheck;
+    } else {
+      return TypeCheckKind::kArrayCheck;
+    }
+  } else if (cls->IsFinal()) {
+    return TypeCheckKind::kExactCheck;
+  } else if (cls->IsAbstract()) {
+    return TypeCheckKind::kAbstractClassCheck;
+  } else {
+    return TypeCheckKind::kClassHierarchyCheck;
+  }
+}
+
 void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) {
   HLoadString* load_string =
       new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc);
@@ -1825,8 +1848,22 @@
 HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) {
   ScopedObjectAccess soa(Thread::Current());
   const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
-  Handle<mirror::Class> klass = ResolveClass(soa, type_index);
-  bool needs_access_check = LoadClassNeedsAccessCheck(klass);
+  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
+  Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass(
+      soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_));
+
+  bool needs_access_check = true;
+  if (klass != nullptr) {
+    if (klass->IsPublic()) {
+      needs_access_check = false;
+    } else {
+      ObjPtr<mirror::Class> compiling_class = GetCompilingClass();
+      if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) {
+        needs_access_check = false;
+      }
+    }
+  }
+
   return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check);
 }
 
@@ -1871,83 +1908,25 @@
   return load_class;
 }
 
-Handle<mirror::Class> HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa,
-                                                        dex::TypeIndex type_index) {
-  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
-  ObjPtr<mirror::Class> klass = compiler_driver_->ResolveClass(
-      soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_);
-  // TODO: Avoid creating excessive handles if the method references the same class repeatedly.
-  // (Use a map on the local_allocator_.)
-  return handles_->NewHandle(klass);
-}
-
-bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) {
-  if (klass == nullptr) {
-    return true;
-  } else if (klass->IsPublic()) {
-    return false;
-  } else {
-    ObjPtr<mirror::Class> compiling_class = GetCompilingClass();
-    return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get());
-  }
-}
-
 void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
                                          uint8_t destination,
                                          uint8_t reference,
                                          dex::TypeIndex type_index,
                                          uint32_t dex_pc) {
   HInstruction* object = LoadLocal(reference, DataType::Type::kReference);
+  HLoadClass* cls = BuildLoadClass(type_index, dex_pc);
 
   ScopedObjectAccess soa(Thread::Current());
-  const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
-  Handle<mirror::Class> klass = ResolveClass(soa, type_index);
-  bool needs_access_check = LoadClassNeedsAccessCheck(klass);
-  TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind(
-      klass.Get(), code_generator_, compiler_driver_, needs_access_check);
-
-  HInstruction* class_or_null = nullptr;
-  HIntConstant* bitstring_path_to_root = nullptr;
-  HIntConstant* bitstring_mask = nullptr;
-  if (check_kind == TypeCheckKind::kBitstringCheck) {
-    // TODO: Allow using the bitstring check also if we need an access check.
-    DCHECK(!needs_access_check);
-    class_or_null = graph_->GetNullConstant(dex_pc);
-    MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
-    uint32_t path_to_root =
-        SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass.Get());
-    uint32_t mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass.Get());
-    bitstring_path_to_root = graph_->GetIntConstant(static_cast<int32_t>(path_to_root), dex_pc);
-    bitstring_mask = graph_->GetIntConstant(static_cast<int32_t>(mask), dex_pc);
-  } else {
-    class_or_null = BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check);
-  }
-  DCHECK(class_or_null != nullptr);
-
+  TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass());
   if (instruction.Opcode() == Instruction::INSTANCE_OF) {
-    AppendInstruction(new (allocator_) HInstanceOf(object,
-                                                   class_or_null,
-                                                   check_kind,
-                                                   klass,
-                                                   dex_pc,
-                                                   allocator_,
-                                                   bitstring_path_to_root,
-                                                   bitstring_mask));
+    AppendInstruction(new (allocator_) HInstanceOf(object, cls, check_kind, dex_pc));
     UpdateLocal(destination, current_block_->GetLastInstruction());
   } else {
     DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
     // We emit a CheckCast followed by a BoundType. CheckCast is a statement
     // which may throw. If it succeeds BoundType sets the new type of `object`
     // for all subsequent uses.
-    AppendInstruction(
-        new (allocator_) HCheckCast(object,
-                                    class_or_null,
-                                    check_kind,
-                                    klass,
-                                    dex_pc,
-                                    allocator_,
-                                    bitstring_path_to_root,
-                                    bitstring_mask));
+    AppendInstruction(new (allocator_) HCheckCast(object, cls, check_kind, dex_pc));
     AppendInstruction(new (allocator_) HBoundType(object, dex_pc));
     UpdateLocal(reference, current_block_->GetLastInstruction());
   }
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index f788292..4428c53 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -39,7 +39,6 @@
 class HBasicBlockBuilder;
 class Instruction;
 class OptimizingCompilerStats;
-class ScopedObjectAccess;
 class SsaBuilder;
 class VariableSizedHandleScope;
 
@@ -233,12 +232,6 @@
                              bool needs_access_check)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  Handle<mirror::Class> ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Returns the outer-most compiling method's class.
   ObjPtr<mirror::Class> GetOutermostCompilingClass() const;
 
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 2538fa3..a42a85d 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -576,9 +576,7 @@
 
 // Returns whether doing a type test between the class of `object` against `klass` has
 // a statically known outcome. The result of the test is stored in `outcome`.
-static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti,
-                                     HInstruction* object,
-                                     /*out*/bool* outcome) {
+static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) {
   DCHECK(!object->IsNullConstant()) << "Null constants should be special cased";
   ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo();
   ScopedObjectAccess soa(Thread::Current());
@@ -588,6 +586,7 @@
     return false;
   }
 
+  ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI();
   if (!class_rti.IsValid()) {
     // Happens when the loaded class is unresolved.
     return false;
@@ -612,8 +611,8 @@
 
 void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
   HInstruction* object = check_cast->InputAt(0);
-  if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck &&
-      check_cast->GetTargetClass()->NeedsAccessCheck()) {
+  HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
+  if (load_class->NeedsAccessCheck()) {
     // If we need to perform an access check we cannot remove the instruction.
     return;
   }
@@ -631,18 +630,15 @@
   // Note: The `outcome` is initialized to please valgrind - the compiler can reorder
   // the return value check with the `outcome` check, b/27651442 .
   bool outcome = false;
-  if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) {
+  if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
     if (outcome) {
       check_cast->GetBlock()->RemoveInstruction(check_cast);
       MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast);
-      if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) {
-        HLoadClass* load_class = check_cast->GetTargetClass();
-        if (!load_class->HasUses()) {
-          // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
-          // However, here we know that it cannot because the checkcast was successfull, hence
-          // the class was already loaded.
-          load_class->GetBlock()->RemoveInstruction(load_class);
-        }
+      if (!load_class->HasUses()) {
+        // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
+        // However, here we know that it cannot because the checkcast was successfull, hence
+        // the class was already loaded.
+        load_class->GetBlock()->RemoveInstruction(load_class);
       }
     } else {
       // Don't do anything for exceptional cases for now. Ideally we should remove
@@ -653,8 +649,8 @@
 
 void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
   HInstruction* object = instruction->InputAt(0);
-  if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck &&
-      instruction->GetTargetClass()->NeedsAccessCheck()) {
+  HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass();
+  if (load_class->NeedsAccessCheck()) {
     // If we need to perform an access check we cannot remove the instruction.
     return;
   }
@@ -677,7 +673,7 @@
   // Note: The `outcome` is initialized to please valgrind - the compiler can reorder
   // the return value check with the `outcome` check, b/27651442 .
   bool outcome = false;
-  if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) {
+  if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
     MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf);
     if (outcome && can_be_null) {
       // Type test will succeed, we just need a null test.
@@ -690,14 +686,11 @@
     }
     RecordSimplification();
     instruction->GetBlock()->RemoveInstruction(instruction);
-    if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) {
-      HLoadClass* load_class = instruction->GetTargetClass();
-      if (!load_class->HasUses()) {
-        // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
-        // However, here we know that it cannot because the instanceof check was successfull, hence
-        // the class was already loaded.
-        load_class->GetBlock()->RemoveInstruction(load_class);
-      }
+    if (outcome && !load_class->HasUses()) {
+      // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
+      // However, here we know that it cannot because the instanceof check was successfull, hence
+      // the class was already loaded.
+      load_class->GetBlock()->RemoveInstruction(load_class);
     }
   }
 }
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 5587f87..91e475d 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -3105,8 +3105,6 @@
       return os << "array_object_check";
     case TypeCheckKind::kArrayCheck:
       return os << "array_check";
-    case TypeCheckKind::kBitstringCheck:
-      return os << "bitstring_check";
     default:
       LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast<int>(rhs);
       UNREACHABLE();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index a9782a6..43ca2cf 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -5951,7 +5951,8 @@
         special_input_(HUserRecord<HInstruction*>(current_method)),
         type_index_(type_index),
         dex_file_(dex_file),
-        klass_(klass) {
+        klass_(klass),
+        loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
     // Referrers class should not need access check. We never inline unverified
     // methods so we can't possibly end up in this situation.
     DCHECK(!is_referrers_class || !needs_access_check);
@@ -5961,7 +5962,6 @@
     SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
     SetPackedFlag<kFlagIsInBootImage>(false);
     SetPackedFlag<kFlagGenerateClInitCheck>(false);
-    SetPackedFlag<kFlagValidLoadedClassRTI>(false);
   }
 
   bool IsClonable() const OVERRIDE { return true; }
@@ -6010,18 +6010,13 @@
   }
 
   ReferenceTypeInfo GetLoadedClassRTI() {
-    if (GetPackedFlag<kFlagValidLoadedClassRTI>()) {
-      // Note: The is_exact flag from the return value should not be used.
-      return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true);
-    } else {
-      return ReferenceTypeInfo::CreateInvalid();
-    }
+    return loaded_class_rti_;
   }
 
-  // Loaded class RTI is marked as valid by RTP if the klass_ is admissible.
-  void SetValidLoadedClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(klass_ != nullptr);
-    SetPackedFlag<kFlagValidLoadedClassRTI>(true);
+  void SetLoadedClassRTI(ReferenceTypeInfo rti) {
+    // Make sure we only set exact types (the loaded class should never be merged).
+    DCHECK(rti.IsExact());
+    loaded_class_rti_ = rti;
   }
 
   dex::TypeIndex GetTypeIndex() const { return type_index_; }
@@ -6074,8 +6069,7 @@
   static constexpr size_t kFieldLoadKind           = kFlagGenerateClInitCheck + 1;
   static constexpr size_t kFieldLoadKindSize =
       MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
-  static constexpr size_t kFlagValidLoadedClassRTI = kFieldLoadKind + kFieldLoadKindSize;
-  static constexpr size_t kNumberOfLoadClassPackedBits = kFlagValidLoadedClassRTI + 1;
+  static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize;
   static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields.");
   using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>;
 
@@ -6103,6 +6097,8 @@
   const DexFile& dex_file_;
 
   Handle<mirror::Class> klass_;
+
+  ReferenceTypeInfo loaded_class_rti_;
 };
 std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs);
 
@@ -6630,143 +6626,49 @@
   kInterfaceCheck,        // No optimization yet when checking against an interface.
   kArrayObjectCheck,      // Can just check if the array is not primitive.
   kArrayCheck,            // No optimization yet when checking against a generic array.
-  kBitstringCheck,        // Compare the type check bitstring.
   kLast = kArrayCheck
 };
 
 std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs);
 
-// Note: HTypeCheckInstruction is just a helper class, not an abstract instruction with an
-// `IsTypeCheckInstruction()`. (New virtual methods in the HInstruction class have a high cost.)
-class HTypeCheckInstruction : public HVariableInputSizeInstruction {
+class HInstanceOf FINAL : public HExpression<2> {
  public:
-  HTypeCheckInstruction(HInstruction* object,
-                        HInstruction* target_class_or_null,
-                        TypeCheckKind check_kind,
-                        Handle<mirror::Class> klass,
-                        uint32_t dex_pc,
-                        ArenaAllocator* allocator,
-                        HIntConstant* bitstring_path_to_root,
-                        HIntConstant* bitstring_mask,
-                        SideEffects side_effects)
-      : HVariableInputSizeInstruction(
-          side_effects,
-          dex_pc,
-          allocator,
-          /* number_of_inputs */ check_kind == TypeCheckKind::kBitstringCheck ? 4u : 2u,
-          kArenaAllocTypeCheckInputs),
-        klass_(klass) {
+  HInstanceOf(HInstruction* object,
+              HLoadClass* target_class,
+              TypeCheckKind check_kind,
+              uint32_t dex_pc)
+      : HExpression(DataType::Type::kBool,
+                    SideEffectsForArchRuntimeCalls(check_kind),
+                    dex_pc) {
     SetPackedField<TypeCheckKindField>(check_kind);
     SetPackedFlag<kFlagMustDoNullCheck>(true);
-    SetPackedFlag<kFlagValidTargetClassRTI>(false);
     SetRawInputAt(0, object);
-    SetRawInputAt(1, target_class_or_null);
-    DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_path_to_root != nullptr);
-    DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_mask != nullptr);
-    if (check_kind == TypeCheckKind::kBitstringCheck) {
-      DCHECK(target_class_or_null->IsNullConstant());
-      SetRawInputAt(2, bitstring_path_to_root);
-      SetRawInputAt(3, bitstring_mask);
-    } else {
-      DCHECK(target_class_or_null->IsLoadClass());
-    }
+    SetRawInputAt(1, target_class);
   }
 
   HLoadClass* GetTargetClass() const {
-    DCHECK_NE(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);
     HInstruction* load_class = InputAt(1);
     DCHECK(load_class->IsLoadClass());
     return load_class->AsLoadClass();
   }
 
-  uint32_t GetBitstringPathToRoot() const {
-    DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);
-    HInstruction* path_to_root = InputAt(2);
-    DCHECK(path_to_root->IsIntConstant());
-    return static_cast<uint32_t>(path_to_root->AsIntConstant()->GetValue());
-  }
-
-  uint32_t GetBitstringMask() const {
-    DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);
-    HInstruction* mask = InputAt(3);
-    DCHECK(mask->IsIntConstant());
-    return static_cast<uint32_t>(mask->AsIntConstant()->GetValue());
-  }
-
   bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
 
-  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
-    DCHECK(other->IsInstanceOf() || other->IsCheckCast()) << other->DebugName();
-    return GetPackedFields() == down_cast<const HTypeCheckInstruction*>(other)->GetPackedFields();
+  bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
+    return true;
   }
 
-  bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
-  void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
-  TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
-  bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
-
-  ReferenceTypeInfo GetTargetClassRTI() {
-    if (GetPackedFlag<kFlagValidTargetClassRTI>()) {
-      // Note: The is_exact flag from the return value should not be used.
-      return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true);
-    } else {
-      return ReferenceTypeInfo::CreateInvalid();
-    }
-  }
-
-  // Target class RTI is marked as valid by RTP if the klass_ is admissible.
-  void SetValidTargetClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(klass_ != nullptr);
-    SetPackedFlag<kFlagValidTargetClassRTI>(true);
-  }
-
-  Handle<mirror::Class> GetClass() const {
-    return klass_;
-  }
-
- protected:
-  DEFAULT_COPY_CONSTRUCTOR(TypeCheckInstruction);
-
- private:
-  static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
-  static constexpr size_t kFieldTypeCheckKindSize =
-      MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
-  static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
-  static constexpr size_t kFlagValidTargetClassRTI = kFlagMustDoNullCheck + 1;
-  static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagValidTargetClassRTI + 1;
-  static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-  using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
-
-  Handle<mirror::Class> klass_;
-};
-
-class HInstanceOf FINAL : public HTypeCheckInstruction {
- public:
-  HInstanceOf(HInstruction* object,
-              HInstruction* target_class_or_null,
-              TypeCheckKind check_kind,
-              Handle<mirror::Class> klass,
-              uint32_t dex_pc,
-              ArenaAllocator* allocator,
-              HIntConstant* bitstring_path_to_root,
-              HIntConstant* bitstring_mask)
-      : HTypeCheckInstruction(object,
-                              target_class_or_null,
-                              check_kind,
-                              klass,
-                              dex_pc,
-                              allocator,
-                              bitstring_path_to_root,
-                              bitstring_mask,
-                              SideEffectsForArchRuntimeCalls(check_kind)) {}
-
-  DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; }
-
   bool NeedsEnvironment() const OVERRIDE {
     return CanCallRuntime(GetTypeCheckKind());
   }
 
+  // Used only in code generation.
+  bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
+  void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
+  TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
+  bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
+
   static bool CanCallRuntime(TypeCheckKind check_kind) {
     // Mips currently does runtime calls for any other checks.
     return check_kind != TypeCheckKind::kExactCheck;
@@ -6780,6 +6682,15 @@
 
  protected:
   DEFAULT_COPY_CONSTRUCTOR(InstanceOf);
+
+ private:
+  static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits;
+  static constexpr size_t kFieldTypeCheckKindSize =
+      MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
+  static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
+  static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1;
+  static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+  using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
 };
 
 class HBoundType FINAL : public HExpression<1> {
@@ -6829,25 +6740,31 @@
   ReferenceTypeInfo upper_bound_;
 };
 
-class HCheckCast FINAL : public HTypeCheckInstruction {
+class HCheckCast FINAL : public HTemplateInstruction<2> {
  public:
   HCheckCast(HInstruction* object,
-             HInstruction* target_class_or_null,
+             HLoadClass* target_class,
              TypeCheckKind check_kind,
-             Handle<mirror::Class> klass,
-             uint32_t dex_pc,
-             ArenaAllocator* allocator,
-             HIntConstant* bitstring_path_to_root,
-             HIntConstant* bitstring_mask)
-      : HTypeCheckInstruction(object,
-                              target_class_or_null,
-                              check_kind,
-                              klass,
-                              dex_pc,
-                              allocator,
-                              bitstring_path_to_root,
-                              bitstring_mask,
-                              SideEffects::CanTriggerGC()) {}
+             uint32_t dex_pc)
+      : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
+    SetPackedField<TypeCheckKindField>(check_kind);
+    SetPackedFlag<kFlagMustDoNullCheck>(true);
+    SetRawInputAt(0, object);
+    SetRawInputAt(1, target_class);
+  }
+
+  HLoadClass* GetTargetClass() const {
+    HInstruction* load_class = InputAt(1);
+    DCHECK(load_class->IsLoadClass());
+    return load_class->AsLoadClass();
+  }
+
+  bool IsClonable() const OVERRIDE { return true; }
+  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
+    return true;
+  }
 
   bool NeedsEnvironment() const OVERRIDE {
     // Instruction may throw a CheckCastError.
@@ -6856,10 +6773,24 @@
 
   bool CanThrow() const OVERRIDE { return true; }
 
+  bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
+  void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
+  TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
+  bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
+
   DECLARE_INSTRUCTION(CheckCast);
 
  protected:
   DEFAULT_COPY_CONSTRUCTOR(CheckCast);
+
+ private:
+  static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
+  static constexpr size_t kFieldTypeCheckKindSize =
+      MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
+  static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
+  static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1;
+  static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+  using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
 };
 
 /**
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index a6a2f46..0023265 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -99,7 +99,6 @@
   kConstructorFenceRemovedLSE,
   kConstructorFenceRemovedPFRA,
   kConstructorFenceRemovedCFRE,
-  kBitstringTypeCheck,
   kJitOutOfMemoryForCommit,
   kLastStat
 };
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 5973339..f843c00 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -34,20 +34,6 @@
   }
 }
 
-void PrepareForRegisterAllocation::VisitCheckCast(HCheckCast* check_cast) {
-  // Record only those bitstring type checks that make it to the codegen stage.
-  if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
-    MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck);
-  }
-}
-
-void PrepareForRegisterAllocation::VisitInstanceOf(HInstanceOf* instance_of) {
-  // Record only those bitstring type checks that make it to the codegen stage.
-  if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
-    MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck);
-  }
-}
-
 void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) {
   check->ReplaceWith(check->InputAt(0));
 }
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index f6e4d3e..2c64f01 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -40,8 +40,6 @@
       "prepare_for_register_allocation";
 
  private:
-  void VisitCheckCast(HCheckCast* check_cast) OVERRIDE;
-  void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE;
   void VisitNullCheck(HNullCheck* check) OVERRIDE;
   void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE;
   void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 178d7fd..8bb124e 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -87,7 +87,6 @@
   void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE;
   void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
   void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
-  void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE;
   void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
   void VisitLoadString(HLoadString* instr) OVERRIDE;
   void VisitLoadException(HLoadException* instr) OVERRIDE;
@@ -172,12 +171,6 @@
                 << "NullCheck " << instr->GetReferenceTypeInfo()
                 << "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo();
           }
-        } else if (instr->IsInstanceOf()) {
-          HInstanceOf* iof = instr->AsInstanceOf();
-          DCHECK(!iof->GetTargetClassRTI().IsValid() || iof->GetTargetClassRTI().IsExact());
-        } else if (instr->IsCheckCast()) {
-          HCheckCast* check = instr->AsCheckCast();
-          DCHECK(!check->GetTargetClassRTI().IsValid() || check->GetTargetClassRTI().IsExact());
         }
       }
     }
@@ -506,7 +499,8 @@
     return;
   }
 
-  ReferenceTypeInfo class_rti = instanceOf->GetTargetClassRTI();
+  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;
@@ -650,20 +644,15 @@
 
 void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) {
   ScopedObjectAccess soa(Thread::Current());
-  if (IsAdmissible(instr->GetClass().Get())) {
-    instr->SetValidLoadedClassRTI();
+  Handle<mirror::Class> resolved_class = instr->GetClass();
+  if (IsAdmissible(resolved_class.Get())) {
+    instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(
+        resolved_class, /* is_exact */ true));
   }
   instr->SetReferenceTypeInfo(
       ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true));
 }
 
-void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) {
-  ScopedObjectAccess soa(Thread::Current());
-  if (IsAdmissible(instr->GetClass().Get())) {
-    instr->SetValidTargetClassRTI();
-  }
-}
-
 void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) {
   instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());
 }
@@ -731,6 +720,8 @@
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
+  HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
+  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
   HBoundType* bound_type = check_cast->GetNext()->AsBoundType();
   if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) {
     // The next instruction is not an uninitialized BoundType. This must be
@@ -739,14 +730,12 @@
   }
   DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0));
 
-  ScopedObjectAccess soa(Thread::Current());
-  Handle<mirror::Class> klass = check_cast->GetClass();
-  if (IsAdmissible(klass.Get())) {
+  if (class_rti.IsValid()) {
     DCHECK(is_first_run_);
-    check_cast->SetValidTargetClassRTI();
+    ScopedObjectAccess soa(Thread::Current());
     // This is the first run of RTP and class is resolved.
-    bool is_exact = klass->CannotBeAssignedFromOtherTypes();
-    bound_type->SetUpperBound(ReferenceTypeInfo::Create(klass, is_exact),
+    bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
+    bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact),
                               /* CheckCast succeeds for nulls. */ true);
   } else {
     // This is the first run of RTP and class is unresolved. Remove the binding.
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index dffef17..1e49411 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -236,75 +236,6 @@
   return load_kind;
 }
 
-static inline bool CanUseTypeCheckBitstring(ObjPtr<mirror::Class> klass,
-                                            CodeGenerator* codegen,
-                                            CompilerDriver* compiler_driver)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  DCHECK(!klass->IsProxyClass());
-  DCHECK(!klass->IsArrayClass());
-
-  if (Runtime::Current()->UseJitCompilation()) {
-    // If we're JITting, try to assign a type check bitstring (fall through).
-  } else if (codegen->GetCompilerOptions().IsBootImage()) {
-    const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex());
-    if (!compiler_driver->IsImageClass(descriptor)) {
-      return false;
-    }
-    // If the target is a boot image class, try to assign a type check bitstring (fall through).
-    // (If --force-determinism, this was already done; repeating is OK and yields the same result.)
-  } else {
-    // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring
-    // already assigned in the boot image.
-    return false;
-  }
-
-  // Try to assign a type check bitstring.
-  MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
-  if ((false) &&  // FIXME: Inliner does not respect compiler_driver->IsClassToCompile()
-                  // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569
-      kIsDebugBuild &&
-      codegen->GetCompilerOptions().IsBootImage() &&
-      codegen->GetCompilerOptions().IsForceDeterminism()) {
-    SubtypeCheckInfo::State old_state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass);
-    CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed)
-        << klass->PrettyDescriptor() << "/" << old_state
-        << " in " << codegen->GetGraph()->PrettyMethod();
-  }
-  SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass);
-  return state == SubtypeCheckInfo::kAssigned;
-}
-
-TypeCheckKind HSharpening::ComputeTypeCheckKind(ObjPtr<mirror::Class> klass,
-                                                CodeGenerator* codegen,
-                                                CompilerDriver* compiler_driver,
-                                                bool needs_access_check) {
-  if (klass == nullptr) {
-    return TypeCheckKind::kUnresolvedCheck;
-  } else if (klass->IsInterface()) {
-    return TypeCheckKind::kInterfaceCheck;
-  } else if (klass->IsArrayClass()) {
-    if (klass->GetComponentType()->IsObjectClass()) {
-      return TypeCheckKind::kArrayObjectCheck;
-    } else if (klass->CannotBeAssignedFromOtherTypes()) {
-      return TypeCheckKind::kExactCheck;
-    } else {
-      return TypeCheckKind::kArrayCheck;
-    }
-  } else if (klass->IsFinal()) {  // TODO: Consider using bitstring for final classes.
-    return TypeCheckKind::kExactCheck;
-  } else if (kUseBitstringTypeCheck &&
-             !needs_access_check &&
-             CanUseTypeCheckBitstring(klass, codegen, compiler_driver)) {
-    // TODO: We should not need the `!needs_access_check` check but getting rid of that
-    // requires rewriting some optimizations in instruction simplifier.
-    return TypeCheckKind::kBitstringCheck;
-  } else if (klass->IsAbstract()) {
-    return TypeCheckKind::kAbstractClassCheck;
-  } else {
-    return TypeCheckKind::kClassHierarchyCheck;
-  }
-}
-
 void HSharpening::ProcessLoadString(
     HLoadString* load_string,
     CodeGenerator* codegen,
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index fa3e948..6df7d6d 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -44,10 +44,12 @@
 
   static constexpr const char* kSharpeningPassName = "sharpening";
 
-  // Used by Sharpening and InstructionSimplifier.
-  static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke,
-                                          CodeGenerator* codegen,
-                                          CompilerDriver* compiler_driver);
+  // Used by the builder.
+  static void ProcessLoadString(HLoadString* load_string,
+                                CodeGenerator* codegen,
+                                CompilerDriver* compiler_driver,
+                                const DexCompilationUnit& dex_compilation_unit,
+                                VariableSizedHandleScope* handles);
 
   // Used by the builder and the inliner.
   static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class,
@@ -56,19 +58,10 @@
                                                    const DexCompilationUnit& dex_compilation_unit)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Used by the builder.
-  static TypeCheckKind ComputeTypeCheckKind(ObjPtr<mirror::Class> klass,
-                                            CodeGenerator* codegen,
-                                            CompilerDriver* compiler_driver,
-                                            bool needs_access_check)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // Used by the builder.
-  static void ProcessLoadString(HLoadString* load_string,
-                                CodeGenerator* codegen,
-                                CompilerDriver* compiler_driver,
-                                const DexCompilationUnit& dex_compilation_unit,
-                                VariableSizedHandleScope* handles);
+  // Used by Sharpening and InstructionSimplifier.
+  static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke,
+                                          CodeGenerator* codegen,
+                                          CompilerDriver* compiler_driver);
 
  private:
   CodeGenerator* codegen_;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 1671a24..737d2a8 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -794,9 +794,6 @@
     .extern artInstanceOfFromCode
     .extern artThrowClassCastExceptionForObject
 ENTRY art_quick_check_instance_of
-    // Type check using the bit string passes null as the target class. In that case just throw.
-    cbz r1, .Lthrow_class_cast_exception_for_bitstring_check
-
     push {r0-r2, lr}                    @ save arguments, padding (r2) and link register
     .cfi_adjust_cfa_offset 16
     .cfi_rel_offset r0, 0
@@ -815,7 +812,6 @@
     .cfi_restore r2
     .cfi_restore lr
 
-.Lthrow_class_cast_exception_for_bitstring_check:
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2       @ save all registers as basis for long jump context
     mov r2, r9                      @ pass Thread::Current
     bl  artThrowClassCastExceptionForObject  @ (Object*, Class*, Thread*)
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 0614118..b0e7b0a 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1333,9 +1333,6 @@
     .extern artInstanceOfFromCode
     .extern artThrowClassCastExceptionForObject
 ENTRY art_quick_check_instance_of
-    // Type check using the bit string passes null as the target class. In that case just throw.
-    cbz x1, .Lthrow_class_cast_exception_for_bitstring_check
-
     // Store arguments and link register
     // Stack needs to be 16B aligned on calls.
     SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32
@@ -1361,7 +1358,6 @@
     // Restore
     RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
 
-.Lthrow_class_cast_exception_for_bitstring_check:
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context
     mov x2, xSELF                     // pass Thread::Current
     bl artThrowClassCastExceptionForObject     // (Object*, Class*, Thread*)
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index d8fe480..b2f7e10 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1423,10 +1423,6 @@
     .extern artInstanceOfFromCode
     .extern artThrowClassCastExceptionForObject
 ENTRY art_quick_check_instance_of
-    // Type check using the bit string passes null as the target class. In that case just throw.
-    beqz   $a1, .Lthrow_class_cast_exception_for_bitstring_check
-    nop
-
     addiu  $sp, $sp, -32
     .cfi_adjust_cfa_offset 32
     sw     $gp, 16($sp)
@@ -1445,15 +1441,12 @@
     jalr   $zero, $ra
     addiu  $sp, $sp, 32
     .cfi_adjust_cfa_offset -32
-
 .Lthrow_class_cast_exception:
     lw     $t9, 8($sp)
     lw     $a1, 4($sp)
     lw     $a0, 0($sp)
     addiu  $sp, $sp, 32
     .cfi_adjust_cfa_offset -32
-
-.Lthrow_class_cast_exception_for_bitstring_check:
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
     la   $t9, artThrowClassCastExceptionForObject
     jalr $zero, $t9                 # artThrowClassCastException (Object*, Class*, Thread*)
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index a5edc1f..63f4f6c 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1364,9 +1364,6 @@
     .extern artInstanceOfFromCode
     .extern artThrowClassCastExceptionForObject
 ENTRY art_quick_check_instance_of
-    // Type check using the bit string passes null as the target class. In that case just throw.
-    beqzc  $a1, .Lthrow_class_cast_exception_for_bitstring_check
-
     daddiu $sp, $sp, -32
     .cfi_adjust_cfa_offset 32
     sd     $ra, 24($sp)
@@ -1382,15 +1379,12 @@
     jalr   $zero, $ra
     daddiu $sp, $sp, 32
     .cfi_adjust_cfa_offset -32
-
 .Lthrow_class_cast_exception:
     ld     $t9, 16($sp)
     ld     $a1, 8($sp)
     ld     $a0, 0($sp)
     daddiu $sp, $sp, 32
     .cfi_adjust_cfa_offset -32
-
-.Lthrow_class_cast_exception_for_bitstring_check:
     SETUP_GP
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
     dla  $t9, artThrowClassCastExceptionForObject
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index d64e2fd..5a28120 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1431,10 +1431,6 @@
 END_FUNCTION art_quick_instance_of
 
 DEFINE_FUNCTION art_quick_check_instance_of
-    // Type check using the bit string passes null as the target class. In that case just throw.
-    testl %ecx, %ecx
-    jz .Lthrow_class_cast_exception_for_bitstring_check
-
     PUSH eax                              // alignment padding
     PUSH ecx                              // pass arg2 - checked class
     PUSH eax                              // pass arg1 - obj
@@ -1452,7 +1448,6 @@
     addl LITERAL(4), %esp
     CFI_ADJUST_CFA_OFFSET(-4)
 
-.Lthrow_class_cast_exception_for_bitstring_check:
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx // save all registers as basis for long jump context
     // Outgoing argument set up
     PUSH eax                              // alignment padding
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 81ad780..781ade9 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1402,10 +1402,6 @@
 END_FUNCTION art_quick_unlock_object_no_inline
 
 DEFINE_FUNCTION art_quick_check_instance_of
-    // Type check using the bit string passes null as the target class. In that case just throw.
-    testl %esi, %esi
-    jz .Lthrow_class_cast_exception_for_bitstring_check
-
     // We could check the super classes here but that is usually already checked in the caller.
     PUSH rdi                          // Save args for exc
     PUSH rsi
@@ -1429,7 +1425,6 @@
     POP rsi                           // Pop arguments
     POP rdi
 
-.Lthrow_class_cast_exception_for_bitstring_check:
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context
     mov %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
     call SYMBOL(artThrowClassCastExceptionForObject)  // (Object* src, Class* dest, Thread*)
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 0fcf394..e87f631 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -56,7 +56,6 @@
   "CtorFenceIns ",
   "InvokeInputs ",
   "PhiInputs    ",
-  "TypeCheckIns ",
   "LoopInfo     ",
   "LIBackEdges  ",
   "TryCatchInf  ",
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 5f3fc02a..beaba67 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -62,7 +62,6 @@
   kArenaAllocConstructorFenceInputs,
   kArenaAllocInvokeInputs,
   kArenaAllocPhiInputs,
-  kArenaAllocTypeCheckInputs,
   kArenaAllocLoopInfo,
   kArenaAllocLoopInfoBackEdges,
   kArenaAllocTryCatchInfo,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index e7ee9f2..af45a69 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4482,14 +4482,6 @@
 
   Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(temp_klass, klass);
 
-  // SubtypeCheckInfo::Initialized must happen-before any new-instance for that type.
-  // See also ClassLinker::EnsureInitialized().
-  {
-    MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
-    SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(klass.Get());
-    // TODO: Avoid taking subtype_check_lock_ if SubtypeCheck for j.l.r.Proxy is already assigned.
-  }
-
   {
     // Lock on klass is released. Lock new class object.
     ObjectLock<mirror::Class> initialization_lock(self, klass);
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 4b26bee..565b4ed 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -15,11 +15,8 @@
  */
 
 #include "callee_save_frame.h"
-#include "dex/code_item_accessors-inl.h"
-#include "dex/dex_instruction-inl.h"
 #include "common_throws.h"
 #include "mirror/object-inl.h"
-#include "nth_caller_visitor.h"
 #include "thread.h"
 #include "well_known_classes.h"
 
@@ -114,21 +111,6 @@
                                                      Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
-  if (dest_type == nullptr) {
-    // Find the target class for check cast using the bitstring check (dest_type == null).
-    NthCallerVisitor visitor(self, 0u);
-    visitor.WalkStack();
-    DCHECK(visitor.caller != nullptr);
-    uint32_t dex_pc = visitor.GetDexPc();
-    CodeItemDataAccessor accessor(visitor.caller);
-    const Instruction& check_cast = accessor.InstructionAt(dex_pc);
-    DCHECK_EQ(check_cast.Opcode(), Instruction::CHECK_CAST);
-    dex::TypeIndex type_index(check_cast.VRegB_21c());
-    ClassLinker* linker = Runtime::Current()->GetClassLinker();
-    dest_type = linker->LookupResolvedType(type_index, visitor.caller).Ptr();
-    CHECK(dest_type != nullptr) << "Target class should have been previously resolved: "
-        << visitor.caller->GetDexFile()->PrettyType(type_index);
-  }
   DCHECK(!dest_type->IsAssignableFrom(src_type));
   ThrowClassCastException(dest_type, src_type);
   self->QuickDeliverException();
diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h
index 03a6d9c..54d2f00 100644
--- a/runtime/subtype_check.h
+++ b/runtime/subtype_check.h
@@ -283,17 +283,6 @@
     return SubtypeCheckInfo::kUninitialized;
   }
 
-  // Retrieve the state of this class's SubtypeCheckInfo.
-  //
-  // Cost: O(Depth(Class)).
-  //
-  // Returns: The precise SubtypeCheckInfo::State.
-  static SubtypeCheckInfo::State GetState(ClassPtr klass)
-      REQUIRES(Locks::subtype_check_lock_)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetSubtypeCheckInfo(klass).GetState();
-  }
-
   // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to
   // be used by a fast check "encoded_src & mask_target == encoded_target".
   //
@@ -316,9 +305,8 @@
   static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass)
       REQUIRES(Locks::subtype_check_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass);
-    DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState());
-    return sci.GetEncodedPathToRoot();
+    DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState());
+    return GetSubtypeCheckInfo(klass).GetEncodedPathToRoot();
   }
 
   // Retrieve the path to root bitstring mask as a plain uintN_t value that is amenable to
@@ -330,9 +318,8 @@
   static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass)
       REQUIRES(Locks::subtype_check_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass);
-    DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState());
-    return sci.GetEncodedPathToRootMask();
+    DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState());
+    return GetSubtypeCheckInfo(klass).GetEncodedPathToRootMask();
   }
 
   // Is the source class a subclass of the target?
diff --git a/test/670-bitstring-type-check/build b/test/670-bitstring-type-check/build
deleted file mode 100644
index 38307f2..0000000
--- a/test/670-bitstring-type-check/build
+++ /dev/null
@@ -1,216 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2018 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.
-
-# Stop if something fails.
-set -e
-
-# Write out the source file.
-
-mkdir src
-cat >src/Main.java <<EOF
-/*
- * Copyright (C) 2018 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.
- */
-
-EOF
-
-for i in {0..8192}; do echo "class Level1Class$i { }" >>src/Main.java; done
-for i in {0..1024}; do echo "class Level2Class$i extends Level1Class0 { }" >>src/Main.java; done
-
-cat >>src/Main.java <<EOF
-class Level3Class0 extends Level2Class0 { }
-class Level4Class0 extends Level3Class0 { }
-class Level5Class0 extends Level4Class0 { }
-class Level6Class0 extends Level5Class0 { }
-class Level7Class0 extends Level6Class0 { }
-class Level8Class0 extends Level7Class0 { }
-class Level9Class0 extends Level8Class0 { }
-
-public class Main {
-  public static void main(String[] args) throws Exception {
-    // 8193 classes at level 1 make sure we shall have an overflow if there are 13 or
-    // less bits for the level 1 character. 1025 classes at level 2 similarly guarantees
-    // an overflow if the number of bits for level 2 character is 10 or less. To test
-    // type checks also for the depth overflow, we provide a hierarchy 9 levels deep.
-
-    // Make sure the bitstrings are initialized.
-    for (int i = 0; i <= 8192; ++i) {
-      Class.forName("Level1Class" + i).newInstance();
-    }
-    for (int i = 0; i <= 1024; ++i) {
-      Class.forName("Level2Class" + i).newInstance();
-    }
-
-    // Note: Using a different class for tests so that verification of Main.main() does
-    // not try to resolve classes used by the tests. This guarantees uninitialized type
-    // check bitstrings when we enter Main.main() and start initializing them above.
-    Helper.testInstanceOf();
-    Helper.testCheckCast();
-  }
-}
-
-class Helper {
-  public static void testInstanceOf() throws Exception {
-    for (int i = 1; i <= 9; ++i) {
-      Object o = createInstance("Level" + i + "Class0");
-      assertTrue(o instanceof Level1Class0);
-      if (o instanceof Level2Class0) {
-        assertFalse(i < 2);
-      } else {
-        assertTrue(i < 2);
-      }
-      if (o instanceof Level3Class0) {
-        assertFalse(i < 3);
-      } else {
-        assertTrue(i < 3);
-      }
-      if (o instanceof Level4Class0) {
-        assertFalse(i < 4);
-      } else {
-        assertTrue(i < 4);
-      }
-      if (o instanceof Level5Class0) {
-        assertFalse(i < 5);
-      } else {
-        assertTrue(i < 5);
-      }
-      if (o instanceof Level6Class0) {
-        assertFalse(i < 6);
-      } else {
-        assertTrue(i < 6);
-      }
-      if (o instanceof Level7Class0) {
-        assertFalse(i < 7);
-      } else {
-        assertTrue(i < 7);
-      }
-      if (o instanceof Level8Class0) {
-        assertFalse(i < 8);
-      } else {
-        assertTrue(i < 8);
-      }
-      if (o instanceof Level9Class0) {
-        assertFalse(i < 9);
-      } else {
-        assertTrue(i < 9);
-      }
-    }
-
-    assertTrue(createInstance("Level1Class8192") instanceof Level1Class8192);
-    assertFalse(createInstance("Level1Class8192") instanceof Level1Class0);
-    assertTrue(createInstance("Level2Class1024") instanceof Level2Class1024);
-    assertTrue(createInstance("Level2Class1024") instanceof Level1Class0);
-    assertFalse(createInstance("Level2Class1024") instanceof Level2Class0);
-  }
-
-  public static void testCheckCast() throws Exception {
-    for (int i = 1; i <= 9; ++i) {
-      Object o = createInstance("Level" + i + "Class0");
-      Level1Class0 l1c0 = (Level1Class0) o;
-      try {
-        Level2Class0 l2c0 = (Level2Class0) o;
-        assertFalse(i < 2);
-      } catch (ClassCastException cce) {
-        assertTrue(i < 2);
-      }
-      try {
-        Level3Class0 l3c0 = (Level3Class0) o;
-        assertFalse(i < 3);
-      } catch (ClassCastException cce) {
-        assertTrue(i < 3);
-      }
-      try {
-        Level4Class0 l4c0 = (Level4Class0) o;
-        assertFalse(i < 4);
-      } catch (ClassCastException cce) {
-        assertTrue(i < 4);
-      }
-      try {
-        Level5Class0 l5c0 = (Level5Class0) o;
-        assertFalse(i < 5);
-      } catch (ClassCastException cce) {
-        assertTrue(i < 5);
-      }
-      try {
-        Level6Class0 l6c0 = (Level6Class0) o;
-        assertFalse(i < 6);
-      } catch (ClassCastException cce) {
-        assertTrue(i < 6);
-      }
-      try {
-        Level7Class0 l7c0 = (Level7Class0) o;
-        assertFalse(i < 7);
-      } catch (ClassCastException cce) {
-        assertTrue(i < 7);
-      }
-      try {
-        Level8Class0 l8c0 = (Level8Class0) o;
-        assertFalse(i < 8);
-      } catch (ClassCastException cce) {
-        assertTrue(i < 8);
-      }
-      try {
-        Level9Class0 l9c0 = (Level9Class0) o;
-        assertFalse(i < 9);
-      } catch (ClassCastException cce) {
-        assertTrue(i < 9);
-      }
-    }
-
-    Level1Class8192 l1c8192 = (Level1Class8192) createInstance("Level1Class8192");
-    try {
-      Level1Class0 l1c0 = (Level1Class0) createInstance("Level1Class8192");
-      throw new AssertionError("Unexpected");
-    } catch (ClassCastException expected) {}
-    Level2Class1024 l2c1024 = (Level2Class1024) createInstance("Level2Class1024");
-    Level1Class0 l1c0 = (Level1Class0) createInstance("Level2Class1024");
-    try {
-      Level2Class0 l2c0 = (Level2Class0) createInstance("Level2Class1024");
-      throw new AssertionError("Unexpected");
-    } catch (ClassCastException expected) {}
-  }
-
-  public static Object createInstance(String className) throws Exception {
-    return Class.forName(className).newInstance();
-  }
-
-  public static void assertTrue(boolean value) throws Exception {
-    if (!value) {
-      throw new AssertionError();
-    }
-  }
-
-  public static void assertFalse(boolean value) throws Exception {
-    if (value) {
-      throw new AssertionError();
-    }
-  }
-}
-EOF
-
-./default-build "$@"
diff --git a/test/670-bitstring-type-check/expected.txt b/test/670-bitstring-type-check/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/670-bitstring-type-check/expected.txt
+++ /dev/null
diff --git a/test/670-bitstring-type-check/info.txt b/test/670-bitstring-type-check/info.txt
deleted file mode 100644
index a34ba86..0000000
--- a/test/670-bitstring-type-check/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for the bitstring type checks.