Merge "Revert "Test that we can unwind framework code.""
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index cbd0429..e4680ff 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -748,13 +748,11 @@
   for (size_t i = start_index; i < number_of_vreg_arguments; i++, argument_index++) {
     Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]);
     bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble);
-    if (!is_range && is_wide && args[i] + 1 != args[i + 1]) {
-      LOG(WARNING) << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol()
-                   << " at " << dex_pc;
-      // We do not implement non sequential register pair.
-      MaybeRecordStat(MethodCompilationStat::kNotCompiledNonSequentialRegPair);
-      return false;
-    }
+    // Longs and doubles should be in pairs, that is, sequential registers. The verifier should
+    // reject any class where this is violated.
+    DCHECK(is_range || !is_wide || (args[i] + 1 == args[i + 1]))
+        << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol()
+        << " at " << dex_pc;
     HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
     invoke->SetArgumentAt(argument_index, arg);
     if (is_wide) {
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index b6b1bb1..b988813 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -19,6 +19,7 @@
 
 #include <sstream>
 #include <string>
+#include <type_traits>
 
 #include "atomic.h"
 
@@ -38,7 +39,6 @@
   kNotCompiledHugeMethod,
   kNotCompiledLargeMethodNoBranches,
   kNotCompiledNoCodegen,
-  kNotCompiledNonSequentialRegPair,
   kNotCompiledPathological,
   kNotCompiledSpaceFilter,
   kNotCompiledUnhandledInstruction,
@@ -84,14 +84,15 @@
 
       for (int i = 0; i < kLastStat; i++) {
         if (compile_stats_[i] != 0) {
-          LOG(INFO) << PrintMethodCompilationStat(i) << ": " << compile_stats_[i];
+          LOG(INFO) << PrintMethodCompilationStat(static_cast<MethodCompilationStat>(i)) << ": "
+              << compile_stats_[i];
         }
       }
     }
   }
 
  private:
-  std::string PrintMethodCompilationStat(int stat) const {
+  std::string PrintMethodCompilationStat(MethodCompilationStat stat) const {
     switch (stat) {
       case kAttemptCompilation : return "kAttemptCompilation";
       case kCompiledBaseline : return "kCompiledBaseline";
@@ -106,7 +107,6 @@
       case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod";
       case kNotCompiledLargeMethodNoBranches : return "kNotCompiledLargeMethodNoBranches";
       case kNotCompiledNoCodegen : return "kNotCompiledNoCodegen";
-      case kNotCompiledNonSequentialRegPair : return "kNotCompiledNonSequentialRegPair";
       case kNotCompiledPathological : return "kNotCompiledPathological";
       case kNotCompiledSpaceFilter : return "kNotCompiledSpaceFilter";
       case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction";
@@ -120,9 +120,12 @@
       case kRemovedCheckedCast: return "kRemovedCheckedCast";
       case kRemovedDeadInstruction: return "kRemovedDeadInstruction";
       case kRemovedNullCheck: return "kRemovedNullCheck";
-      default: LOG(FATAL) << "invalid stat";
+
+      case kLastStat: break;  // Invalid to print out.
     }
-    return "";
+    LOG(FATAL) << "invalid stat "
+        << static_cast<std::underlying_type<MethodCompilationStat>::type>(stat);
+    UNREACHABLE();
   }
 
   AtomicInteger compile_stats_[kLastStat];
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index a66c38e..5fa58f7 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -465,7 +465,9 @@
 }
 
 bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags,
-                                               uint32_t code_offset, bool expect_direct) {
+                                               uint32_t code_offset,
+                                               std::unordered_set<uint32_t>& direct_method_indexes,
+                                               bool expect_direct) {
   if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
     return false;
   }
@@ -480,6 +482,13 @@
     return false;
   }
 
+  if (expect_direct) {
+    direct_method_indexes.insert(idx);
+  } else if (direct_method_indexes.find(idx) != direct_method_indexes.end()) {
+    ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx);
+    return false;
+  }
+
   constexpr uint32_t access_method_mask = kAccJavaFlagsMask | kAccConstructor |
       kAccDeclaredSynchronized;
   if (UNLIKELY(((access_flags & ~access_method_mask) != 0) ||
@@ -682,6 +691,7 @@
 
 bool DexFileVerifier::CheckIntraClassDataItem() {
   ClassDataItemIterator it(*dex_file_, ptr_);
+  std::unordered_set<uint32_t> direct_method_indexes;
 
   // These calls use the raw access flags to check whether the whole dex field is valid.
 
@@ -697,13 +707,13 @@
   }
   for (; it.HasNextDirectMethod(); it.Next()) {
     if (!CheckClassDataItemMethod(it.GetMemberIndex(), it.GetRawMemberAccessFlags(),
-        it.GetMethodCodeItemOffset(), true)) {
+        it.GetMethodCodeItemOffset(), direct_method_indexes, true)) {
       return false;
     }
   }
   for (; it.HasNextVirtualMethod(); it.Next()) {
     if (!CheckClassDataItemMethod(it.GetMemberIndex(), it.GetRawMemberAccessFlags(),
-        it.GetMethodCodeItemOffset(), false)) {
+        it.GetMethodCodeItemOffset(), direct_method_indexes, false)) {
       return false;
     }
   }
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 877dfc2..ccc40d4 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -59,6 +59,7 @@
                                  uint32_t* handler_offsets, uint32_t handlers_size);
   bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static);
   bool CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, uint32_t code_offset,
+                                std::unordered_set<uint32_t>& direct_method_indexes,
                                 bool expect_direct);
   bool CheckPadding(size_t offset, uint32_t aligned_offset);
   bool CheckEncodedValue();
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 4d88227..b86a7ee 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3372,11 +3372,27 @@
             << " but expected " << reg_type;
         return nullptr;
       }
-    } else if (!work_line_->VerifyRegisterType(this, get_reg, reg_type)) {
-      // Continue on soft failures. We need to find possible hard failures to avoid problems in the
-      // compiler.
-      if (have_pending_hard_failure_) {
-        return nullptr;
+    } else {
+      if (!work_line_->VerifyRegisterType(this, get_reg, reg_type)) {
+        // Continue on soft failures. We need to find possible hard failures to avoid problems in
+        // the compiler.
+        if (have_pending_hard_failure_) {
+          return nullptr;
+        }
+      } else if (reg_type.IsLongOrDoubleTypes()) {
+        // Check that registers are consecutive (for non-range invokes). Invokes are the only
+        // instructions not specifying register pairs by the first component, but require them
+        // nonetheless. Only check when there's an actual register in the parameters. If there's
+        // none, this will fail below.
+        if (!is_range && sig_registers + 1 < expected_args) {
+          uint32_t second_reg = arg[sig_registers + 1];
+          if (second_reg != get_reg + 1) {
+            Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation, long or double parameter "
+                "at index " << sig_registers << " is not a pair: " << get_reg << " + "
+                << second_reg << ".";
+            return nullptr;
+          }
+        }
       }
     }
     sig_registers += reg_type.IsLongOrDoubleTypes() ?  2 : 1;
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index a6b216b..8565637 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -16,4 +16,5 @@
 MoveExceptionOnEntry
 EmptySparseSwitch
 b/20224106
+b/17410612
 Done!
diff --git a/test/800-smali/smali/b_17410612.smali b/test/800-smali/smali/b_17410612.smali
new file mode 100644
index 0000000..17718cb
--- /dev/null
+++ b/test/800-smali/smali/b_17410612.smali
@@ -0,0 +1,14 @@
+.class public LB17410612;
+
+# Test that an invoke with a long parameter has the long parameter in
+# a pair. This should fail in the verifier and not an abort in the compiler.
+
+.super Ljava/lang/Object;
+
+.method public static run()V
+    .registers 4
+    const-wide v0, 0          # Make (v0, v1) a long
+    const-wide v2, 0          # Make (v2, v3) a long
+    invoke-static {v0, v3}, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;
+    return-void
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 3e88364..33df06d 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -81,6 +81,8 @@
                 null));
         testCases.add(new TestCase("b/20224106", "B20224106", "run", null, new VerifyError(),
                 0));
+        testCases.add(new TestCase("b/17410612", "B17410612", "run", null, new VerifyError(),
+                0));
     }
 
     public void runTests() {
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 240ed41..09841bf 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -296,6 +296,10 @@
 else
     FLAGS="$FLAGS -Xnorelocate"
     COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate"
+    if [ "$HOST" = "y" ]; then
+        # Increase ulimit to 64MB in case we are running hprof test.
+        ulimit -S 64000 || exit 1
+    fi
 fi
 
 if [ "$HOST" = "n" ]; then