Split VERIFY_ERROR_GENERIC into VERIFY_ERROR_BAD_CLASS_HARD/SOFT.

The verifier was suppressing compilation of some classes that were able
to verify at runtime. Now VERIFY_ERROR_BAD_CLASS_HARD is used for
structural errors to prevent compilation, and
VERIFY_ERROR_BAD_CLASS_HARD is used for all other verify errors.

(cherry picked from commit da4dc735b9650f63e13b85c9775416006717d27c)

Change-Id: I339639fa94af30f93be12f1ab9d4ef4cec6767e6
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index aedd599..a5ab8cd 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -46,7 +46,8 @@
 std::ostream& operator<<(std::ostream& os, const VerifyError& rhs) {
   switch (rhs) {
   case VERIFY_ERROR_NONE: os << "VERIFY_ERROR_NONE"; break;
-  case VERIFY_ERROR_GENERIC: os << "VERIFY_ERROR_GENERIC"; break;
+  case VERIFY_ERROR_BAD_CLASS_HARD: os << "VERIFY_ERROR_BAD_CLASS_HARD"; break;
+  case VERIFY_ERROR_BAD_CLASS_SOFT: os << "VERIFY_ERROR_BAD_CLASS_SOFT"; break;
   case VERIFY_ERROR_NO_CLASS: os << "VERIFY_ERROR_NO_CLASS"; break;
   case VERIFY_ERROR_NO_FIELD: os << "VERIFY_ERROR_NO_FIELD"; break;
   case VERIFY_ERROR_NO_METHOD: os << "VERIFY_ERROR_NO_METHOD"; break;
@@ -573,7 +574,7 @@
 bool RegisterLine::CheckConstructorReturn() const {
   for (size_t i = 0; i < num_regs_; i++) {
     if (GetRegisterType(i).IsUninitializedThisReference()) {
-      verifier_->Fail(VERIFY_ERROR_GENERIC)
+      verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
           << "Constructor returning without calling superclass constructor";
       return false;
     }
@@ -588,10 +589,10 @@
     line_[vdst + 1] = new_type.HighHalf(verifier_->GetRegTypeCache()).GetId();
   } else if (new_type.IsHighHalf()) {
     /* should never set these explicitly */
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "Explicit set of high register type";
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Explicit set of high register type";
     return false;
   } else if (new_type.IsConflict()) {  // should only be set during a merge
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "Set register to unknown type " << new_type;
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
     return false;
   } else {
     line_[vdst] = new_type.GetId();
@@ -625,14 +626,14 @@
 
 const RegType& RegisterLine::GetInvocationThis(const DecodedInstruction& dec_insn) {
   if (dec_insn.vA < 1) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "invoke lacks 'this'";
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke lacks 'this'";
     return verifier_->GetRegTypeCache()->Unknown();
   }
   /* get the element type of the array held in vsrc */
   const RegType& this_type = GetRegisterType(dec_insn.vC);
   if (!this_type.IsReferenceTypes()) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "tried to get class from non-reference register v"
-                                          << dec_insn.vC << " (type=" << this_type << ")";
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "tried to get class from non-reference register v"
+                                                 << dec_insn.vC << " (type=" << this_type << ")";
     return verifier_->GetRegTypeCache()->Unknown();
   }
   return this_type;
@@ -642,15 +643,15 @@
   // Verify the src register type against the check type refining the type of the register
   const RegType& src_type = GetRegisterType(vsrc);
   if (!check_type.IsAssignableFrom(src_type)) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "register v" << vsrc << " has type " << src_type
-                                          << " but expected " << check_type;
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "register v" << vsrc << " has type " << src_type
+                                                 << " but expected " << check_type;
     return false;
   }
   if (check_type.IsLowHalf()) {
     const RegType& src_type_h = GetRegisterType(vsrc + 1);
     if (!src_type.CheckWidePair(src_type_h)) {
-      verifier_->Fail(VERIFY_ERROR_GENERIC) << "wide register v" << vsrc << " has type "
-                                            << src_type << "/" << src_type_h;
+      verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "wide register v" << vsrc << " has type "
+                                                   << src_type << "/" << src_type_h;
       return false;
     }
   }
@@ -691,8 +692,8 @@
   }
   if ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
       (cat == kTypeCategoryRef && !type.IsReferenceTypes())) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
-                                          << " cat=" << static_cast<int>(cat);
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
+                                                 << " cat=" << static_cast<int>(cat);
   } else if (cat == kTypeCategoryRef) {
     CopyRegToLockDepth(vdst, vsrc);
   }
@@ -703,8 +704,8 @@
   const RegType& type_h = GetRegisterType(vsrc + 1);
 
   if (!type_l.CheckWidePair(type_h)) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "copy2 v" << vdst << "<-v" << vsrc
-                                         << " type=" << type_l << "/" << type_h;
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy2 v" << vdst << "<-v" << vsrc
+                                                 << " type=" << type_l << "/" << type_h;
   } else {
     SetRegisterType(vdst, type_l);  // implicitly sets the second half
   }
@@ -714,7 +715,7 @@
   const RegType& type = verifier_->GetRegTypeCache()->GetFromId(result_[0]);
   if ((!is_reference && !type.IsCategory1Types()) ||
       (is_reference && !type.IsReferenceTypes())) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC)
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
         << "copyRes1 v" << vdst << "<- result0"  << " type=" << type;
   } else {
     DCHECK(verifier_->GetRegTypeCache()->GetFromId(result_[1]).IsUnknown());
@@ -731,7 +732,7 @@
   const RegType& type_l = verifier_->GetRegTypeCache()->GetFromId(result_[0]);
   const RegType& type_h = verifier_->GetRegTypeCache()->GetFromId(result_[1]);
   if (!type_l.IsCategory2Types()) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC)
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
         << "copyRes2 v" << vdst << "<- result0"  << " type=" << type_l;
   } else {
     DCHECK(type_l.CheckWidePair(type_h));  // Set should never allow this case
@@ -803,9 +804,9 @@
 void RegisterLine::PushMonitor(uint32_t reg_idx, int32_t insn_idx) {
   const RegType& reg_type = GetRegisterType(reg_idx);
   if (!reg_type.IsReferenceTypes()) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "monitor-enter on non-object (" << reg_type << ")";
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter on non-object (" << reg_type << ")";
   } else if (monitors_.size() >= 32) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "monitor-enter stack overflow: " << monitors_.size();
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter stack overflow: " << monitors_.size();
   } else {
     SetRegToLockDepth(reg_idx, monitors_.size());
     monitors_.push_back(insn_idx);
@@ -815,16 +816,16 @@
 void RegisterLine::PopMonitor(uint32_t reg_idx) {
   const RegType& reg_type = GetRegisterType(reg_idx);
   if (!reg_type.IsReferenceTypes()) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "monitor-exit on non-object (" << reg_type << ")";
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit on non-object (" << reg_type << ")";
   } else if (monitors_.empty()) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "monitor-exit stack underflow";
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit stack underflow";
   } else {
     monitors_.pop_back();
     if(!IsSetLockDepth(reg_idx, monitors_.size())) {
       // Bug 3215458: Locks and unlocks are on objects, if that object is a literal then before
       // format "036" the constant collector may create unlocks on the same object but referenced
       // via different registers.
-      ((verifier_->DexFileVersion() >= 36) ? verifier_->Fail(VERIFY_ERROR_GENERIC)
+      ((verifier_->DexFileVersion() >= 36) ? verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
                                            : verifier_->LogVerifyInfo())
             << "monitor-exit not unlocking the top of the monitor stack";
     } else {
@@ -836,7 +837,7 @@
 
 bool RegisterLine::VerifyMonitorStackEmpty() {
   if (MonitorStackDepth() != 0) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "expected empty monitor stack";
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected empty monitor stack";
     return false;
   } else {
     return true;
@@ -855,7 +856,7 @@
     }
   }
   if(monitors_.size() != incoming_line->monitors_.size()) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "mismatched stack depths (depth="
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "mismatched stack depths (depth="
         << MonitorStackDepth() << ", incoming depth=" << incoming_line->MonitorStackDepth() << ")";
   } else if (reg_to_lock_depths_ != incoming_line->reg_to_lock_depths_) {
     for (uint32_t idx = 0; idx < num_regs_; idx++) {
@@ -865,8 +866,8 @@
         if (depths == 0 || incoming_depths == 0) {
           reg_to_lock_depths_.erase(idx);
         } else {
-          verifier_->Fail(VERIFY_ERROR_GENERIC) << "mismatched stack depths for register v" << idx
-                                                << ": " << depths  << " != " << incoming_depths;
+          verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "mismatched stack depths for register v" << idx
+                                                       << ": " << depths  << " != " << incoming_depths;
           break;
         }
       }
@@ -985,7 +986,8 @@
       std::cout << std::endl << verifier.info_messages_.str();
       verifier.Dump(std::cout);
     }
-    DCHECK_EQ(verifier.failure_, VERIFY_ERROR_GENERIC);
+    DCHECK((verifier.failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) ||
+           (verifier.failure_ == VERIFY_ERROR_BAD_CLASS_HARD)) << verifier.failure_;
   }
   return success;
 }
@@ -1056,7 +1058,8 @@
       std::cout << std::endl << verifier.info_messages_.str();
       verifier.Dump(std::cout);
     }
-    DCHECK_EQ(verifier.failure_, VERIFY_ERROR_GENERIC);
+    DCHECK((verifier.failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) ||
+           (verifier.failure_ == VERIFY_ERROR_BAD_CLASS_HARD)) << verifier.failure_;
   }
   return success;
 }
@@ -1096,7 +1099,7 @@
   // If there aren't any instructions, make sure that's expected, then exit successfully.
   if (code_item_ == NULL) {
     if (!method_->IsNative() && !method_->IsAbstract()) {
-      Fail(VERIFY_ERROR_GENERIC) << "zero-length code in concrete non-native method";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
       return false;
     } else {
       return true;
@@ -1111,8 +1114,8 @@
   }
   // Sanity-check the register counts. ins + locals = registers, so make sure that ins <= registers.
   if (code_item_->ins_size_ > code_item_->registers_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "bad register counts (ins=" << code_item_->ins_size_
-                               << " regs=" << code_item_->registers_size_;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" << code_item_->ins_size_
+                                      << " regs=" << code_item_->registers_size_;
     return false;
   }
   // Allocate and initialize an array to hold instruction data.
@@ -1131,20 +1134,20 @@
   if (Runtime::Current()->IsCompiler()) {
     switch (error) {
       // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx
-      // errors into generic errors so that we re-verify at runtime. We may fail to find or to agree
-      // on access because of not yet available class loaders, or class loaders that will differ at
-      // runtime.
+      // errors into soft verification errors so that we re-verify at runtime. We may fail to find
+      // or to agree on access because of not yet available class loaders, or class loaders that
+      // will differ at runtime.
       case VERIFY_ERROR_NO_CLASS:
       case VERIFY_ERROR_NO_FIELD:
       case VERIFY_ERROR_NO_METHOD:
       case VERIFY_ERROR_ACCESS_CLASS:
       case VERIFY_ERROR_ACCESS_FIELD:
       case VERIFY_ERROR_ACCESS_METHOD:
-        error = VERIFY_ERROR_GENERIC;
+        error = VERIFY_ERROR_BAD_CLASS_SOFT;
         break;
-      // Generic failures at compile time will still fail at runtime, so the class is marked as
-      // rejected to prevent it from being compiled.
-      case VERIFY_ERROR_GENERIC: {
+      // Hard verification failures at compile time will still fail at runtime, so the class is
+      // marked as rejected to prevent it from being compiled.
+      case VERIFY_ERROR_BAD_CLASS_HARD: {
         Compiler::ClassReference ref(dex_file_, class_def_idx_);
         AddRejectedClass(ref);
         break;
@@ -1180,8 +1183,8 @@
   }
 
   if (dex_pc != insns_size) {
-    Fail(VERIFY_ERROR_GENERIC) << "code did not end where expected ("
-        << dex_pc << " vs. " << insns_size << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code did not end where expected ("
+                                      << dex_pc << " vs. " << insns_size << ")";
     return false;
   }
 
@@ -1203,12 +1206,12 @@
     uint32_t start = try_item->start_addr_;
     uint32_t end = start + try_item->insn_count_;
     if ((start >= end) || (start >= insns_size) || (end > insns_size)) {
-      Fail(VERIFY_ERROR_GENERIC) << "bad exception entry: startAddr=" << start
-                                 << " endAddr=" << end << " (size=" << insns_size << ")";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad exception entry: startAddr=" << start
+                                        << " endAddr=" << end << " (size=" << insns_size << ")";
       return false;
     }
     if (!insn_flags_[start].IsOpcode()) {
-      Fail(VERIFY_ERROR_GENERIC) << "'try' block starts inside an instruction (" << start << ")";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'try' block starts inside an instruction (" << start << ")";
       return false;
     }
     for (uint32_t dex_pc = start; dex_pc < end;
@@ -1225,12 +1228,12 @@
     for (; iterator.HasNext(); iterator.Next()) {
       uint32_t dex_pc= iterator.GetHandlerAddress();
       if (!insn_flags_[dex_pc].IsOpcode()) {
-        Fail(VERIFY_ERROR_GENERIC) << "exception handler starts at bad address (" << dex_pc << ")";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "exception handler starts at bad address (" << dex_pc << ")";
         return false;
       }
       const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
       if (inst->Opcode() != Instruction::MOVE_EXCEPTION) {
-        Fail(VERIFY_ERROR_GENERIC) << "exception handler doesn't start with move-exception ("
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "exception handler doesn't start with move-exception ("
                                    << dex_pc << ")";
         return false;
       }
@@ -1342,7 +1345,7 @@
       result = result && CheckVarArgRangeRegs(dec_insn.vA, dec_insn.vC);
       break;
     case Instruction::kVerifyError:
-      Fail(VERIFY_ERROR_GENERIC) << "unexpected opcode " << inst->Name();
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected opcode " << inst->Name();
       result = false;
       break;
   }
@@ -1351,8 +1354,8 @@
 
 bool DexVerifier::CheckRegisterIndex(uint32_t idx) {
   if (idx >= code_item_->registers_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "register index out of range (" << idx << " >= "
-                               << code_item_->registers_size_ << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register index out of range (" << idx << " >= "
+                                      << code_item_->registers_size_ << ")";
     return false;
   }
   return true;
@@ -1360,8 +1363,8 @@
 
 bool DexVerifier::CheckWideRegisterIndex(uint32_t idx) {
   if (idx + 1 >= code_item_->registers_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "wide register index out of range (" << idx
-                               << "+1 >= " << code_item_->registers_size_ << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register index out of range (" << idx
+                                      << "+1 >= " << code_item_->registers_size_ << ")";
     return false;
   }
   return true;
@@ -1369,8 +1372,8 @@
 
 bool DexVerifier::CheckFieldIndex(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().field_ids_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "bad field index " << idx << " (max "
-                               << dex_file_->GetHeader().field_ids_size_ << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad field index " << idx << " (max "
+                                      << dex_file_->GetHeader().field_ids_size_ << ")";
     return false;
   }
   return true;
@@ -1378,8 +1381,8 @@
 
 bool DexVerifier::CheckMethodIndex(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().method_ids_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "bad method index " << idx << " (max "
-                               << dex_file_->GetHeader().method_ids_size_ << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad method index " << idx << " (max "
+                                      << dex_file_->GetHeader().method_ids_size_ << ")";
     return false;
   }
   return true;
@@ -1387,14 +1390,14 @@
 
 bool DexVerifier::CheckNewInstance(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().type_ids_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "bad type index " << idx << " (max "
-                               << dex_file_->GetHeader().type_ids_size_ << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
+                                      << dex_file_->GetHeader().type_ids_size_ << ")";
     return false;
   }
   // We don't need the actual class, just a pointer to the class name.
   const char* descriptor = dex_file_->StringByTypeIdx(idx);
   if (descriptor[0] != 'L') {
-    Fail(VERIFY_ERROR_GENERIC) << "can't call new-instance on type '" << descriptor << "'";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't call new-instance on type '" << descriptor << "'";
     return false;
   }
   return true;
@@ -1402,8 +1405,8 @@
 
 bool DexVerifier::CheckStringIndex(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().string_ids_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "bad string index " << idx << " (max "
-                               << dex_file_->GetHeader().string_ids_size_ << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad string index " << idx << " (max "
+                                      << dex_file_->GetHeader().string_ids_size_ << ")";
     return false;
   }
   return true;
@@ -1411,8 +1414,8 @@
 
 bool DexVerifier::CheckTypeIndex(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().type_ids_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "bad type index " << idx << " (max "
-                               << dex_file_->GetHeader().type_ids_size_ << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
+                                      << dex_file_->GetHeader().type_ids_size_ << ")";
     return false;
   }
   return true;
@@ -1420,8 +1423,8 @@
 
 bool DexVerifier::CheckNewArray(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().type_ids_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "bad type index " << idx << " (max "
-                               << dex_file_->GetHeader().type_ids_size_ << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
+                                      << dex_file_->GetHeader().type_ids_size_ << ")";
     return false;
   }
   int bracket_count = 0;
@@ -1432,11 +1435,11 @@
   }
   if (bracket_count == 0) {
     /* The given class must be an array type. */
-    Fail(VERIFY_ERROR_GENERIC) << "can't new-array class '" << descriptor << "' (not an array)";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't new-array class '" << descriptor << "' (not an array)";
     return false;
   } else if (bracket_count > 255) {
     /* It is illegal to create an array of more than 255 dimensions. */
-    Fail(VERIFY_ERROR_GENERIC) << "can't new-array class '" << descriptor << "' (exceeds limit)";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't new-array class '" << descriptor << "' (exceeds limit)";
     return false;
   }
   return true;
@@ -1453,16 +1456,16 @@
   array_data_offset = insns[1] | (((int32_t) insns[2]) << 16);
   if ((int32_t) cur_offset + array_data_offset < 0 ||
       cur_offset + array_data_offset + 2 >= insn_count) {
-    Fail(VERIFY_ERROR_GENERIC) << "invalid array data start: at " << cur_offset
-                               << ", data offset " << array_data_offset << ", count " << insn_count;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid array data start: at " << cur_offset
+                                      << ", data offset " << array_data_offset << ", count " << insn_count;
     return false;
   }
   /* offset to array data table is a relative branch-style offset */
   array_data = insns + array_data_offset;
   /* make sure the table is 32-bit aligned */
   if ((((uint32_t) array_data) & 0x03) != 0) {
-    Fail(VERIFY_ERROR_GENERIC) << "unaligned array data table: at " << cur_offset
-                               << ", data offset " << array_data_offset;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unaligned array data table: at " << cur_offset
+                                      << ", data offset " << array_data_offset;
     return false;
   }
   uint32_t value_width = array_data[1];
@@ -1470,10 +1473,10 @@
   uint32_t table_size = 4 + (value_width * value_count + 1) / 2;
   /* make sure the end of the switch is in range */
   if (cur_offset + array_data_offset + table_size > insn_count) {
-    Fail(VERIFY_ERROR_GENERIC) << "invalid array data end: at " << cur_offset
-                               << ", data offset " << array_data_offset << ", end "
-                               << cur_offset + array_data_offset + table_size
-                               << ", count " << insn_count;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid array data end: at " << cur_offset
+                                      << ", data offset " << array_data_offset << ", end "
+                                      << cur_offset + array_data_offset + table_size
+                                      << ", count " << insn_count;
     return false;
   }
   return true;
@@ -1486,20 +1489,20 @@
     return false;
   }
   if (!selfOkay && offset == 0) {
-    Fail(VERIFY_ERROR_GENERIC) << "branch offset of zero not allowed at" << (void*) cur_offset;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "branch offset of zero not allowed at" << (void*) cur_offset;
     return false;
   }
   // Check for 32-bit overflow. This isn't strictly necessary if we can depend on the runtime
   // to have identical "wrap-around" behavior, but it's unwise to depend on that.
   if (((int64_t) cur_offset + (int64_t) offset) != (int64_t) (cur_offset + offset)) {
-    Fail(VERIFY_ERROR_GENERIC) << "branch target overflow " << (void*) cur_offset << " +" << offset;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "branch target overflow " << (void*) cur_offset << " +" << offset;
     return false;
   }
   const uint32_t insn_count = code_item_->insns_size_in_code_units_;
   int32_t abs_offset = cur_offset + offset;
   if (abs_offset < 0 || (uint32_t) abs_offset >= insn_count || !insn_flags_[abs_offset].IsOpcode()) {
-    Fail(VERIFY_ERROR_GENERIC) << "invalid branch target " << offset << " (-> "
-                               << (void*) abs_offset << ") at " << (void*) cur_offset;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid branch target " << offset << " (-> "
+                                      << (void*) abs_offset << ") at " << (void*) cur_offset;
     return false;
   }
   insn_flags_[abs_offset].SetBranchTarget();
@@ -1551,16 +1554,16 @@
   /* make sure the start of the switch is in range */
   int32_t switch_offset = insns[1] | ((int32_t) insns[2]) << 16;
   if ((int32_t) cur_offset + switch_offset < 0 || cur_offset + switch_offset + 2 >= insn_count) {
-    Fail(VERIFY_ERROR_GENERIC) << "invalid switch start: at " << cur_offset
-                               << ", switch offset " << switch_offset << ", count " << insn_count;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch start: at " << cur_offset
+                                      << ", switch offset " << switch_offset << ", count " << insn_count;
     return false;
   }
   /* offset to switch table is a relative branch-style offset */
   const uint16_t* switch_insns = insns + switch_offset;
   /* make sure the table is 32-bit aligned */
   if ((((uint32_t) switch_insns) & 0x03) != 0) {
-    Fail(VERIFY_ERROR_GENERIC) << "unaligned switch table: at " << cur_offset
-                               << ", switch offset " << switch_offset;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unaligned switch table: at " << cur_offset
+                                      << ", switch offset " << switch_offset;
     return false;
   }
   uint32_t switch_count = switch_insns[1];
@@ -1579,16 +1582,16 @@
   }
   uint32_t table_size = targets_offset + switch_count * 2;
   if (switch_insns[0] != expected_signature) {
-    Fail(VERIFY_ERROR_GENERIC) << StringPrintf("wrong signature for switch table (%x, wanted %x)",
-                                               switch_insns[0], expected_signature);
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << StringPrintf("wrong signature for switch table (%x, wanted %x)",
+                                                      switch_insns[0], expected_signature);
     return false;
   }
   /* make sure the end of the switch is in range */
   if (cur_offset + switch_offset + table_size > (uint32_t) insn_count) {
-    Fail(VERIFY_ERROR_GENERIC) << "invalid switch end: at " << cur_offset << ", switch offset "
-                               << switch_offset << ", end "
-                               << (cur_offset + switch_offset + table_size)
-                               << ", count " << insn_count;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch end: at " << cur_offset << ", switch offset "
+                                      << switch_offset << ", end "
+                                      << (cur_offset + switch_offset + table_size)
+                                      << ", count " << insn_count;
     return false;
   }
   /* for a sparse switch, verify the keys are in ascending order */
@@ -1598,8 +1601,8 @@
       int32_t key = (int32_t) switch_insns[keys_offset + targ * 2] |
                     (int32_t) (switch_insns[keys_offset + targ * 2 + 1] << 16);
       if (key <= last_key) {
-        Fail(VERIFY_ERROR_GENERIC) << "invalid packed switch: last key=" << last_key
-                                   << ", this=" << key;
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid packed switch: last key=" << last_key
+                                          << ", this=" << key;
         return false;
       }
       last_key = key;
@@ -1611,9 +1614,9 @@
                      (int32_t) (switch_insns[targets_offset + targ * 2 + 1] << 16);
     int32_t abs_offset = cur_offset + offset;
     if (abs_offset < 0 || abs_offset >= (int32_t) insn_count || !insn_flags_[abs_offset].IsOpcode()) {
-      Fail(VERIFY_ERROR_GENERIC) << "invalid switch target " << offset << " (-> "
-                                 << (void*) abs_offset << ") at "
-                                 << (void*) cur_offset << "[" << targ << "]";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch target " << offset << " (-> "
+                                        << (void*) abs_offset << ") at "
+                                        << (void*) cur_offset << "[" << targ << "]";
       return false;
     }
     insn_flags_[abs_offset].SetBranchTarget();
@@ -1623,14 +1626,14 @@
 
 bool DexVerifier::CheckVarArgRegs(uint32_t vA, uint32_t arg[]) {
   if (vA > 5) {
-    Fail(VERIFY_ERROR_GENERIC) << "invalid arg count (" << vA << ") in non-range invoke)";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid arg count (" << vA << ") in non-range invoke)";
     return false;
   }
   uint16_t registers_size = code_item_->registers_size_;
   for (uint32_t idx = 0; idx < vA; idx++) {
     if (arg[idx] >= registers_size) {
-      Fail(VERIFY_ERROR_GENERIC) << "invalid reg index (" << arg[idx]
-                                 << ") in non-range invoke (>= " << registers_size << ")";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid reg index (" << arg[idx]
+                                        << ") in non-range invoke (>= " << registers_size << ")";
       return false;
     }
   }
@@ -1643,8 +1646,8 @@
   // vA/vC are unsigned 8-bit/16-bit quantities for /range instructions, so there's no risk of
   // integer overflow when adding them here.
   if (vA + vC > registers_size) {
-    Fail(VERIFY_ERROR_GENERIC) << "invalid reg index " << vA << "+" << vC << " in range invoke (> "
-                               << registers_size << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid reg index " << vA << "+" << vC << " in range invoke (> "
+                                      << registers_size << ")";
     return false;
   }
   return true;
@@ -1786,8 +1789,8 @@
       LOG(FATAL) << "Null descriptor";
     }
     if (cur_arg >= expected_args) {
-      Fail(VERIFY_ERROR_GENERIC) << "expected " << expected_args
-                                 << " args, found more (" << descriptor << ")";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected " << expected_args
+                                        << " args, found more (" << descriptor << ")";
       return false;
     }
     switch (descriptor[0]) {
@@ -1829,13 +1832,13 @@
         break;
       }
       default:
-        Fail(VERIFY_ERROR_GENERIC) << "unexpected signature type char '" << descriptor << "'";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected signature type char '" << descriptor << "'";
         return false;
     }
     cur_arg++;
   }
   if (cur_arg != expected_args) {
-    Fail(VERIFY_ERROR_GENERIC) << "expected " << expected_args << " arguments, found " << cur_arg;
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected " << expected_args << " arguments, found " << cur_arg;
     return false;
   }
   const char* descriptor = dex_file_->GetReturnTypeDescriptor(proto_id);
@@ -1868,8 +1871,8 @@
     result = false;
   }
   if (!result) {
-    Fail(VERIFY_ERROR_GENERIC) << "unexpected char in return type descriptor '"
-                               << descriptor << "'";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected char in return type descriptor '"
+                                      << descriptor << "'";
   }
   return result;
 }
@@ -2041,7 +2044,7 @@
        * the course of executing code then we have a problem.
        */
       if (dec_insn.vA != 0) {
-        Fail(VERIFY_ERROR_GENERIC) << "encountered data table in instruction stream";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "encountered data table in instruction stream";
       }
       break;
 
@@ -2094,7 +2097,7 @@
     case Instruction::RETURN_VOID:
       if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
         if (!GetMethodReturnType().IsUnknown()) {
-          Fail(VERIFY_ERROR_GENERIC) << "return-void not expected";
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void not expected";
         }
       }
       break;
@@ -2103,7 +2106,7 @@
         /* check the method signature */
         const RegType& return_type = GetMethodReturnType();
         if (!return_type.IsCategory1Types()) {
-          Fail(VERIFY_ERROR_GENERIC) << "unexpected non-category 1 return type " << return_type;
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected non-category 1 return type " << return_type;
         } else {
           // Compilers may generate synthetic functions that write byte values into boolean fields.
           // Also, it may use integer values for boolean, byte, short, and character return types.
@@ -2125,7 +2128,7 @@
         /* check the method signature */
         const RegType& return_type = GetMethodReturnType();
         if (!return_type.IsCategory2Types()) {
-          Fail(VERIFY_ERROR_GENERIC) << "return-wide not expected";
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-wide not expected";
         } else {
           /* check the register contents */
           work_line_->VerifyRegisterType(dec_insn.vA, return_type);
@@ -2139,7 +2142,7 @@
       if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
         const RegType& return_type = GetMethodReturnType();
         if (!return_type.IsReferenceTypes()) {
-          Fail(VERIFY_ERROR_GENERIC) << "return-object not expected";
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected";
         } else {
           /* return_type is the *expected* return type, not register value */
           DCHECK(!return_type.IsZero());
@@ -2148,9 +2151,9 @@
           // Disallow returning uninitialized values and verify that the reference in vAA is an
           // instance of the "return_type"
           if (reg_type.IsUninitializedTypes()) {
-            Fail(VERIFY_ERROR_GENERIC) << "returning uninitialized object '" << reg_type << "'";
+            Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "returning uninitialized object '" << reg_type << "'";
           } else if (!return_type.IsAssignableFrom(reg_type)) {
-            Fail(VERIFY_ERROR_GENERIC) << "returning '" << reg_type
+            Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "returning '" << reg_type
                 << "', but expected from declaration '" << return_type << "'";
           }
         }
@@ -2236,9 +2239,9 @@
       const RegType& orig_type =
           work_line_->GetRegisterType(is_checkcast ? dec_insn.vA : dec_insn.vB);
       if (!res_type.IsNonZeroReferenceTypes()) {
-        Fail(VERIFY_ERROR_GENERIC) << "check-cast on unexpected class " << res_type;
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on unexpected class " << res_type;
       } else if (!orig_type.IsReferenceTypes()) {
-        Fail(VERIFY_ERROR_GENERIC) << "check-cast on non-reference in v" << dec_insn.vA;
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on non-reference in v" << dec_insn.vA;
       } else {
         if (is_checkcast) {
           work_line_->SetRegisterType(dec_insn.vA, res_type);
@@ -2252,7 +2255,7 @@
       const RegType& res_type = work_line_->GetRegisterType(dec_insn.vB);
       if (res_type.IsReferenceTypes()) {
         if (!res_type.IsArrayTypes() && !res_type.IsZero()) {  // ie not an array or null
-          Fail(VERIFY_ERROR_GENERIC) << "array-length on non-array " << res_type;
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type;
         } else {
           work_line_->SetRegisterType(dec_insn.vA, reg_types_.Integer());
         }
@@ -2323,7 +2326,7 @@
     case Instruction::THROW: {
       const RegType& res_type = work_line_->GetRegisterType(dec_insn.vA);
       if (!reg_types_.JavaLangThrowable().IsAssignableFrom(res_type)) {
-        Fail(VERIFY_ERROR_GENERIC) << "thrown class " << res_type << " not instanceof Throwable";
+        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "thrown class " << res_type << " not instanceof Throwable";
       }
       break;
     }
@@ -2345,27 +2348,27 @@
       /* array_type can be null if the reg type is Zero */
       if (!array_type.IsZero()) {
         if (!array_type.IsArrayTypes()) {
-          Fail(VERIFY_ERROR_GENERIC) << "invalid fill-array-data with array type " << array_type;
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type;
         } else {
           const RegType& component_type = reg_types_.GetComponentType(array_type,
                                                     method_->GetDeclaringClass()->GetClassLoader());
           DCHECK(!component_type.IsUnknown());
           if (component_type.IsNonZeroReferenceTypes()) {
-            Fail(VERIFY_ERROR_GENERIC) << "invalid fill-array-data with component type "
-                                       << component_type;
+            Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type "
+                                              << component_type;
           } else {
             // Now verify if the element width in the table matches the element width declared in
             // the array
             const uint16_t* array_data = insns + (insns[1] | (((int32_t) insns[2]) << 16));
             if (array_data[0] != Instruction::kArrayDataSignature) {
-              Fail(VERIFY_ERROR_GENERIC) << "invalid magic for array-data";
+              Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid magic for array-data";
             } else {
               size_t elem_width = Primitive::ComponentSize(component_type.GetPrimitiveType());
               // Since we don't compress the data in Dex, expect to see equal width of data stored
               // in the table and expected from the array class.
               if (array_data[1] != elem_width) {
-                Fail(VERIFY_ERROR_GENERIC) << "array-data size mismatch (" << array_data[1]
-                                           << " vs " << elem_width << ")";
+                Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-data size mismatch (" << array_data[1]
+                                                  << " vs " << elem_width << ")";
               }
             }
           }
@@ -2386,8 +2389,8 @@
         mismatch = !reg_type1.IsIntegralTypes() || !reg_type2.IsIntegralTypes();
       }
       if (mismatch) {
-        Fail(VERIFY_ERROR_GENERIC) << "args to if-eq/if-ne (" << reg_type1 << "," << reg_type2
-                                   << ") must both be references or integral";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "args to if-eq/if-ne (" << reg_type1 << "," << reg_type2
+                                          << ") must both be references or integral";
       }
       break;
     }
@@ -2398,8 +2401,8 @@
       const RegType& reg_type1 = work_line_->GetRegisterType(dec_insn.vA);
       const RegType& reg_type2 = work_line_->GetRegisterType(dec_insn.vB);
       if (!reg_type1.IsIntegralTypes() || !reg_type2.IsIntegralTypes()) {
-        Fail(VERIFY_ERROR_GENERIC) << "args to 'if' (" << reg_type1 << ","
-                                   << reg_type2 << ") must be integral";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "args to 'if' (" << reg_type1 << ","
+                                          << reg_type2 << ") must be integral";
       }
       break;
     }
@@ -2407,7 +2410,7 @@
     case Instruction::IF_NEZ: {
       const RegType& reg_type = work_line_->GetRegisterType(dec_insn.vA);
       if (!reg_type.IsReferenceTypes() && !reg_type.IsIntegralTypes()) {
-        Fail(VERIFY_ERROR_GENERIC) << "type " << reg_type << " unexpected as arg to if-eqz/if-nez";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type << " unexpected as arg to if-eqz/if-nez";
       }
       break;
     }
@@ -2417,8 +2420,8 @@
     case Instruction::IF_LEZ: {
       const RegType& reg_type = work_line_->GetRegisterType(dec_insn.vA);
       if (!reg_type.IsIntegralTypes()) {
-        Fail(VERIFY_ERROR_GENERIC) << "type " << reg_type
-                                   << " unexpected as arg to if-ltz/if-gez/if-gtz/if-lez";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type
+                                          << " unexpected as arg to if-ltz/if-gez/if-gtz/if-lez";
       }
       break;
     }
@@ -2608,7 +2611,7 @@
 
           /* no null refs allowed (?) */
           if (this_type.IsZero()) {
-            Fail(VERIFY_ERROR_GENERIC) << "unable to initialize null ref";
+            Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unable to initialize null ref";
             break;
           }
           if (called_method != NULL) {
@@ -2617,19 +2620,19 @@
             /* must be in same class or in superclass */
             if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) {
               if (this_class != method_->GetDeclaringClass()) {
-                Fail(VERIFY_ERROR_GENERIC)
+                Fail(VERIFY_ERROR_BAD_CLASS_HARD)
                     << "invoke-direct <init> on super only allowed for 'this' in <init>";
                 break;
               }
             }  else if (called_method->GetDeclaringClass() != this_class) {
-              Fail(VERIFY_ERROR_GENERIC) << "invoke-direct <init> must be on current class or super";
+              Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke-direct <init> must be on current class or super";
               break;
             }
           }
 
           /* arg must be an uninitialized reference */
           if (!this_type.IsUninitializedTypes()) {
-            Fail(VERIFY_ERROR_GENERIC) << "Expected initialization on uninitialized reference "
+            Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected initialization on uninitialized reference "
                 << this_type;
             break;
           }
@@ -2701,7 +2704,7 @@
             /* null pointer always passes (and always fails at runtime) */
           } else {
             if (this_type.IsUninitializedTypes()) {
-              Fail(VERIFY_ERROR_GENERIC) << "interface call on uninitialized object "
+              Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface call on uninitialized object "
                   << this_type;
               break;
             }
@@ -2960,7 +2963,7 @@
     case Instruction::UNUSED_7A:
     case Instruction::UNUSED_EC:
     case Instruction::UNUSED_FF:
-      Fail(VERIFY_ERROR_GENERIC) << "Unexpected opcode " << inst->DumpString(dex_file_);
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
       break;
 
     /*
@@ -2970,7 +2973,7 @@
   }  // end - switch (dec_insn.opcode)
 
   if (failure_ != VERIFY_ERROR_NONE) {
-    if (failure_ == VERIFY_ERROR_GENERIC) {
+    if (failure_ == VERIFY_ERROR_BAD_CLASS_HARD || failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) {
       /* immediate failure, reject class */
       fail_messages_ << std::endl << "Rejecting opcode " << inst->DumpString(dex_file_);
       return false;
@@ -2998,12 +3001,12 @@
   if ((opcode_flags & Instruction::kContinue) != 0) {
     uint32_t next_insn_idx = work_insn_idx_ + CurrentInsnFlags().GetLengthInCodeUnits();
     if (next_insn_idx >= code_item_->insns_size_in_code_units_) {
-      Fail(VERIFY_ERROR_GENERIC) << "Execution can walk off end of code area";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Execution can walk off end of code area";
       return false;
     }
     // The only way to get to a move-exception instruction is to get thrown there. Make sure the
     // next instruction isn't one.
-    if (!CheckMoveException(code_item_->insns_, next_insn_idx)) {
+    if (!CheckNotMoveException(code_item_->insns_, next_insn_idx)) {
       return false;
     }
     RegisterLine* next_line = reg_table_.GetLine(next_insn_idx);
@@ -3038,11 +3041,11 @@
     bool isConditional, selfOkay;
     if (!GetBranchOffset(work_insn_idx_, &branch_target, &isConditional, &selfOkay)) {
       /* should never happen after static verification */
-      Fail(VERIFY_ERROR_GENERIC) << "bad branch";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad branch";
       return false;
     }
     DCHECK_EQ(isConditional, (opcode_flags & Instruction::kContinue) != 0);
-    if (!CheckMoveException(code_item_->insns_, work_insn_idx_ + branch_target)) {
+    if (!CheckNotMoveException(code_item_->insns_, work_insn_idx_ + branch_target)) {
       return false;
     }
     /* update branch target, set "changed" if appropriate */
@@ -3082,7 +3085,7 @@
          (((int32_t) switch_insns[offset_to_targets + targ * 2 + 1]) << 16);
       abs_offset = work_insn_idx_ + offset;
       DCHECK_LT(abs_offset, code_item_->insns_size_in_code_units_);
-      if (!CheckMoveException(code_item_->insns_, abs_offset)) {
+      if (!CheckNotMoveException(code_item_->insns_, abs_offset)) {
         return false;
       }
       if (!UpdateRegisters(abs_offset, work_line_.get()))
@@ -3123,7 +3126,7 @@
        * it will do so before grabbing the lock).
        */
       if (dec_insn.opcode != Instruction::MONITOR_ENTER || work_line_->MonitorStackDepth() != 1) {
-        Fail(VERIFY_ERROR_GENERIC)
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD)
             << "expected to be within a catch-all for an instruction where a monitor is held";
         return false;
       }
@@ -3167,7 +3170,7 @@
     method_->GetDexCacheResolvedTypes()->Set(class_idx, result.GetClass());
   }
   if (result.IsUnknown()) {
-    Fail(VERIFY_ERROR_GENERIC) << "accessing unknown class in " << PrettyDescriptor(referrer);
+    Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing unknown class in " << PrettyDescriptor(referrer);
     return result;
   }
   // Check if access is allowed. Unresolved types use AllocObjectFromCodeWithAccessCheck to
@@ -3202,7 +3205,7 @@
             } else if(!reg_types_.JavaLangThrowable().IsAssignableFrom(exception)) {
               // We don't know enough about the type and the common path merge will result in
               // Conflict. Fail here knowing the correct thing can be done at runtime.
-              Fail(VERIFY_ERROR_GENERIC) << "unexpected non-exception class " << exception;
+              Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception;
               return reg_types_.Unknown();
             } else if (common_super->Equals(exception)) {
               // odd case, but nothing to do
@@ -3218,7 +3221,7 @@
   }
   if (common_super == NULL) {
     /* no catch blocks, or no catches with classes we can find */
-    Fail(VERIFY_ERROR_GENERIC) << "unable to find exception handler";
+    Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unable to find exception handler";
     return reg_types_.Unknown();
   }
   return *common_super;
@@ -3269,14 +3272,14 @@
   // Make sure calls to constructors are "direct". There are additional restrictions but we don't
   // enforce them here.
   if (res_method->IsConstructor() && method_type != METHOD_DIRECT) {
-    Fail(VERIFY_ERROR_GENERIC) << "rejecting non-direct call to constructor "
-                               << PrettyMethod(res_method);
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "rejecting non-direct call to constructor "
+                                      << PrettyMethod(res_method);
     return NULL;
   }
   // Disallow any calls to class initializers.
   if (MethodHelper(res_method).IsClassInitializer()) {
-    Fail(VERIFY_ERROR_GENERIC) << "rejecting call to class initializer "
-                               << PrettyMethod(res_method);
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "rejecting call to class initializer "
+                                      << PrettyMethod(res_method);
     return NULL;
   }
   // Check if access is allowed.
@@ -3287,8 +3290,8 @@
   }
   // Check that invoke-virtual and invoke-super are not used on private methods of the same class.
   if (res_method->IsPrivate() && method_type == METHOD_VIRTUAL) {
-    Fail(VERIFY_ERROR_GENERIC) << "invoke-super/virtual can't be used on private method "
-                               << PrettyMethod(res_method);
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke-super/virtual can't be used on private method "
+                                      << PrettyMethod(res_method);
     return NULL;
   }
   // Check that interface methods match interface classes.
@@ -3349,7 +3352,7 @@
   /* caught by static verifier */
   DCHECK(is_range || expected_args <= 5);
   if (expected_args > code_item_->outs_size_) {
-    Fail(VERIFY_ERROR_GENERIC) << "invalid argument count (" << expected_args
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args
         << ") exceeds outsSize (" << code_item_->outs_size_ << ")";
     return NULL;
   }
@@ -3367,13 +3370,13 @@
       return NULL;
     }
     if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) {
-      Fail(VERIFY_ERROR_GENERIC) << "'this' arg must be initialized";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized";
       return NULL;
     }
     if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) {
       const RegType& res_method_class = reg_types_.FromClass(res_method->GetDeclaringClass());
       if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
-        Fail(VERIFY_ERROR_GENERIC) << "'this' argument '" << actual_arg_type
+        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
             << "' not instance of '" << res_method_class << "'";
         return NULL;
       }
@@ -3389,7 +3392,7 @@
   size_t params_size = params == NULL ? 0 : params->Size();
   for (size_t param_index = 0; param_index < params_size; param_index++) {
     if (actual_args >= expected_args) {
-      Fail(VERIFY_ERROR_GENERIC) << "Rejecting invalid call to '" << PrettyMethod(res_method)
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" << PrettyMethod(res_method)
           << "'. Expected " << expected_args << " arguments, processing argument " << actual_args
           << " (where longs/doubles count twice).";
       return NULL;
@@ -3397,7 +3400,7 @@
     const char* descriptor =
         mh.GetTypeDescriptorFromTypeIdx(params->GetTypeItem(param_index).type_idx_);
     if (descriptor == NULL) {
-      Fail(VERIFY_ERROR_GENERIC) << "Rejecting invocation of " << PrettyMethod(res_method)
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method)
           << " missing signature component";
       return NULL;
     }
@@ -3410,7 +3413,7 @@
     actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1;
   }
   if (actual_args != expected_args) {
-    Fail(VERIFY_ERROR_GENERIC) << "Rejecting invocation of " << PrettyMethod(res_method)
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method)
         << " expected " << expected_args << " arguments, found " << actual_args;
     return NULL;
   } else {
@@ -3431,7 +3434,7 @@
   } else {
     // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
     if (!res_type.IsArrayTypes()) {
-      Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class " << res_type;
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "new-array on non-array class " << res_type;
     } else if (!is_filled) {
       /* make sure "size" register is valid type */
       work_line_->VerifyRegisterType(dec_insn.vB, reg_types_.Integer());
@@ -3460,7 +3463,7 @@
                              const RegType& insn_type, bool is_primitive) {
   const RegType& index_type = work_line_->GetRegisterType(dec_insn.vC);
   if (!index_type.IsArrayIndexTypes()) {
-    Fail(VERIFY_ERROR_GENERIC) << "Invalid reg type for array index (" << index_type << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
   } else {
     const RegType& array_type = work_line_->GetRegisterType(dec_insn.vB);
     if (array_type.IsZero()) {
@@ -3474,21 +3477,21 @@
         work_line_->SetRegisterType(dec_insn.vA, reg_types_.ConstLo());
       }
     } else if (!array_type.IsArrayTypes()) {
-      Fail(VERIFY_ERROR_GENERIC) << "not array type " << array_type << " with aget";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
     } else {
       /* verify the class */
       const RegType& component_type = reg_types_.GetComponentType(array_type,
                                                     method_->GetDeclaringClass()->GetClassLoader());
       if (!component_type.IsReferenceTypes() && !is_primitive) {
-        Fail(VERIFY_ERROR_GENERIC) << "primitive array type " << array_type
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
             << " source for aget-object";
       } else if (component_type.IsNonZeroReferenceTypes() && is_primitive) {
-        Fail(VERIFY_ERROR_GENERIC) << "reference array type " << array_type
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "reference array type " << array_type
             << " source for category 1 aget";
       } else if (is_primitive && !insn_type.Equals(component_type) &&
                  !((insn_type.IsInteger() && component_type.IsFloat()) ||
                    (insn_type.IsLong() && component_type.IsDouble()))) {
-          Fail(VERIFY_ERROR_GENERIC) << "array type " << array_type
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array type " << array_type
               << " incompatible with aget of type " << insn_type;
       } else {
           // Use knowledge of the field type which is stronger than the type inferred from the
@@ -3504,28 +3507,28 @@
                              const RegType& insn_type, bool is_primitive) {
   const RegType& index_type = work_line_->GetRegisterType(dec_insn.vC);
   if (!index_type.IsArrayIndexTypes()) {
-    Fail(VERIFY_ERROR_GENERIC) << "Invalid reg type for array index (" << index_type << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
   } else {
     const RegType& array_type = work_line_->GetRegisterType(dec_insn.vB);
     if (array_type.IsZero()) {
       // Null array type; this code path will fail at runtime. Infer a merge-able type from the
       // instruction type.
     } else if (!array_type.IsArrayTypes()) {
-      Fail(VERIFY_ERROR_GENERIC) << "not array type " << array_type << " with aput";
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput";
     } else {
       /* verify the class */
       const RegType& component_type = reg_types_.GetComponentType(array_type,
                                                     method_->GetDeclaringClass()->GetClassLoader());
       if (!component_type.IsReferenceTypes() && !is_primitive) {
-        Fail(VERIFY_ERROR_GENERIC) << "primitive array type " << array_type
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
             << " source for aput-object";
       } else if (component_type.IsNonZeroReferenceTypes() && is_primitive) {
-        Fail(VERIFY_ERROR_GENERIC) << "reference array type " << array_type
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "reference array type " << array_type
             << " source for category 1 aput";
       } else if (is_primitive && !insn_type.Equals(component_type) &&
                  !((insn_type.IsInteger() && component_type.IsFloat()) ||
                    (insn_type.IsLong() && component_type.IsDouble()))) {
-        Fail(VERIFY_ERROR_GENERIC) << "array type " << array_type
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array type " << array_type
             << " incompatible with aput of type " << insn_type;
       } else {
         // The instruction agrees with the type of array, confirm the value to be stored does too
@@ -3609,9 +3612,9 @@
              field->GetDeclaringClass() != method_->GetDeclaringClass())) {
     // Field accesses through uninitialized references are only allowable for constructors where
     // the field is declared in this class
-    Fail(VERIFY_ERROR_GENERIC) << "cannot access instance field " << PrettyField(field)
-                               << " of a not fully initialized object within the context of "
-                               << PrettyMethod(method_);
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
+                                      << " of a not fully initialized object within the context of "
+                                      << PrettyMethod(method_);
     return NULL;
   } else if(!field->GetDeclaringClass()->IsAssignableFrom(obj_type.GetClass())) {
     // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
@@ -3659,17 +3662,17 @@
         // This is a global failure rather than a class change failure as the instructions and
         // the descriptors for the type should have been consistent within the same file at
         // compile time
-        Fail(VERIFY_ERROR_GENERIC) << "expected field " << PrettyField(field)
-                                   << " to be of type '" << insn_type
-                                   << "' but found type '" << field_type << "' in get";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+                                          << " to be of type '" << insn_type
+                                          << "' but found type '" << field_type << "' in get";
         return;
       }
     } else {
       if (!insn_type.IsAssignableFrom(field_type)) {
-        Fail(VERIFY_ERROR_GENERIC) << "expected field " << PrettyField(field)
-                                   << " to be compatible with type '" << insn_type
-                                   << "' but found type '" << field_type
-                                   << "' in get-object";
+        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+                                          << " to be compatible with type '" << insn_type
+                                          << "' but found type '" << field_type
+                                          << "' in get-object";
         return;
       }
     }
@@ -3733,25 +3736,25 @@
         // This is a global failure rather than a class change failure as the instructions and
         // the descriptors for the type should have been consistent within the same file at
         // compile time
-        Fail(VERIFY_ERROR_GENERIC) << "expected field " << PrettyField(field)
-                                   << " to be of type '" << insn_type
-                                   << "' but found type '" << field_type
-                                   << "' in put";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+                                          << " to be of type '" << insn_type
+                                          << "' but found type '" << field_type
+                                          << "' in put";
         return;
       }
       if (!value_compatible) {
-        Fail(VERIFY_ERROR_GENERIC) << "unexpected value in v" << dec_insn.vA
-                                   << " of type " << value_type
-                                   << " but expected " << field_type
-                                   << " for store to " << PrettyField(field) << " in put";
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << dec_insn.vA
+                                          << " of type " << value_type
+                                          << " but expected " << field_type
+                                          << " for store to " << PrettyField(field) << " in put";
         return;
       }
     } else {
       if (!insn_type.IsAssignableFrom(field_type)) {
-        Fail(VERIFY_ERROR_GENERIC) << "expected field " << PrettyField(field)
-                                  << " to be compatible with type '" << insn_type
-                                  << "' but found type '" << field_type
-                                  << "' in put-object";
+        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+                                          << " to be compatible with type '" << insn_type
+                                          << "' but found type '" << field_type
+                                          << "' in put-object";
         return;
       }
       work_line_->VerifyRegisterType(dec_insn.vA, field_type);
@@ -3759,9 +3762,9 @@
   }
 }
 
-bool DexVerifier::CheckMoveException(const uint16_t* insns, int insn_idx) {
+bool DexVerifier::CheckNotMoveException(const uint16_t* insns, int insn_idx) {
   if ((insns[insn_idx] & 0xff) == Instruction::MOVE_EXCEPTION) {
-    Fail(VERIFY_ERROR_GENERIC) << "invalid use of move-exception";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid use of move-exception";
     return false;
   }
   return true;
@@ -3918,7 +3921,7 @@
   // There's a single byte to encode the size of each bitmap
   if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) {
     // TODO: either a better GC map format or per method failures
-    Fail(VERIFY_ERROR_GENERIC) << "Cannot encode GC map for method with "
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
        << ref_bitmap_bits << " registers";
     return NULL;
   }
@@ -3926,7 +3929,7 @@
   // There are 2 bytes to encode the number of entries
   if (num_entries >= 65536) {
     // TODO: either a better GC map format or per method failures
-    Fail(VERIFY_ERROR_GENERIC) << "Cannot encode GC map for method with "
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
        << num_entries << " entries";
     return NULL;
   }
@@ -3940,14 +3943,14 @@
     pc_bytes = 2;
   } else {
     // TODO: either a better GC map format or per method failures
-    Fail(VERIFY_ERROR_GENERIC) << "Cannot encode GC map for method with "
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
        << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)";
     return NULL;
   }
   size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries ) + 4;
   std::vector<uint8_t>* table = new std::vector<uint8_t>;
   if (table == NULL) {
-    Fail(VERIFY_ERROR_GENERIC) << "Failed to encode GC map (size=" << table_size << ")";
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Failed to encode GC map (size=" << table_size << ")";
     return NULL;
   }
   // Write table header
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index 17826e7..eaae42c 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -543,21 +543,26 @@
 
 /*
  * An enumeration of problems that can turn up during verification.
- * VERIFY_ERROR_GENERIC denotes a failure that causes the entire class to be rejected. Other errors
- * denote verification errors that cause bytecode to be rewritten to fail at runtime.
+ * Both VERIFY_ERROR_BAD_CLASS_SOFT and VERIFY_ERROR_BAD_CLASS_HARD denote failures that cause
+ * the entire class to be rejected. However, VERIFY_ERROR_BAD_CLASS_SOFT denotes a soft failure
+ * that can potentially be corrected, and the verifier will try again at runtime.
+ * VERIFY_ERROR_BAD_CLASS_HARD denotes a hard failure that can't be corrected, and will cause
+ * the class to remain uncompiled. Other errors denote verification errors that cause bytecode
+ * to be rewritten to fail at runtime.
  */
 enum VerifyError {
-  VERIFY_ERROR_NONE = 0,      /* no error; must be zero */
-  VERIFY_ERROR_GENERIC,       /* VerifyError */
+  VERIFY_ERROR_NONE = 0,       /* no error; must be zero */
+  VERIFY_ERROR_BAD_CLASS_HARD, /* VerifyError; hard error that skips compilation */
+  VERIFY_ERROR_BAD_CLASS_SOFT, /* VerifyError; soft error that verifies again at runtime */
 
-  VERIFY_ERROR_NO_CLASS,      /* NoClassDefFoundError */
-  VERIFY_ERROR_NO_FIELD,      /* NoSuchFieldError */
-  VERIFY_ERROR_NO_METHOD,     /* NoSuchMethodError */
-  VERIFY_ERROR_ACCESS_CLASS,  /* IllegalAccessError */
-  VERIFY_ERROR_ACCESS_FIELD,  /* IllegalAccessError */
-  VERIFY_ERROR_ACCESS_METHOD, /* IllegalAccessError */
-  VERIFY_ERROR_CLASS_CHANGE,  /* IncompatibleClassChangeError */
-  VERIFY_ERROR_INSTANTIATION, /* InstantiationError */
+  VERIFY_ERROR_NO_CLASS,       /* NoClassDefFoundError */
+  VERIFY_ERROR_NO_FIELD,       /* NoSuchFieldError */
+  VERIFY_ERROR_NO_METHOD,      /* NoSuchMethodError */
+  VERIFY_ERROR_ACCESS_CLASS,   /* IllegalAccessError */
+  VERIFY_ERROR_ACCESS_FIELD,   /* IllegalAccessError */
+  VERIFY_ERROR_ACCESS_METHOD,  /* IllegalAccessError */
+  VERIFY_ERROR_CLASS_CHANGE,   /* IncompatibleClassChangeError */
+  VERIFY_ERROR_INSTANTIATION,  /* InstantiationError */
 };
 std::ostream& operator<<(std::ostream& os, const VerifyError& rhs);
 
@@ -1249,7 +1254,7 @@
    * to execute a move-exception is as the first instruction of an exception handler.
    * Returns "true" if all is well, "false" if the target instruction is move-exception.
    */
-  bool CheckMoveException(const uint16_t* insns, int insn_idx);
+  bool CheckNotMoveException(const uint16_t* insns, int insn_idx);
 
   /*
    * Replace an instruction with "throw-verification-error". This allows us to
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 93d4728..5ed856a 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -344,7 +344,8 @@
     exception_class = "Ljava/lang/InstantiationError;";
     msg = ClassNameFromIndex(method, ref, ref_type, false);
     break;
-  case verifier::VERIFY_ERROR_GENERIC:
+  case verifier::VERIFY_ERROR_BAD_CLASS_SOFT:
+  case verifier::VERIFY_ERROR_BAD_CLASS_HARD:
     // Generic VerifyError; use default exception, no message.
     break;
   case verifier::VERIFY_ERROR_NONE: