Verifier no longer checks dead code after failing instruction at runtime.

When a failure is detected by the verifier at runtime, the code
following the failing instruction is unreachable, and is no longer
checked. This prevents further failures from causing the whole class to
be thrown out. OTP is working again.

Change-Id: I143a76370a2bc9998df526b03219b3ae40a77093
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 30bee66..55d537c 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -333,6 +333,7 @@
       interesting_dex_pc_(-1),
       monitor_enter_dex_pcs_(NULL),
       have_pending_hard_failure_(false),
+      have_pending_runtime_throw_failure_(false),
       new_instance_count_(0),
       monitor_enter_count_(0) {
 }
@@ -396,14 +397,18 @@
     case VERIFY_ERROR_ACCESS_METHOD:
     case VERIFY_ERROR_INSTANTIATION:
     case VERIFY_ERROR_CLASS_CHANGE:
-      // If we're optimistically running verification at compile time, turn NO_xxx, ACCESS_xxx,
-      // class change and instantiation 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. In these cases, we don't want to
-      // affect the soundness of the code being compiled. Instead, the generated code runs "slow
-      // paths" that dynamically perform the verification and cause the behavior to be that akin
-      // to an interpreter.
-      error = VERIFY_ERROR_BAD_CLASS_SOFT;
+      if (Runtime::Current()->IsCompiler()) {
+        // If we're optimistically running verification at compile time, turn NO_xxx, ACCESS_xxx,
+        // class change and instantiation 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. In these cases, we don't want to
+        // affect the soundness of the code being compiled. Instead, the generated code runs "slow
+        // paths" that dynamically perform the verification and cause the behavior to be that akin
+        // to an interpreter.
+        error = VERIFY_ERROR_BAD_CLASS_SOFT;
+      } else {
+        have_pending_runtime_throw_failure_ = true;
+      }
       break;
       // Indication that verification should be retried at runtime.
     case VERIFY_ERROR_BAD_CLASS_SOFT:
@@ -2265,6 +2270,9 @@
     /* immediate failure, reject class */
     info_messages_ << "Rejecting opcode " << inst->DumpString(dex_file_);
     return false;
+  } else if (have_pending_runtime_throw_failure_) {
+    /* slow path will throw, mark following code as unreachable */
+    opcode_flags = Instruction::kThrow;
   }
   /*
    * If we didn't just set the result register, clear it out. This ensures that you can only use
diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h
index 3e3154b..244f1f8 100644
--- a/src/verifier/method_verifier.h
+++ b/src/verifier/method_verifier.h
@@ -649,6 +649,11 @@
   std::vector<std::ostringstream*> failure_messages_;
   // Is there a pending hard failure?
   bool have_pending_hard_failure_;
+  // Is there a pending runtime throw failure? A runtime throw failure is when an instruction
+  // would fail at runtime throwing an exception. Such an instruction causes the following code
+  // to be unreachable. This is set by Fail and used to ensure we don't process unreachable
+  // instructions that would hard fail the verification.
+  bool have_pending_runtime_throw_failure_;
 
   // Info message log use primarily for verifier diagnostics.
   std::ostringstream info_messages_;