diff options
38 files changed, 763 insertions, 132 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b661e001c8..c27f8dbe4a 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -75,8 +75,11 @@ $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(call dexpreopt-remove-classes.dex,$@) ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali)) +ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali)) ART_TEST_HOST_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) ART_TEST_TARGET_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_TARGET_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_TARGET_GTEST_Main_DEX))))$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) +ART_TEST_HOST_GTEST_VerifierDepsMulti_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDepsMulti,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) +ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX := $(dir $(ART_TEST_TARGET_GTEST_Main_DEX))$(subst Main,VerifierDepsMulti,$(basename $(notdir $(ART_TEST_TARGET_GTEST_Main_DEX))))$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) $(ART_TEST_HOST_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^) @@ -84,6 +87,12 @@ $(ART_TEST_HOST_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(HO $(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^) +$(ART_TEST_HOST_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMulti_SRC) $(HOST_OUT_EXECUTABLES)/smali + $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^) + +$(ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMulti_SRC) $(HOST_OUT_EXECUTABLES)/smali + $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^) + # Dex file dependencies for each gtest. ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested @@ -115,7 +124,7 @@ ART_GTEST_stub_test_DEX_DEPS := AllFields ART_GTEST_transaction_test_DEX_DEPS := Transaction ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup ART_GTEST_unstarted_runtime_test_DEX_DEPS := Nested -ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps MultiDex +ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps VerifierDepsMulti MultiDex ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler # The elf writer test has dependencies on core.oat. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index a5e4cb0877..057e3c9960 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -535,9 +535,8 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( if (klass->IsVerified()) { // Class is verified so we can enable DEX-to-DEX compilation for performance. return max_level; - } else if (klass->IsCompileTimeVerified()) { + } else if (klass->ShouldVerifyAtRuntime()) { // Class verification has soft-failed. Anyway, ensure at least correctness. - DCHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime); return optimizer::DexToDexCompilationLevel::kRequired; } else { // Class verification has failed: do not run DEX-to-DEX compilation. @@ -964,7 +963,7 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader, if (cls == nullptr) { soa.Self()->ClearException(); } else if (&cls->GetDexFile() == dex_file) { - DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->IsCompileTimeVerified()) + DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->ShouldVerifyAtRuntime()) << cls->PrettyClass() << " " << cls->GetStatus(); } @@ -2160,6 +2159,14 @@ class VerifyClassVisitor : public CompilationVisitor { LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor) << " because: " << error_msg; manager_->GetCompiler()->SetHadHardVerifierFailure(); + } else { + // Force a soft failure for the VerifierDeps. This is a sanity measure, as + // the vdex file already records that the class hasn't been resolved. It avoids + // trying to do future verification optimizations when processing the vdex file. + DCHECK(failure_kind == verifier::MethodVerifier::kSoftFailure || + failure_kind == verifier::MethodVerifier::kNoFailure) + << failure_kind; + failure_kind = verifier::MethodVerifier::kSoftFailure; } } else if (!SkipClass(jclass_loader, dex_file, klass.Get())) { CHECK(klass->IsResolved()) << klass->PrettyClass(); @@ -2172,7 +2179,7 @@ class VerifyClassVisitor : public CompilationVisitor { manager_->GetCompiler()->SetHadHardVerifierFailure(); } - CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous()) + CHECK(klass->ShouldVerifyAtRuntime() || klass->IsVerified() || klass->IsErroneous()) << klass->PrettyDescriptor() << ": state=" << klass->GetStatus(); // It is *very* problematic if there are verification errors in the boot classpath. For example, @@ -2186,6 +2193,13 @@ class VerifyClassVisitor : public CompilationVisitor { DCHECK(klass->IsVerified()) << "Boot classpath class " << klass->PrettyClass() << " failed to fully verify: state= " << klass->GetStatus(); } + if (klass->IsVerified()) { + DCHECK_EQ(failure_kind, verifier::MethodVerifier::kNoFailure); + } else if (klass->ShouldVerifyAtRuntime()) { + DCHECK_EQ(failure_kind, verifier::MethodVerifier::kSoftFailure); + } else { + DCHECK_EQ(failure_kind, verifier::MethodVerifier::kHardFailure); + } } } else { // Make the skip a soft failure, essentially being considered as verify at runtime. diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 117d1131b5..65d82ed980 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -238,10 +238,11 @@ bool ImageWriter::Write(int image_fd, case ImageHeader::kStorageModeLZ4: { const size_t compressed_max_size = LZ4_compressBound(image_data_size); compressed_data.reset(new char[compressed_max_size]); - data_size = LZ4_compress( + data_size = LZ4_compress_default( reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader), &compressed_data[0], - image_data_size); + image_data_size, + compressed_max_size); break; } diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index 5d58207511..cb6e14b2bd 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -43,7 +43,7 @@ class BoundsCheckEliminationTest : public testing::Test { void RunBCE() { graph_->BuildDominatorTree(); - InstructionSimplifier(graph_).Run(); + InstructionSimplifier(graph_, /* codegen */ nullptr).Run(); SideEffectsAnalysis side_effects(graph_); side_effects.Run(); diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 1cd65c1c66..d6513c8e34 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -57,21 +57,27 @@ static bool IsIntAndGet(HInstruction* instruction, int64_t* value) { return false; } -/** Returns b^e for b,e >= 1. Sets overflow if arithmetic wrap-around occurred. */ +/** Computes a * b for a,b > 0 (at least until first overflow happens). */ +static int64_t SafeMul(int64_t a, int64_t b, /*out*/ bool* overflow) { + if (a > 0 && b > 0 && a > (std::numeric_limits<int64_t>::max() / b)) { + *overflow = true; + } + return a * b; +} + +/** Returns b^e for b,e > 0. Sets overflow if arithmetic wrap-around occurred. */ static int64_t IntPow(int64_t b, int64_t e, /*out*/ bool* overflow) { - DCHECK_GE(b, 1); - DCHECK_GE(e, 1); + DCHECK_LT(0, b); + DCHECK_LT(0, e); int64_t pow = 1; while (e) { if (e & 1) { - int64_t oldpow = pow; - pow *= b; - if (pow < oldpow) { - *overflow = true; - } + pow = SafeMul(pow, b, overflow); } e >>= 1; - b *= b; + if (e) { + b = SafeMul(b, b, overflow); + } } return pow; } @@ -384,7 +390,8 @@ bool InductionVarRange::IsUnitStride(HInstruction* instruction, HInductionVarAnalysis::InductionInfo* trip = nullptr; if (HasInductionInfo(instruction, instruction, &loop, &info, &trip)) { if (info->induction_class == HInductionVarAnalysis::kLinear && - info->op_b->operation == HInductionVarAnalysis::kFetch) { + info->op_b->operation == HInductionVarAnalysis::kFetch && + !HInductionVarAnalysis::IsNarrowingLinear(info)) { int64_t stride_value = 0; if (IsConstant(info->op_a, kExact, &stride_value) && stride_value == 1) { int64_t off_value = 0; diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 3e340908bf..e3926c58c4 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -549,7 +549,7 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, is_referrer, invoke_instruction->GetDexPc(), /* needs_access_check */ false); - HLoadClass::LoadKind kind = HSharpening::SharpenClass( + HLoadClass::LoadKind kind = HSharpening::ComputeLoadClassKind( load_class, codegen_, compiler_driver_, caller_compilation_unit_); DCHECK(kind != HLoadClass::LoadKind::kInvalid) << "We should always be able to reference a class for inline caches"; @@ -1491,7 +1491,7 @@ size_t HInliner::RunOptimizations(HGraph* callee_graph, HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner"); HConstantFolding fold(callee_graph, "constant_folding$inliner"); HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_, handles_); - InstructionSimplifier simplify(callee_graph, inline_stats_); + InstructionSimplifier simplify(callee_graph, codegen_, inline_stats_); IntrinsicsRecognizer intrinsics(callee_graph, inline_stats_); HOptimization* optimizations[] = { diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index c60f6e5393..fe11433ae0 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1676,10 +1676,10 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, dex_pc, needs_access_check); - HLoadClass::LoadKind load_kind = HSharpening::SharpenClass(load_class, - code_generator_, - compiler_driver_, - *dex_compilation_unit_); + HLoadClass::LoadKind load_kind = HSharpening::ComputeLoadClassKind(load_class, + code_generator_, + compiler_driver_, + *dex_compilation_unit_); if (load_kind == HLoadClass::LoadKind::kInvalid) { // We actually cannot reference this class, we're forced to bail. diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 35f59cb4a4..17421fc364 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -19,14 +19,18 @@ #include "escape.h" #include "intrinsics.h" #include "mirror/class-inl.h" +#include "sharpening.h" #include "scoped_thread_state_change-inl.h" namespace art { class InstructionSimplifierVisitor : public HGraphDelegateVisitor { public: - InstructionSimplifierVisitor(HGraph* graph, OptimizingCompilerStats* stats) + InstructionSimplifierVisitor(HGraph* graph, + CodeGenerator* codegen, + OptimizingCompilerStats* stats) : HGraphDelegateVisitor(graph), + codegen_(codegen), stats_(stats) {} void Run(); @@ -112,6 +116,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyAllocationIntrinsic(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); + CodeGenerator* codegen_; OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; int simplifications_at_current_position_ = 0; @@ -123,7 +128,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { }; void InstructionSimplifier::Run() { - InstructionSimplifierVisitor visitor(graph_, stats_); + InstructionSimplifierVisitor visitor(graph_, codegen_, stats_); visitor.Run(); } @@ -1805,6 +1810,8 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) { ScopedObjectAccess soa(Thread::Current()); + Primitive::Type source_component_type = Primitive::kPrimVoid; + Primitive::Type destination_component_type = Primitive::kPrimVoid; ReferenceTypeInfo destination_rti = destination->GetReferenceTypeInfo(); if (destination_rti.IsValid()) { if (destination_rti.IsObjectArray()) { @@ -1814,6 +1821,8 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) optimizations.SetDestinationIsTypedObjectArray(); } if (destination_rti.IsPrimitiveArrayClass()) { + destination_component_type = + destination_rti.GetTypeHandle()->GetComponentType()->GetPrimitiveType(); optimizations.SetDestinationIsPrimitiveArray(); } else if (destination_rti.IsNonPrimitiveArrayClass()) { optimizations.SetDestinationIsNonPrimitiveArray(); @@ -1826,10 +1835,55 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) } if (source_rti.IsPrimitiveArrayClass()) { optimizations.SetSourceIsPrimitiveArray(); + source_component_type = source_rti.GetTypeHandle()->GetComponentType()->GetPrimitiveType(); } else if (source_rti.IsNonPrimitiveArrayClass()) { optimizations.SetSourceIsNonPrimitiveArray(); } } + // For primitive arrays, use their optimized ArtMethod implementations. + if ((source_component_type != Primitive::kPrimVoid) && + (source_component_type == destination_component_type)) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + PointerSize image_size = class_linker->GetImagePointerSize(); + HInvokeStaticOrDirect* invoke = instruction->AsInvokeStaticOrDirect(); + mirror::Class* system = invoke->GetResolvedMethod()->GetDeclaringClass(); + ArtMethod* method = nullptr; + switch (source_component_type) { + case Primitive::kPrimBoolean: + method = system->FindDeclaredDirectMethod("arraycopy", "([ZI[ZII)V", image_size); + break; + case Primitive::kPrimByte: + method = system->FindDeclaredDirectMethod("arraycopy", "([BI[BII)V", image_size); + break; + case Primitive::kPrimChar: + method = system->FindDeclaredDirectMethod("arraycopy", "([CI[CII)V", image_size); + break; + case Primitive::kPrimShort: + method = system->FindDeclaredDirectMethod("arraycopy", "([SI[SII)V", image_size); + break; + case Primitive::kPrimInt: + method = system->FindDeclaredDirectMethod("arraycopy", "([II[III)V", image_size); + break; + case Primitive::kPrimFloat: + method = system->FindDeclaredDirectMethod("arraycopy", "([FI[FII)V", image_size); + break; + case Primitive::kPrimLong: + method = system->FindDeclaredDirectMethod("arraycopy", "([JI[JII)V", image_size); + break; + case Primitive::kPrimDouble: + method = system->FindDeclaredDirectMethod("arraycopy", "([DI[DII)V", image_size); + break; + default: + LOG(FATAL) << "Unreachable"; + } + DCHECK(method != nullptr); + invoke->SetResolvedMethod(method); + // Sharpen the new invoke. Note that we do not update the dex method index of + // the invoke, as we would need to look it up in the current dex file, and it + // is unlikely that it exists. The most usual situation for such typed + // arraycopy methods is a direct pointer to the boot image. + HSharpening::SharpenInvokeStaticOrDirect(invoke, codegen_); + } } } diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h index 7fe1067aa9..f7329a4a1f 100644 --- a/compiler/optimizing/instruction_simplifier.h +++ b/compiler/optimizing/instruction_simplifier.h @@ -23,6 +23,8 @@ namespace art { +class CodeGenerator; + /** * Implements optimizations specific to each instruction. * @@ -36,15 +38,19 @@ namespace art { class InstructionSimplifier : public HOptimization { public: explicit InstructionSimplifier(HGraph* graph, + CodeGenerator* codegen, OptimizingCompilerStats* stats = nullptr, const char* name = kInstructionSimplifierPassName) - : HOptimization(graph, name, stats) {} + : HOptimization(graph, name, stats), + codegen_(codegen) {} static constexpr const char* kInstructionSimplifierPassName = "instruction_simplifier"; void Run() OVERRIDE; private: + CodeGenerator* codegen_; + DISALLOW_COPY_AND_ASSIGN(InstructionSimplifier); }; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index c39aed2c6a..69ef9e178c 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -3915,6 +3915,7 @@ class HInvoke : public HVariableInputSizeInstruction { bool IsIntrinsic() const { return intrinsic_ != Intrinsics::kNone; } ArtMethod* GetResolvedMethod() const { return resolved_method_; } + void SetResolvedMethod(ArtMethod* method) { resolved_method_ = method; } DECLARE_ABSTRACT_INSTRUCTION(Invoke); @@ -3957,7 +3958,7 @@ class HInvoke : public HVariableInputSizeInstruction { } uint32_t number_of_arguments_; - ArtMethod* const resolved_method_; + ArtMethod* resolved_method_; const uint32_t dex_method_index_; Intrinsics intrinsic_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 607b9433ae..d6153b091c 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -507,7 +507,7 @@ static HOptimization* BuildOptimization( } else if (opt_name == HInductionVarAnalysis::kInductionPassName) { return new (arena) HInductionVarAnalysis(graph); } else if (opt_name == InstructionSimplifier::kInstructionSimplifierPassName) { - return new (arena) InstructionSimplifier(graph, stats, pass_name.c_str()); + return new (arena) InstructionSimplifier(graph, codegen, stats, pass_name.c_str()); } else if (opt_name == IntrinsicsRecognizer::kIntrinsicsRecognizerPassName) { return new (arena) IntrinsicsRecognizer(graph, stats); } else if (opt_name == LICM::kLoopInvariantCodeMotionPassName) { @@ -768,7 +768,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, HDeadCodeElimination* dce3 = new (arena) HDeadCodeElimination( graph, stats, "dead_code_elimination$final"); HConstantFolding* fold1 = new (arena) HConstantFolding(graph, "constant_folding"); - InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats); + InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, codegen, stats); HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph, stats); HConstantFolding* fold2 = new (arena) HConstantFolding( graph, "constant_folding$after_inlining"); @@ -786,11 +786,11 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, HSharpening* sharpening = new (arena) HSharpening( graph, codegen, dex_compilation_unit, driver, handles); InstructionSimplifier* simplify2 = new (arena) InstructionSimplifier( - graph, stats, "instruction_simplifier$after_inlining"); + graph, codegen, stats, "instruction_simplifier$after_inlining"); InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier( - graph, stats, "instruction_simplifier$after_bce"); + graph, codegen, stats, "instruction_simplifier$after_bce"); InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier( - graph, stats, "instruction_simplifier$before_codegen"); + graph, codegen, stats, "instruction_simplifier$before_codegen"); IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, stats); CHAGuardOptimization* cha_guard = new (arena) CHAGuardOptimization(graph); CodeSinking* code_sinking = new (arena) CodeSinking(graph, stats); diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index be400925d5..8f1827be69 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -41,7 +41,7 @@ void HSharpening::Run() { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); if (instruction->IsInvokeStaticOrDirect()) { - ProcessInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect()); + SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), codegen_); } else if (instruction->IsLoadString()) { ProcessLoadString(instruction->AsLoadString()); } @@ -70,7 +70,9 @@ static bool AOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& options) return IsInBootImage(method) && !options.GetCompilePic() && !options.GetIncludePatchInformation(); } -void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + +void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, + CodeGenerator* codegen) { if (invoke->IsStringInit()) { // Not using the dex cache arrays. But we could still try to use a better dispatch... // TODO: Use direct_method and direct_code for the appropriate StringFactory method. @@ -97,12 +99,12 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { // We don't optimize for debuggable as it would prevent us from obsoleting the method in some // situations. - if (callee == codegen_->GetGraph()->GetArtMethod() && !codegen_->GetGraph()->IsDebuggable()) { + if (callee == codegen->GetGraph()->GetArtMethod() && !codegen->GetGraph()->IsDebuggable()) { // Recursive call. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive; code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf; } else if (Runtime::Current()->UseJitCompilation() || - AOTCanEmbedMethod(callee, codegen_->GetCompilerOptions())) { + AOTCanEmbedMethod(callee, codegen->GetCompilerOptions())) { // JIT or on-device AOT compilation referencing a boot image method. // Use the method address directly. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress; @@ -111,13 +113,17 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { } else { // Use PC-relative access to the dex cache arrays. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative; - DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()), - &graph_->GetDexFile()); + // Note: we use the invoke's graph instead of the codegen graph, which are + // different when inlining (the codegen graph is the most outer graph). The + // invoke's dex method index is relative to the dex file where the invoke's graph + // was built from. + DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen->GetInstructionSet()), + &invoke->GetBlock()->GetGraph()->GetDexFile()); method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex()); code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } - if (graph_->IsDebuggable()) { + if (codegen->GetGraph()->IsDebuggable()) { // For debuggable apps always use the code pointer from ArtMethod // so that we don't circumvent instrumentation stubs if installed. code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; @@ -127,14 +133,14 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { method_load_kind, code_ptr_location, method_load_data }; HInvokeStaticOrDirect::DispatchInfo dispatch_info = - codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke); + codegen->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke); invoke->SetDispatchInfo(dispatch_info); } -HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit) { +HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit) { Handle<mirror::Class> klass = load_class->GetClass(); DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod || load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index 4240b2f339..10707c796f 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -48,14 +48,16 @@ class HSharpening : public HOptimization { static constexpr const char* kSharpeningPassName = "sharpening"; // Used by the builder and the inliner. - static HLoadClass::LoadKind SharpenClass(HLoadClass* load_class, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit) + static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit) REQUIRES_SHARED(Locks::mutator_lock_); + // Used by Sharpening and InstructionSimplifier. + static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, CodeGenerator* codegen); + private: - void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke); void ProcessLoadString(HLoadString* load_string); CodeGenerator* codegen_; diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index c892b25ed3..01c33591e5 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -246,9 +246,13 @@ class VerifierDepsTest : public CommonCompilerTest { } bool HasUnverifiedClass(const std::string& cls) { - const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str()); + return HasUnverifiedClass(cls, *primary_dex_file_); + } + + bool HasUnverifiedClass(const std::string& cls, const DexFile& dex_file) { + const DexFile::TypeId* type_id = dex_file.FindTypeId(cls.c_str()); DCHECK(type_id != nullptr); - dex::TypeIndex index = primary_dex_file_->GetIndexForTypeId(*type_id); + dex::TypeIndex index = dex_file.GetIndexForTypeId(*type_id); for (const auto& dex_dep : verifier_deps_->dex_deps_) { for (dex::TypeIndex entry : dex_dep.second->unverified_classes_) { if (index == entry) { @@ -1141,7 +1145,7 @@ TEST_F(VerifierDepsTest, UnverifiedClasses) { // Test that a class with hard failure is recorded. ASSERT_TRUE(HasUnverifiedClass("LMyVerificationFailure;")); // Test that a class with unresolved super is recorded. - ASSERT_FALSE(HasUnverifiedClass("LMyClassWithNoSuper;")); + ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuper;")); // Test that a class with unresolved super and hard failure is recorded. ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;")); } @@ -1511,5 +1515,18 @@ TEST_F(VerifierDepsTest, CompilerDriver) { } } +TEST_F(VerifierDepsTest, MultiDexVerification) { + VerifyDexFile("VerifierDepsMulti"); + ASSERT_EQ(NumberOfCompiledDexFiles(), 2u); + + ASSERT_TRUE(HasUnverifiedClass("LMySoftVerificationFailure;", *dex_files_[1])); + ASSERT_TRUE(HasUnverifiedClass("LMySub1SoftVerificationFailure;", *dex_files_[0])); + ASSERT_TRUE(HasUnverifiedClass("LMySub2SoftVerificationFailure;", *dex_files_[0])); + + std::vector<uint8_t> buffer; + verifier_deps_->Encode(dex_files_, &buffer); + ASSERT_FALSE(buffer.empty()); +} + } // namespace verifier } // namespace art diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 5a758aedf4..52f3b52ee2 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -182,7 +182,8 @@ class ProfileAssistantTest : public CommonRuntimeTest { void AssertInlineCaches(ArtMethod* method, const std::set<mirror::Class*>& expected_clases, const ProfileCompilationInfo& info, - bool megamorphic) + bool is_megamorphic, + bool is_missing_types) REQUIRES_SHARED(Locks::mutator_lock_) { ProfileCompilationInfo::OfflineProfileMethodInfo pmi; ASSERT_TRUE(info.GetMethod(method->GetDexFile()->GetLocation(), @@ -192,7 +193,8 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_EQ(pmi.inline_caches.size(), 1u); ProfileCompilationInfo::DexPcData dex_pc_data = pmi.inline_caches.begin()->second; - ASSERT_EQ(dex_pc_data.is_megamorphic, megamorphic); + ASSERT_EQ(dex_pc_data.is_megamorphic, is_megamorphic); + ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types); ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size()); size_t found = 0; for (mirror::Class* it : expected_clases) { @@ -482,6 +484,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "LTestInline;->inlineMonomorphic(LSuper;)I+LSubA;", "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;", "LTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;", + "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types", "LTestInline;->noInlineCache(LSuper;)I" }; std::string input_file_contents; @@ -521,7 +524,11 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { ASSERT_TRUE(inline_monomorphic != nullptr); std::set<mirror::Class*> expected_monomorphic; expected_monomorphic.insert(sub_a); - AssertInlineCaches(inline_monomorphic, expected_monomorphic, info, /*megamorphic*/ false); + AssertInlineCaches(inline_monomorphic, + expected_monomorphic, + info, + /*megamorphic*/false, + /*missing_types*/false); } { @@ -534,7 +541,11 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { expected_polymorphic.insert(sub_a); expected_polymorphic.insert(sub_b); expected_polymorphic.insert(sub_c); - AssertInlineCaches(inline_polymorhic, expected_polymorphic, info, /*megamorphic*/ false); + AssertInlineCaches(inline_polymorhic, + expected_polymorphic, + info, + /*megamorphic*/false, + /*missing_types*/false); } { @@ -544,7 +555,25 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "inlineMegamorphic"); ASSERT_TRUE(inline_megamorphic != nullptr); std::set<mirror::Class*> expected_megamorphic; - AssertInlineCaches(inline_megamorphic, expected_megamorphic, info, /*megamorphic*/ true); + AssertInlineCaches(inline_megamorphic, + expected_megamorphic, + info, + /*megamorphic*/true, + /*missing_types*/false); + } + + { + // Verify that method inlineMegamorphic has the expected inline caches and nothing else. + ArtMethod* inline_missing_types = GetVirtualMethod(class_loader, + "LTestInline;", + "inlineMissingTypes"); + ASSERT_TRUE(inline_missing_types != nullptr); + std::set<mirror::Class*> expected_missing_Types; + AssertInlineCaches(inline_missing_types, + expected_missing_Types, + info, + /*megamorphic*/false, + /*missing_types*/true); } { diff --git a/profman/profman.cc b/profman/profman.cc index a99a0ea2da..f7316cc129 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -139,6 +139,7 @@ static constexpr uint16_t kDefaultTestProfileClassRatio = 5; // Separators used when parsing human friendly representation of profiles. static const std::string kMethodSep = "->"; +static const std::string kMissingTypesMarker = "missing_types"; static constexpr char kProfileParsingInlineChacheSep = '+'; static constexpr char kProfileParsingTypeSep = ','; static constexpr char kProfileParsingFirstCharInSignature = '('; @@ -624,8 +625,11 @@ class ProfMan FINAL { // Process a line defining a class or a method and its inline caches. // Upon success return true and add the class or the method info to profile. - // The format of the method line is: + // The possible line formats are: + // "LJustTheCass;". // "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;". + // "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types". + // "LTestInline;->inlineNoInlineCaches(LSuper;)I". // The method and classes are searched only in the given dex files. bool ProcessLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files, const std::string& line, @@ -642,7 +646,7 @@ class ProfMan FINAL { ProfileMethodInfo::ProfileClassReference class_ref; if (!FindClass(dex_files, klass, &class_ref)) { - LOG(ERROR) << "Could not find class: " << klass; + LOG(WARNING) << "Could not find class: " << klass; return false; } @@ -664,10 +668,14 @@ class ProfMan FINAL { std::vector<std::string> inline_cache_elems; std::vector<std::string> method_elems; + bool is_missing_types = false; Split(method_str, kProfileParsingInlineChacheSep, &method_elems); if (method_elems.size() == 2) { method_spec = method_elems[0]; - Split(method_elems[1], kProfileParsingTypeSep, &inline_cache_elems); + is_missing_types = method_elems[1] == kMissingTypesMarker; + if (!is_missing_types) { + Split(method_elems[1], kProfileParsingTypeSep, &inline_cache_elems); + } } else if (method_elems.size() == 1) { method_spec = method_elems[0]; } else { @@ -689,7 +697,7 @@ class ProfMan FINAL { } } std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches; - inline_caches.emplace_back(dex_pc, classes); + inline_caches.emplace_back(dex_pc, is_missing_types, classes); std::vector<ProfileMethodInfo> pmi; pmi.emplace_back(class_ref.dex_file, method_index, inline_caches); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index d232714338..b611aa2132 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3900,8 +3900,10 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self, if (!supertype->IsVerified() && !supertype->IsErroneous()) { VerifyClass(self, supertype); } - if (supertype->IsCompileTimeVerified()) { - // Either we are verified or we soft failed and need to retry at runtime. + + if (supertype->IsVerified() || supertype->ShouldVerifyAtRuntime()) { + // The supertype is either verified, or we soft failed at AOT time. + DCHECK(supertype->IsVerified() || Runtime::Current()->IsAotCompiler()); return true; } // If we got this far then we have a hard failure. @@ -3967,13 +3969,16 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass( return verifier::MethodVerifier::kHardFailure; } - // Don't attempt to re-verify if already sufficiently verified. + // Don't attempt to re-verify if already verified. if (klass->IsVerified()) { EnsureSkipAccessChecksMethods(klass, image_pointer_size_); return verifier::MethodVerifier::kNoFailure; } - if (klass->IsCompileTimeVerified() && Runtime::Current()->IsAotCompiler()) { - return verifier::MethodVerifier::kNoFailure; + + // For AOT, don't attempt to re-verify if we have already found we should + // verify at runtime. + if (Runtime::Current()->IsAotCompiler() && klass->ShouldVerifyAtRuntime()) { + return verifier::MethodVerifier::kSoftFailure; } if (klass->GetStatus() == mirror::Class::kStatusResolved) { @@ -4926,7 +4931,15 @@ bool ClassLinker::InitializeDefaultInterfaceRecursive(Thread* self, // First we initialize all of iface's super-interfaces recursively. for (size_t i = 0; i < num_direct_ifaces; i++) { ObjPtr<mirror::Class> super_iface = mirror::Class::GetDirectInterface(self, iface.Get(), i); - DCHECK(super_iface != nullptr); + if (UNLIKELY(super_iface == nullptr)) { + const char* iface_descriptor = + iface->GetDexFile().StringByTypeIdx(iface->GetDirectInterfaceTypeIdx(i)); + LOG(FATAL) << "Check failed: super_iface != nullptr " + << "Debug data for bug 34839984: " + << iface->PrettyDescriptor() << " iface #" << i << " " << iface_descriptor + << " space: " << DescribeSpace(iface.Get()) + << " loaders: " << DescribeLoaders(iface.Get(), iface_descriptor); + } if (!super_iface->HasBeenRecursivelyInitialized()) { // Recursive step handle_super_iface.Assign(super_iface); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 62acedfb1b..8b2a2b4d13 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -1262,6 +1262,7 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca for (size_t i = 0; i < info->number_of_inline_caches_; ++i) { std::vector<ProfileMethodInfo::ProfileClassReference> profile_classes; const InlineCache& cache = info->cache_[i]; + bool is_missing_types = false; for (size_t k = 0; k < InlineCache::kIndividualCacheSize; k++) { mirror::Class* cls = cache.classes_[k].Read(); if (cls == nullptr) { @@ -1284,17 +1285,20 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca } if (!type_index.IsValid()) { // Could be a proxy class or an array for which we couldn't find the type index. + is_missing_types = true; continue; } if (ContainsElement(dex_base_locations, class_dex_file->GetBaseLocation())) { // Only consider classes from the same apk (including multidex). profile_classes.emplace_back(/*ProfileMethodInfo::ProfileClassReference*/ class_dex_file, type_index); + } else { + is_missing_types = true; } } if (!profile_classes.empty()) { inline_caches.emplace_back(/*ProfileMethodInfo::ProfileInlineCache*/ - cache.dex_pc_, profile_classes); + cache.dex_pc_, is_missing_types, profile_classes); } } methods.emplace_back(/*ProfileMethodInfo*/ diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 8c5bb0d775..b23a86313f 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -47,16 +47,19 @@ static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; // using the same test profile. static constexpr bool kDebugIgnoreChecksum = false; -static constexpr uint8_t kMegamorphicEncoding = 7; +static constexpr uint8_t kIsMissingTypesEncoding = 6; +static constexpr uint8_t kIsMegamorphicEncoding = 7; static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t), "InlineCache::kIndividualCacheSize does not have the expect type size"); -static_assert(InlineCache::kIndividualCacheSize < kMegamorphicEncoding, +static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding, + "InlineCache::kIndividualCacheSize is larger than expected"); +static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding, "InlineCache::kIndividualCacheSize is larger than expected"); void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx, const dex::TypeIndex& type_idx) { - if (is_megamorphic) { + if (is_megamorphic || is_missing_types) { return; } classes.emplace(dex_profile_idx, type_idx); @@ -207,7 +210,8 @@ static constexpr size_t kLineHeaderSize = * Classes are grouped per their dex files and the line * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...` - * M stands for megamorphic and it's encoded as the byte kMegamorphicEncoding. + * M stands for megamorphic or missing types and it's encoded as either + * the byte kIsMegamorphicEncoding or kIsMissingTypesEncoding. * When present, there will be no class ids following. **/ bool ProfileCompilationInfo::Save(int fd) { @@ -298,10 +302,19 @@ void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer // Add the dex pc. AddUintToBuffer(buffer, dex_pc); - if (dex_pc_data.is_megamorphic) { - // Add the megamorphic encoding if needed and continue. - // If megamorphic, we don't add the rest of the classes. - AddUintToBuffer(buffer, kMegamorphicEncoding); + // Add the megamorphic/missing_types encoding if needed and continue. + // In either cases we don't add any classes to the profiles and so there's + // no point to continue. + // TODO(calin): in case we miss types there is still value to add the + // rest of the classes. They can be added without bumping the profile version. + if (dex_pc_data.is_missing_types) { + DCHECK(!dex_pc_data.is_megamorphic); // at this point the megamorphic flag should not be set. + DCHECK_EQ(classes.size(), 0u); + AddUintToBuffer(buffer, kIsMissingTypesEncoding); + continue; + } else if (dex_pc_data.is_megamorphic) { + DCHECK_EQ(classes.size(), 0u); + AddUintToBuffer(buffer, kIsMegamorphicEncoding); continue; } @@ -412,11 +425,21 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location, for (const auto& pmi_inline_cache_it : pmi.inline_caches) { uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first; const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second; - auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(pmi_ic_dex_pc); + DexPcData& dex_pc_data = inline_cache_it->second.FindOrAdd(pmi_ic_dex_pc)->second; + if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) { + // We are already megamorphic or we are missing types; no point in going forward. + continue; + } + + if (pmi_ic_dex_pc_data.is_missing_types) { + dex_pc_data.SetIsMissingTypes(); + continue; + } if (pmi_ic_dex_pc_data.is_megamorphic) { - dex_pc_data_it->second.SetMegamorphic(); + dex_pc_data.SetIsMegamorphic(); continue; } + for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) { const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index]; DexFileData* class_dex_data = GetOrAddDexFileData( @@ -425,7 +448,7 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location, if (class_dex_data == nullptr) { // checksum mismatch return false; } - dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index); + dex_pc_data.AddClass(class_dex_data->profile_index, class_ref.type_index); } } return true; @@ -441,6 +464,11 @@ bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) { auto inline_cache_it = data->method_map.FindOrAdd(pmi.dex_method_index); for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) { + if (cache.is_missing_types) { + auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc); + dex_pc_data_it->second.SetIsMissingTypes(); + continue; + } for (const ProfileMethodInfo::ProfileClassReference& class_ref : cache.classes) { DexFileData* class_dex_data = GetOrAddDexFileData( GetProfileDexFileKey(class_ref.dex_file->GetLocation()), @@ -449,6 +477,10 @@ bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) { return false; } auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc); + if (dex_pc_data_it->second.is_missing_types) { + // Don't bother adding classes if we are missing types. + break; + } dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index); } } @@ -487,8 +519,12 @@ bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer, READ_UINT(uint16_t, buffer, dex_pc, error); READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error); auto dex_pc_data_it = inline_cache->FindOrAdd(dex_pc); - if (dex_to_classes_map_size == kMegamorphicEncoding) { - dex_pc_data_it->second.SetMegamorphic(); + if (dex_to_classes_map_size == kIsMissingTypesEncoding) { + dex_pc_data_it->second.SetIsMissingTypes(); + continue; + } + if (dex_to_classes_map_size == kIsMegamorphicEncoding) { + dex_pc_data_it->second.SetIsMegamorphic(); continue; } for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) { @@ -835,8 +871,10 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) { uint16_t other_dex_pc = other_ic_it.first; const ClassSet& other_class_set = other_ic_it.second.classes; auto class_set = method_it->second.FindOrAdd(other_dex_pc); - if (other_ic_it.second.is_megamorphic) { - class_set->second.SetMegamorphic(); + if (other_ic_it.second.is_missing_types) { + class_set->second.SetIsMissingTypes(); + } else if (other_ic_it.second.is_megamorphic) { + class_set->second.SetIsMegamorphic(); } else { for (const auto& class_it : other_class_set) { class_set->second.AddClass(dex_profile_index_remap.Get( @@ -999,8 +1037,10 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* os << "["; for (const auto& inline_cache_it : method_it.second) { os << "{" << std::hex << inline_cache_it.first << std::dec << ":"; - if (inline_cache_it.second.is_megamorphic) { - os << "M"; + if (inline_cache_it.second.is_missing_types) { + os << "MT"; + } else if (inline_cache_it.second.is_megamorphic) { + os << "MM"; } else { for (const ClassReference& class_ref : inline_cache_it.second.classes) { os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index) @@ -1136,7 +1176,7 @@ bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==( } // We can't use a simple equality test because we need to match the dex files - // of the inline caches which might have different profile indices. + // of the inline caches which might have different profile indexes. for (const auto& inline_cache_it : inline_caches) { uint16_t dex_pc = inline_cache_it.first; const DexPcData dex_pc_data = inline_cache_it.second; @@ -1145,7 +1185,8 @@ bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==( return false; } const DexPcData& other_dex_pc_data = other_it->second; - if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic) { + if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic || + dex_pc_data.is_missing_types != other_dex_pc_data.is_missing_types) { return false; } for (const ClassReference& class_ref : dex_pc_data.classes) { diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index f089dff898..6ad528c805 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -45,10 +45,13 @@ struct ProfileMethodInfo { }; struct ProfileInlineCache { - ProfileInlineCache(uint32_t pc, const std::vector<ProfileClassReference>& profile_classes) - : dex_pc(pc), classes(profile_classes) {} + ProfileInlineCache(uint32_t pc, + bool missing_types, + const std::vector<ProfileClassReference>& profile_classes) + : dex_pc(pc), is_missing_types(missing_types), classes(profile_classes) {} const uint32_t dex_pc; + const bool is_missing_types; const std::vector<ProfileClassReference> classes; }; @@ -134,18 +137,30 @@ class ProfileCompilationInfo { // Encodes the actual inline cache for a given dex pc (whether or not the receiver is // megamorphic and its possible types). - // If the receiver is megamorphic the set of classes will be empty. + // If the receiver is megamorphic or is missing types the set of classes will be empty. struct DexPcData { - DexPcData() : is_megamorphic(false) {} + DexPcData() : is_missing_types(false), is_megamorphic(false) {} void AddClass(uint16_t dex_profile_idx, const dex::TypeIndex& type_idx); - void SetMegamorphic() { + void SetIsMegamorphic() { + if (is_missing_types) return; is_megamorphic = true; classes.clear(); } + void SetIsMissingTypes() { + is_megamorphic = false; + is_missing_types = true; + classes.clear(); + } bool operator==(const DexPcData& other) const { - return is_megamorphic == other.is_megamorphic && classes == other.classes; + return is_megamorphic == other.is_megamorphic && + is_missing_types == other.is_missing_types && + classes == other.classes; } + // Not all runtime types can be encoded in the profile. For example if the receiver + // type is in a dex file which is not tracked for profiling its type cannot be + // encoded. When types are missing this field will be set to true. + bool is_missing_types; bool is_megamorphic; ClassSet classes; }; diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 332280a0e3..5cd8e8fef5 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -108,26 +108,31 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { for (ArtMethod* method : methods) { std::vector<ProfileMethodInfo::ProfileInlineCache> caches; // Monomorphic - for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) { + for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { std::vector<ProfileMethodInfo::ProfileClassReference> classes; classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0)); - caches.emplace_back(dex_pc, classes); + caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); } // Polymorphic - for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) { + for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { std::vector<ProfileMethodInfo::ProfileClassReference> classes; for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) { classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); } - caches.emplace_back(dex_pc, classes); + caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); } // Megamorphic - for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) { + for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { std::vector<ProfileMethodInfo::ProfileClassReference> classes; for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) { classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); } - caches.emplace_back(dex_pc, classes); + caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); + } + // Missing types + for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { + std::vector<ProfileMethodInfo::ProfileClassReference> classes; + caches.emplace_back(dex_pc, /*is_missing_types*/true, classes); } ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches); profile_methods.push_back(pmi); @@ -148,12 +153,15 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi; SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index for (const auto& inline_cache : pmi.inline_caches) { + ProfileCompilationInfo::DexPcData& dex_pc_data = + offline_pmi.inline_caches.FindOrAdd(inline_cache.dex_pc)->second; + if (inline_cache.is_missing_types) { + dex_pc_data.SetIsMissingTypes(); + } for (const auto& class_ref : inline_cache.classes) { uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file), static_cast<uint8_t>(dex_map.size()))->second; - offline_pmi.inline_caches - .FindOrAdd(inline_cache.dex_pc)->second - .AddClass(dex_profile_index, class_ref.type_index); + dex_pc_data.AddClass(dex_profile_index, class_ref.type_index); if (dex_profile_index >= offline_pmi.dex_references.size()) { // This is a new dex. const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey( @@ -170,18 +178,18 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() { ProfileCompilationInfo::OfflineProfileMethodInfo pmi; - pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); - pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2); - pmi.dex_references.emplace_back("dex_location3", /* checksum */ 3); + pmi.dex_references.emplace_back("dex_location1", /* checksum */1); + pmi.dex_references.emplace_back("dex_location2", /* checksum */2); + pmi.dex_references.emplace_back("dex_location3", /* checksum */3); // Monomorphic - for (uint16_t dex_pc = 0; dex_pc < 10; dex_pc++) { + for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { ProfileCompilationInfo::DexPcData dex_pc_data; dex_pc_data.AddClass(0, dex::TypeIndex(0)); pmi.inline_caches.Put(dex_pc, dex_pc_data); } // Polymorphic - for (uint16_t dex_pc = 10; dex_pc < 20; dex_pc++) { + for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { ProfileCompilationInfo::DexPcData dex_pc_data; dex_pc_data.AddClass(0, dex::TypeIndex(0)); dex_pc_data.AddClass(1, dex::TypeIndex(1)); @@ -190,9 +198,15 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { pmi.inline_caches.Put(dex_pc, dex_pc_data); } // Megamorphic - for (uint16_t dex_pc = 20; dex_pc < 30; dex_pc++) { + for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.SetIsMegamorphic(); + pmi.inline_caches.Put(dex_pc, dex_pc_data); + } + // Missing types + for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { ProfileCompilationInfo::DexPcData dex_pc_data; - dex_pc_data.is_megamorphic = true; + dex_pc_data.SetIsMissingTypes(); pmi.inline_caches.Put(dex_pc, dex_pc_data); } @@ -207,7 +221,13 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { } } - // Cannot sizeof the actual arrays so hardcode the values here. + void SetIsMissingTypes(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) { + for (auto it : pmi->inline_caches) { + it.second.SetIsMissingTypes(); + } + } + + // Cannot sizeof the actual arrays so hard code the values here. // They should not change anyway. static constexpr int kProfileMagicSize = 4; static constexpr int kProfileVersionSize = 4; @@ -530,6 +550,58 @@ TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) { ASSERT_TRUE(loaded_pmi1 == pmi_extra); } +TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) { + ScratchFile profile; + + ProfileCompilationInfo saved_info; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo(); + + // Add methods with inline caches. + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info)); + } + + ASSERT_TRUE(saved_info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Make some inline caches megamorphic and add them to the profile again. + ProfileCompilationInfo saved_info_extra; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo(); + MakeMegamorphic(&pmi_extra); + for (uint16_t method_idx = 5; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra)); + } + + // Mark all inline caches with missing types and add them to the profile again. + // This will verify that all inline caches (megamorphic or not) should be marked as missing types. + ProfileCompilationInfo::OfflineProfileMethodInfo missing_types = GetOfflineProfileMethodInfo(); + SetIsMissingTypes(&missing_types); + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra)); + } + + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(saved_info_extra.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Merge the profiles so that we have the same view as the file. + ASSERT_TRUE(saved_info.MergeWith(saved_info_extra)); + + // Check that we get back what we saved. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(loaded_info.Load(GetFd(profile))); + + ASSERT_TRUE(loaded_info.Equals(saved_info)); + + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1; + ASSERT_TRUE(loaded_info.GetMethod("dex_location1", + /* checksum */ 1, + /* method_idx */ 3, + &loaded_pmi1)); + ASSERT_TRUE(loaded_pmi1 == pmi_extra); +} + TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { ScratchFile profile; @@ -570,7 +642,7 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { } } -TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCahce) { +TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) { ScratchFile profile; ProfileCompilationInfo info; @@ -662,7 +734,7 @@ TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) { ProfileCompilationInfo::OfflineProfileMethodInfo pmi; pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); ProfileCompilationInfo::DexPcData dex_pc_data; - dex_pc_data.is_megamorphic = true; + dex_pc_data.SetIsMegamorphic(); pmi.inline_caches.Put(/*dex_pc*/ 0, dex_pc_data); ProfileCompilationInfo info_megamorphic; @@ -686,4 +758,33 @@ TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) { ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile))); } +TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) { + // Create an inline cache with missing types + ProfileCompilationInfo::OfflineProfileMethodInfo pmi; + pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.SetIsMissingTypes(); + pmi.inline_caches.Put(/*dex_pc*/ 0, dex_pc_data); + + ProfileCompilationInfo info_megamorphic; + ASSERT_TRUE(AddMethod("dex_location1", + /*checksum*/ 1, + /*method_idx*/ 0, + pmi, + &info_megamorphic)); + + // Create a profile with no inline caches (for the same method). + ProfileCompilationInfo info_no_inline_cache; + ASSERT_TRUE(AddMethod("dex_location1", + /*checksum*/ 1, + /*method_idx*/ 0, + &info_no_inline_cache)); + + // Merge the missing type cache into the empty one. + // Everything should be saved without errors. + ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic)); + ScratchFile profile; + ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile))); +} + } // namespace art diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index d34f09c721..b68eedcb11 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -206,10 +206,10 @@ class MANAGED Class FINAL : public Object { return status >= kStatusResolved || status == kStatusErrorResolved; } - // Returns true if the class was compile-time verified. + // Returns true if the class should be verified at runtime. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - bool IsCompileTimeVerified() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() >= kStatusRetryVerificationAtRuntime; + bool ShouldVerifyAtRuntime() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetStatus<kVerifyFlags>() == kStatusRetryVerificationAtRuntime; } // Returns true if the class has been verified. diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java index ad3ff448d0..2d9daf1d43 100644 --- a/test/618-checker-induction/src/Main.java +++ b/test/618-checker-induction/src/Main.java @@ -21,6 +21,8 @@ public class Main { static int[] a = new int[10]; + static int[] novec = new int[20]; // to prevent vectorization + /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before) /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none // @@ -132,16 +134,18 @@ public class Main { /// CHECK-START: void Main.deadInduction() loop_optimization (before) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // /// CHECK-START: void Main.deadInduction() loop_optimization (after) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none static void deadInduction() { int dead = 0; for (int i = 0; i < a.length; i++) { - a[i] = 1; + a[i] = novec[2 * i] + 1; dead += 5; } } @@ -151,17 +155,19 @@ public class Main { /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // /// CHECK-START: void Main.deadManyInduction() loop_optimization (after) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none static void deadManyInduction() { int dead1 = 0, dead2 = 1, dead3 = 3; for (int i = 0; i < a.length; i++) { dead1 += 5; - a[i] = 2; + a[i] = novec[2 * i] + 2; dead2 += 10; dead3 += 100; } @@ -170,16 +176,18 @@ public class Main { /// CHECK-START: void Main.deadSequence() loop_optimization (before) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // /// CHECK-START: void Main.deadSequence() loop_optimization (after) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none static void deadSequence() { int dead = 0; for (int i = 0; i < a.length; i++) { - a[i] = 3; + a[i] = novec[2 * i] + 3; // Increment value defined inside loop, // but sequence itself not used anywhere. dead += i; @@ -191,17 +199,19 @@ public class Main { /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none /// CHECK-NOT: BoundsCheck // /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (after) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none static void deadCycleWithException(int k) { int dead = 0; for (int i = 0; i < a.length; i++) { - a[i] = 4; + a[i] = novec[2 * i] + 4; // Increment value of dead cycle may throw exception. Dynamic // BCE takes care of the bounds check though, which enables // removing the ArrayGet after removing the dead cycle. diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java index 7509d9b4f3..eee90ab285 100644 --- a/test/623-checker-loop-regressions/src/Main.java +++ b/test/623-checker-loop-regressions/src/Main.java @@ -213,6 +213,8 @@ public class Main { /// CHECK-START: long Main.geoLongDivLastValue(long) instruction_simplifier$after_bce (after) /// CHECK-DAG: <<Long:j\d+>> LongConstant 0 loop:none /// CHECK-DAG: Return [<<Long>>] loop:none + // + // Tests overflow in the divisor (while updating intermediate result). static long geoLongDivLastValue(long x) { for (int i = 0; i < 10; i++) { x /= 1081788608; @@ -220,6 +222,26 @@ public class Main { return x; } + /// CHECK-START: long Main.geoLongDivLastValue() loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + // + /// CHECK-START: long Main.geoLongDivLastValue() loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: long Main.geoLongDivLastValue() instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Long:j\d+>> LongConstant 0 loop:none + /// CHECK-DAG: Return [<<Long>>] loop:none + // + // Tests overflow in the divisor (while updating base). + static long geoLongDivLastValue() { + long x = -1; + for (int i2 = 0; i2 < 2; i2++) { + x /= (Long.MAX_VALUE); + } + return x; + } + /// CHECK-START: long Main.geoLongMulLastValue(long) loop_optimization (before) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none @@ -239,6 +261,15 @@ public class Main { return x; } + // If vectorized, the narrowing subscript should not cause + // type inconsistencies in the synthesized code. + static void narrowingSubscript(float[] a) { + float val = 2.0f; + for (long i = 0; i < a.length; i++) { + a[(int) i] += val; + } + } + public static void main(String[] args) { expectEquals(10, earlyExitFirst(-1)); for (int i = 0; i <= 10; i++) { @@ -286,6 +317,8 @@ public class Main { expectEquals(0L, geoLongDivLastValue(9223372036854775807L)); expectEquals(0L, geoLongDivLastValue(-9223372036854775808L)); + expectEquals(0L, geoLongDivLastValue()); + expectEquals( 0L, geoLongMulLastValue(0L)); expectEquals(-8070450532247928832L, geoLongMulLastValue(1L)); expectEquals( 2305843009213693952L, geoLongMulLastValue(2L)); @@ -296,6 +329,12 @@ public class Main { expectEquals( 8070450532247928832L, geoLongMulLastValue(9223372036854775807L)); expectEquals( 0L, geoLongMulLastValue(-9223372036854775808L)); + float[] a = new float[16]; + narrowingSubscript(a); + for (int i = 0; i < 16; i++) { + expectEquals(2.0f, a[i]); + } + System.out.println("passed"); } @@ -310,4 +349,10 @@ public class Main { throw new Error("Expected: " + expected + ", found: " + result); } } + + private static void expectEquals(float expected, float result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } } diff --git a/test/641-checker-arraycopy/build b/test/641-checker-arraycopy/build new file mode 100644 index 0000000000..9abc61897a --- /dev/null +++ b/test/641-checker-arraycopy/build @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright 2017 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. + +# make us exit on a failure +set -e + +# Don't use jack for this test, to ensure we don't use +# the typed System.arraycopy versions directly. +export USE_JACK=false + +./default-build diff --git a/test/641-checker-arraycopy/expected.txt b/test/641-checker-arraycopy/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/641-checker-arraycopy/expected.txt diff --git a/test/641-checker-arraycopy/info.txt b/test/641-checker-arraycopy/info.txt new file mode 100644 index 0000000000..1a1111e145 --- /dev/null +++ b/test/641-checker-arraycopy/info.txt @@ -0,0 +1,2 @@ +Checker test for testing the arraycopy optimization in +instruction simplifier. diff --git a/test/641-checker-arraycopy/src/Main.java b/test/641-checker-arraycopy/src/Main.java new file mode 100644 index 0000000000..f0fcf28d23 --- /dev/null +++ b/test/641-checker-arraycopy/src/Main.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 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 Main { + + // Note that this is testing we haven't intrinsified the byte[] arraycopy version. + // Once we eventually start doing it, we will need to re-adjust this test. + + /// CHECK-START-X86: void Main.typedCopy(java.lang.Object, byte[]) disassembly (after) + /// CHECK: InvokeStaticOrDirect method_name:java.lang.System.arraycopy intrinsic:SystemArrayCopy + /// CHECK-NOT: call + /// CHECK: InvokeStaticOrDirect method_name:java.lang.System.arraycopy intrinsic:SystemArrayCopy + /// CHECK: call + /// CHECK: ReturnVoid + public static void typedCopy(Object o, byte[] foo) { + System.arraycopy(o, 1, o, 0, 1); + System.arraycopy(foo, 1, foo, 0, 1); + } + + public static void untypedCopy(Object o, Object foo) { + System.arraycopy(o, 1, o, 0, 1); + System.arraycopy(foo, 1, foo, 0, 1); + } + + // Test that we still do the optimization after inlining. + + /// CHECK-START-X86: void Main.untypedCopyCaller(java.lang.Object, byte[]) disassembly (after) + /// CHECK: InvokeStaticOrDirect method_name:java.lang.System.arraycopy intrinsic:SystemArrayCopy + /// CHECK-NOT: call + /// CHECK: InvokeStaticOrDirect method_name:java.lang.System.arraycopy intrinsic:SystemArrayCopy + /// CHECK: call + /// CHECK: ReturnVoid + public static void untypedCopyCaller(Object o, byte[] array) { + untypedCopy(o, array); + } + + public static void assertEquals(Object one, Object two) { + if (one != two) { + throw new Error("Expected " + one + ", got " + two); + } + } + + public static void main(String[] args) { + // Simple sanity checks. + byte[] a = new byte[2]; + Object[] o = new Object[2]; + + o[0] = a; + o[1] = o; + a[0] = 1; + a[1] = 2; + + untypedCopyCaller(o, a); + assertEquals(o[0], o); + assertEquals(o[1], o); + assertEquals(a[0], (byte)2); + assertEquals(a[1], (byte)2); + + o[0] = a; + o[1] = o; + a[0] = 1; + a[1] = 2; + + typedCopy(o, a); + assertEquals(o[0], o); + assertEquals(o[1], o); + assertEquals(a[0], (byte)2); + assertEquals(a[1], (byte)2); + } +} diff --git a/test/641-iterations/expected.txt b/test/641-iterations/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/641-iterations/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/641-iterations/info.txt b/test/641-iterations/info.txt new file mode 100644 index 0000000000..fd8059561c --- /dev/null +++ b/test/641-iterations/info.txt @@ -0,0 +1 @@ +Tests on varying trip counts (to validate vector/cleanup loops). diff --git a/test/641-iterations/src/Main.java b/test/641-iterations/src/Main.java new file mode 100644 index 0000000000..6a27f80286 --- /dev/null +++ b/test/641-iterations/src/Main.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 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. + */ + +/** + * Tests of varying trip counts. Focused on testing + * core and cleanup loop after vectorization. + */ +public class Main { + + static int[] sA; + + static void init() { + for (int i = 0; i < sA.length; i++) + sA[i] = 100; + } + + static void doitTo(int n) { + for (int i = 0; i < n; i++) + sA[i] += 1; + } + + static void doitFrom(int n) { + for (int i = n; i < sA.length; i++) + sA[i] += 1; + } + + static void verify(int n) { + for (int i = 0; i < n; i++) + if (sA[i] != 101) + throw new Error("failed inside loop"); + for (int i = n; i < sA.length; i++) + if (sA[i] != 100) + throw new Error("failed outside loop"); + } + + static void verify() { + for (int i = 0; i < sA.length; i++) + if (sA[i] != 101) + throw new Error("failed inside loop"); + } + + static void driver() { + for (int n = 0; n <= sA.length; n++) { + init(); + doitTo(n); + verify(n); + doitFrom(n); + verify(); + } + } + + public static void main(String[] args) { + sA = new int[17]; + driver(); + sA = new int[32]; + driver(); + System.out.println("passed"); + } +} + diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index bb0d51ec51..01eb14eda2 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -624,16 +624,18 @@ TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := # Tests failing in non-Baker read barrier configurations with the Optimizing compiler (AOT). -# 537: Expects an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet +# 537 and 641: Expect an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet # handled in non-Baker read barrier configurations. TEST_ART_BROKEN_OPTIMIZING_NON_BAKER_READ_BARRIER_RUN_TESTS := \ - 537-checker-arraycopy + 537-checker-arraycopy \ + 641-checker-arraycopy # Tests failing in non-Baker read barrier configurations with JIT (Optimizing compiler). -# 537: Expects an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet +# 537 and 641: Expect an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet # handled in non-Baker read barrier configurations. TEST_ART_BROKEN_JIT_NON_BAKER_READ_BARRIER_RUN_TESTS := \ - 537-checker-arraycopy + 537-checker-arraycopy \ + 641-checker-arraycopy ifeq ($(ART_USE_READ_BARRIER),true) ifneq (,$(filter interpreter,$(COMPILER_TYPES))) diff --git a/test/ProfileTestMultiDex/Main.java b/test/ProfileTestMultiDex/Main.java index 73fbb00d25..a8ced544c9 100644 --- a/test/ProfileTestMultiDex/Main.java +++ b/test/ProfileTestMultiDex/Main.java @@ -39,6 +39,10 @@ class TestInline { return s.getValue(); } + public int inlineMissingTypes(Super s) { + return s.getValue(); + } + public int noInlineCache(Super s) { return s.getValue(); } diff --git a/test/VerifierDeps/MySub1SoftVerificationFailure.smali b/test/VerifierDeps/MySub1SoftVerificationFailure.smali new file mode 100644 index 0000000000..8123394e87 --- /dev/null +++ b/test/VerifierDeps/MySub1SoftVerificationFailure.smali @@ -0,0 +1,16 @@ +# Copyright (C) 2017 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 LMySub1SoftVerificationFailure; +.super LMySoftVerificationFailure; diff --git a/test/VerifierDeps/MySub2SoftVerificationFailure.smali b/test/VerifierDeps/MySub2SoftVerificationFailure.smali new file mode 100644 index 0000000000..8d003236c8 --- /dev/null +++ b/test/VerifierDeps/MySub2SoftVerificationFailure.smali @@ -0,0 +1,16 @@ +# Copyright (C) 2017 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 LMySub2SoftVerificationFailure; +.super LMySoftVerificationFailure; diff --git a/test/VerifierDepsMulti/MySoftVerificationFailure.smali b/test/VerifierDepsMulti/MySoftVerificationFailure.smali new file mode 100644 index 0000000000..6b56a3b6dc --- /dev/null +++ b/test/VerifierDepsMulti/MySoftVerificationFailure.smali @@ -0,0 +1,24 @@ +# Copyright (C) 2017 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 LMySoftVerificationFailure; +.super Ljava/lang/Object; + +.method public final foo()V + .registers 1 + sget-object v0, LMySoftVerificationFailure;->error:LUnknownType; + throw v0 +.end method + +.field public static error:LUnknownType; diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index fb005984d0..1473149000 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -455,11 +455,12 @@ if [ "$RELOCATE" = "y" ]; then else FLAGS="$FLAGS -Xnorelocate" COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate" - if [ "$HOST" = "y" ]; then - # Increase ulimit to 128MB in case we are running hprof test, - # or string append test with art-debug-gc. - ulimit -S 128000 || exit 1 - fi +fi + +if [ "$HOST" = "y" ]; then + # Increase ulimit to 128MB in case we are running hprof test, + # or string append test with art-debug-gc. + ulimit -S 128000 || exit 1 fi if [ "$HOST" = "n" ]; then diff --git a/test/knownfailures.json b/test/knownfailures.json index f2f90353ac..50d70f18dc 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -297,7 +297,7 @@ "tests": ["000-nop", "134-nodex2oat-nofallback", "147-stripped-dex-fallback", - "595-profile-saving"], + "595-profile-saving"], "description": "The doesn't compile anything", "env_vars": {"ART_TEST_BISECTION": "true"}, "variant": "optimizing | regalloc_gc" @@ -322,7 +322,7 @@ }, { "tests": ["115-native-bridge", - "088-monitor-verification"], + "088-monitor-verification"], "description": "The test assume they are always compiled.", "env_vars": {"ART_TEST_BISECTION": "true"}, "variant": "optimizing | regalloc_gc" @@ -335,7 +335,8 @@ "variant": "optimizing | regalloc_gc" }, { - "test": "537-checker-arraycopy", + "tests": ["537-checker-arraycopy", + "641-checker-arraycopy"], "env_vars": {"ART_USE_READ_BARRIER": "true"}, "variant": "interpreter | optimizing | regalloc_gc | jit" } |