OSDN Git Service

Fallback to quick in case of soft verification errors
authorCalin Juravle <calin@google.com>
Wed, 1 Apr 2015 19:27:28 +0000 (20:27 +0100)
committerCalin Juravle <calin@google.com>
Fri, 10 Apr 2015 17:14:42 +0000 (18:14 +0100)
Add a regression test: using uninitialized values triggers a soft
verification error and optimizing should not crash.

Thanks to Stephen Kyle (stephenckyle@googlemail.com) for the bug report.

Bug: 19988704

Change-Id: I2493f737efd3fad72f6b41fb60eff1d3731613fb

compiler/dex/verified_method.cc
compiler/dex/verified_method.h
compiler/driver/compiler_driver.cc
compiler/driver/compiler_driver.h
compiler/optimizing/inliner.cc
compiler/optimizing/optimizing_compiler.cc
compiler/optimizing/optimizing_compiler_stats.h
test/471-uninitialized-locals/expected.txt [new file with mode: 0644]
test/471-uninitialized-locals/info.txt [new file with mode: 0644]
test/471-uninitialized-locals/smali/Test.smali [new file with mode: 0644]
test/471-uninitialized-locals/src/Main.java [new file with mode: 0644]

index 5b90ba9..7746221 100644 (file)
@@ -40,6 +40,7 @@ namespace art {
 const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier,
                                              bool compile) {
   std::unique_ptr<VerifiedMethod> verified_method(new VerifiedMethod);
+  verified_method->has_verification_failures_ = method_verifier->HasFailures();
   if (compile) {
     /* Generate a register map. */
     if (!verified_method->GenerateGcMap(method_verifier)) {
index 954cbf4..437ae52 100644 (file)
@@ -70,6 +70,11 @@ class VerifiedMethod {
   // by using the check-cast elision peephole optimization in the verifier.
   bool IsSafeCast(uint32_t pc) const;
 
+  // Returns true if there were any errors during verification.
+  bool HasVerificationFailures() const {
+    return has_verification_failures_;
+  }
+
  private:
   VerifiedMethod() = default;
 
@@ -107,6 +112,8 @@ class VerifiedMethod {
   // dex PC to dex method index or dex field index based on the instruction.
   DequickenMap dequicken_map_;
   SafeCastSet safe_cast_set_;
+
+  bool has_verification_failures_;
 };
 
 }  // namespace art
index c2b8375..1698464 100644 (file)
@@ -2345,6 +2345,13 @@ CompiledMethod* CompilerDriver::GetCompiledMethod(MethodReference ref) const {
   return it->second;
 }
 
+bool CompilerDriver::IsMethodVerifiedWithoutFailures(uint32_t method_idx,
+                                                     const DexFile& dex_file) const {
+  MethodReference method_ref(&dex_file, method_idx);
+  const VerifiedMethod* verified_method = GetVerificationResults()->GetVerifiedMethod(method_ref);
+  return (verified_method != nullptr) && !verified_method->HasVerificationFailures();
+}
+
 size_t CompilerDriver::GetNonRelativeLinkerPatchCount() const {
   MutexLock mu(Thread::Current(), compiled_methods_lock_);
   return non_relative_linker_patch_count_;
index a6ed559..b3ca25b 100644 (file)
@@ -425,6 +425,10 @@ class CompilerDriver {
   void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
       LOCKS_EXCLUDED(compiled_classes_lock_);
 
+  // Checks if the specified method has been verified without failures. Returns
+  // false if the method is not in the verification results (GetVerificationResults).
+  bool IsMethodVerifiedWithoutFailures(uint32_t method_idx, const DexFile& dex_file) const;
+
   SwapVector<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
   SwapSrcMap* DeduplicateSrcMappingTable(const ArrayRef<SrcMapElem>& src_map);
   SwapVector<uint8_t>* DeduplicateMappingTable(const ArrayRef<const uint8_t>& code);
index 2c17a67..65f1ed2 100644 (file)
@@ -31,6 +31,8 @@
 #include "ssa_phi_elimination.h"
 #include "scoped_thread_state_change.h"
 #include "thread.h"
+#include "dex/verified_method.h"
+#include "dex/verification_results.h"
 
 namespace art {
 
@@ -114,9 +116,10 @@ bool HInliner::TryInline(HInvoke* invoke_instruction,
     return false;
   }
 
-  if (!resolved_method->GetDeclaringClass()->IsVerified()) {
+  if (compiler_driver_->IsMethodVerifiedWithoutFailures(
+        method_index, *resolved_method->GetDexFile())) {
     VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
-                   << " is not inlined because its class could not be verified";
+                   << " has verification failures, so it cannot be inlined";
     return false;
   }
 
index 0e02212..4f58462 100644 (file)
@@ -31,6 +31,8 @@
 #include "constant_folding.h"
 #include "dead_code_elimination.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/verified_method.h"
+#include "dex/verification_results.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 #include "driver/dex_compilation_unit.h"
 #include "mirror/art_method-inl.h"
 #include "nodes.h"
 #include "prepare_for_register_allocation.h"
+#include "reference_type_propagation.h"
 #include "register_allocator.h"
 #include "side_effects_analysis.h"
 #include "ssa_builder.h"
 #include "ssa_phi_elimination.h"
 #include "ssa_liveness_analysis.h"
 #include "utils/assembler.h"
-#include "reference_type_propagation.h"
 
 namespace art {
 
@@ -606,15 +608,26 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
                                             InvokeType invoke_type,
                                             uint16_t class_def_idx,
                                             uint32_t method_idx,
-                                            jobject class_loader,
+                                            jobject jclass_loader,
                                             const DexFile& dex_file) const {
-  CompiledMethod* method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
-                                      method_idx, class_loader, dex_file);
+  CompilerDriver* compiler_driver = GetCompilerDriver();
+  CompiledMethod* method = nullptr;
+  if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, dex_file)) {
+     method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
+                         method_idx, jclass_loader, dex_file);
+  } else {
+    if (compiler_driver->GetCompilerOptions().VerifyAtRuntime()) {
+      compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledVerifyAtRuntime);
+    } else {
+      compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledClassNotVerified);
+    }
+  }
+
   if (method != nullptr) {
     return method;
   }
   method = delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
-                              class_loader, dex_file);
+                              jclass_loader, dex_file);
 
   if (method != nullptr) {
     compilation_stats_.RecordStat(MethodCompilationStat::kCompiledQuick);
index 4d5b8d0..d4a936d 100644 (file)
@@ -45,6 +45,8 @@ enum MethodCompilationStat {
   kNotCompiledCantAccesType,
   kNotOptimizedRegisterAllocator,
   kNotCompiledUnhandledInstruction,
+  kNotCompiledVerifyAtRuntime,
+  kNotCompiledClassNotVerified,
   kRemovedCheckedCast,
   kRemovedNullCheck,
   kInstructionSimplifications,
@@ -109,6 +111,8 @@ class OptimizingCompilerStats {
       case kNotCompiledSpaceFilter : return "kNotCompiledSpaceFilter";
       case kNotOptimizedRegisterAllocator : return "kNotOptimizedRegisterAllocator";
       case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction";
+      case kNotCompiledVerifyAtRuntime : return "kNotCompiledVerifyAtRuntime";
+      case kNotCompiledClassNotVerified : return "kNotCompiledClassNotVerified";
       case kRemovedCheckedCast: return "kRemovedCheckedCast";
       case kRemovedNullCheck: return "kRemovedNullCheck";
       case kInstructionSimplifications: return "kInstructionSimplifications";
diff --git a/test/471-uninitialized-locals/expected.txt b/test/471-uninitialized-locals/expected.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/471-uninitialized-locals/info.txt b/test/471-uninitialized-locals/info.txt
new file mode 100644 (file)
index 0000000..ebead8e
--- /dev/null
@@ -0,0 +1,2 @@
+Regression for the optimizing for crashes during compilation of methods which
+use values before initializing them.
diff --git a/test/471-uninitialized-locals/smali/Test.smali b/test/471-uninitialized-locals/smali/Test.smali
new file mode 100644 (file)
index 0000000..17a14bf
--- /dev/null
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2015 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.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method public static ThrowException()V
+   .registers 1
+   throw v0
+.end method
diff --git a/test/471-uninitialized-locals/src/Main.java b/test/471-uninitialized-locals/src/Main.java
new file mode 100644 (file)
index 0000000..b24c518
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+
+  public static void main(String args[]) throws Exception {
+    // Workaround for b/18051191.
+    System.out.println("Test started");
+    try {
+      Class<?> c = Class.forName("Test");
+      Method m = c.getMethod("ThrowException", (Class[]) null);
+      m.invoke(null, (Object[]) null);
+    } catch (VerifyError e) {
+       // Compilation should go fine but we expect the runtime verification to fail.
+      return;
+    }
+
+    throw new Error("Failed to preset verification error!");
+  }
+
+}