diff options
28 files changed, 345 insertions, 60 deletions
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 4f46d5edda..580ef72767 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -51,7 +51,7 @@ class HGraphBuilder : public ValueObject { compiler_driver_(driver), compilation_stats_(compiler_stats), block_builder_(graph, dex_file, code_item), - ssa_builder_(graph, handles), + ssa_builder_(graph, dex_compilation_unit->GetDexCache(), handles), instruction_builder_(graph, &block_builder_, &ssa_builder_, @@ -78,7 +78,7 @@ class HGraphBuilder : public ValueObject { null_dex_cache_(), compilation_stats_(nullptr), block_builder_(graph, nullptr, code_item), - ssa_builder_(graph, handles), + ssa_builder_(graph, null_dex_cache_, handles), instruction_builder_(graph, &block_builder_, &ssa_builder_, diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 77e0cbc600..d60298b954 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -411,7 +411,10 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, // Run type propagation to get the guard typed, and eventually propagate the // type of the receiver. - ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false); + ReferenceTypePropagation rtp_fixup(graph_, + outer_compilation_unit_.GetDexCache(), + handles_, + /* is_first_run */ false); rtp_fixup.Run(); MaybeRecordStat(kInlinedMonomorphicCall); @@ -532,7 +535,10 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, MaybeRecordStat(kInlinedPolymorphicCall); // Run type propagation to get the guards typed. - ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false); + ReferenceTypePropagation rtp_fixup(graph_, + outer_compilation_unit_.GetDexCache(), + handles_, + /* is_first_run */ false); rtp_fixup.Run(); return true; } @@ -709,7 +715,10 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment()); // Run type propagation to get the guard typed. - ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false); + ReferenceTypePropagation rtp_fixup(graph_, + outer_compilation_unit_.GetDexCache(), + handles_, + /* is_first_run */ false); rtp_fixup.Run(); MaybeRecordStat(kInlinedPolymorphicCall); @@ -971,7 +980,8 @@ HInstanceFieldGet* HInliner::CreateInstanceFieldGet(Handle<mirror::DexCache> dex // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537. /* dex_pc */ 0); if (iget->GetType() == Primitive::kPrimNot) { - ReferenceTypePropagation rtp(graph_, handles_, /* is_first_run */ false); + // Use the same dex_cache that we used for field lookup as the hint_dex_cache. + ReferenceTypePropagation rtp(graph_, dex_cache, handles_, /* is_first_run */ false); rtp.Visit(iget); } return iget; @@ -1319,13 +1329,19 @@ void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction, if (invoke_rti.IsStrictSupertypeOf(return_rti) || (return_rti.IsExact() && !invoke_rti.IsExact()) || !return_replacement->CanBeNull()) { - ReferenceTypePropagation(graph_, handles_, /* is_first_run */ false).Run(); + ReferenceTypePropagation(graph_, + outer_compilation_unit_.GetDexCache(), + handles_, + /* is_first_run */ false).Run(); } } } else if (return_replacement->IsInstanceOf()) { if (do_rtp) { // Inlining InstanceOf into an If may put a tighter bound on reference types. - ReferenceTypePropagation(graph_, handles_, /* is_first_run */ false).Run(); + ReferenceTypePropagation(graph_, + outer_compilation_unit_.GetDexCache(), + handles_, + /* is_first_run */ false).Run(); } } } diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index 7a1e06b951..5a0b89c90a 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -79,8 +79,15 @@ static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) void LICM::Run() { DCHECK(side_effects_.HasRun()); + // Only used during debug. - ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().size(), false, kArenaAllocLICM); + ArenaBitVector* visited = nullptr; + if (kIsDebugBuild) { + visited = new (graph_->GetArena()) ArenaBitVector(graph_->GetArena(), + graph_->GetBlocks().size(), + false, + kArenaAllocLICM); + } // Post order visit to visit inner loops before outer loops. for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { @@ -109,10 +116,12 @@ void LICM::Run() { DCHECK(inner->IsInLoop()); if (inner->GetLoopInformation() != loop_info) { // Thanks to post order visit, inner loops were already visited. - DCHECK(visited.IsBitSet(inner->GetBlockId())); + DCHECK(visited->IsBitSet(inner->GetBlockId())); continue; } - visited.SetBit(inner->GetBlockId()); + if (kIsDebugBuild) { + visited->SetBit(inner->GetBlockId()); + } if (contains_irreducible_loop) { // We cannot licm in an irreducible loop, or in a natural loop containing an diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index dc5a8fa9cb..7fc39cbadd 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -169,7 +169,7 @@ class ReferenceTypeInfo : ValueObject { return handle.GetReference() != nullptr; } - bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsValid() const { return IsValidHandle(type_handle_); } @@ -1933,7 +1933,7 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { ReferenceTypeInfo GetReferenceTypeInfo() const { DCHECK_EQ(GetType(), Primitive::kPrimNot); return ReferenceTypeInfo::CreateUnchecked(reference_type_handle_, - GetPackedFlag<kFlagReferenceTypeIsExact>());; + GetPackedFlag<kFlagReferenceTypeIsExact>()); } void AddUseAt(HInstruction* user, size_t index) { diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 95f10e0720..961e3b467c 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -23,6 +23,17 @@ namespace art { +static inline mirror::DexCache* FindDexCacheWithHint(Thread* self, + const DexFile& dex_file, + Handle<mirror::DexCache> hint_dex_cache) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (LIKELY(hint_dex_cache->GetDexFile() == &dex_file)) { + return hint_dex_cache.Get(); + } else { + return Runtime::Current()->GetClassLinker()->FindDexCache(self, dex_file); + } +} + static inline ReferenceTypeInfo::TypeHandle GetRootHandle(StackHandleScopeCollection* handles, ClassLinker::ClassRoot class_root, ReferenceTypeInfo::TypeHandle* cache) { @@ -54,10 +65,12 @@ ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetThrowabl class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { public: RTPVisitor(HGraph* graph, + Handle<mirror::DexCache> hint_dex_cache, HandleCache* handle_cache, ArenaVector<HInstruction*>* worklist, bool is_first_run) : HGraphDelegateVisitor(graph), + hint_dex_cache_(hint_dex_cache), handle_cache_(handle_cache), worklist_(worklist), is_first_run_(is_first_run) {} @@ -86,16 +99,19 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { bool is_exact); private: + Handle<mirror::DexCache> hint_dex_cache_; HandleCache* handle_cache_; ArenaVector<HInstruction*>* worklist_; const bool is_first_run_; }; ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph, + Handle<mirror::DexCache> hint_dex_cache, StackHandleScopeCollection* handles, bool is_first_run, const char* name) : HOptimization(graph, name), + hint_dex_cache_(hint_dex_cache), handle_cache_(handles), worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)), is_first_run_(is_first_run) { @@ -130,7 +146,7 @@ void ReferenceTypePropagation::ValidateTypes() { } void ReferenceTypePropagation::Visit(HInstruction* instruction) { - RTPVisitor visitor(graph_, &handle_cache_, &worklist_, is_first_run_); + RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_); instruction->Accept(&visitor); } @@ -149,7 +165,7 @@ void ReferenceTypePropagation::Run() { } void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) { - RTPVisitor visitor(graph_, &handle_cache_, &worklist_, is_first_run_); + RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_); // Handle Phis first as there might be instructions in the same block who depend on them. for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { VisitPhi(it.Current()->AsPhi()); @@ -358,7 +374,6 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) { HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass(); ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); { - ScopedObjectAccess soa(Thread::Current()); if (!class_rti.IsValid()) { // He have loaded an unresolved class. Don't bother bounding the type. return; @@ -412,7 +427,7 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst ScopedObjectAccess soa(Thread::Current()); StackHandleScope<2> hs(soa.Self()); Handle<mirror::DexCache> dex_cache( - hs.NewHandle(cl->FindDexCache(soa.Self(), invoke->GetDexFile(), false))); + hs.NewHandle(FindDexCacheWithHint(soa.Self(), invoke->GetDexFile(), hint_dex_cache_))); // Use a null loader. We should probably use the compiling method's class loader, // but then we would need to pass it to RTPVisitor just for this debug check. Since // the method is from the String class, the null loader is good enough. @@ -446,8 +461,7 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* DCHECK_EQ(instr->GetType(), Primitive::kPrimNot); ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache( - soa.Self(), dex_file, false); + mirror::DexCache* dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); // Get type from dex cache assuming it was populated by the verifier. SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact); } @@ -460,24 +474,24 @@ void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) { UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); } -static mirror::Class* GetClassFromDexCache(Thread* self, const DexFile& dex_file, uint16_t type_idx) +static mirror::Class* GetClassFromDexCache(Thread* self, + const DexFile& dex_file, + uint16_t type_idx, + Handle<mirror::DexCache> hint_dex_cache) SHARED_REQUIRES(Locks::mutator_lock_) { - mirror::DexCache* dex_cache = - Runtime::Current()->GetClassLinker()->FindDexCache(self, dex_file, /* allow_failure */ true); - if (dex_cache == nullptr) { - // Dex cache could not be found. This should only happen during gtests. - return nullptr; - } + mirror::DexCache* dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache); // Get type from dex cache assuming it was populated by the verifier. return dex_cache->GetResolvedType(type_idx); } void ReferenceTypePropagation::RTPVisitor::VisitParameterValue(HParameterValue* instr) { - ScopedObjectAccess soa(Thread::Current()); // We check if the existing type is valid: the inliner may have set it. if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) { - mirror::Class* resolved_class = - GetClassFromDexCache(soa.Self(), instr->GetDexFile(), instr->GetTypeIndex()); + ScopedObjectAccess soa(Thread::Current()); + mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(), + instr->GetDexFile(), + instr->GetTypeIndex(), + hint_dex_cache_); SetClassAsTypeInfo(instr, resolved_class, /* is_exact */ false); } } @@ -488,11 +502,11 @@ void ReferenceTypePropagation::RTPVisitor::UpdateFieldAccessTypeInfo(HInstructio return; } - ScopedObjectAccess soa(Thread::Current()); mirror::Class* klass = nullptr; // The field index is unknown only during tests. if (info.GetFieldIndex() != kUnknownFieldIndex) { + ScopedObjectAccess soa(Thread::Current()); ClassLinker* cl = Runtime::Current()->GetClassLinker(); ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), info.GetDexCache().Get()); // TODO: There are certain cases where we can't resolve the field. @@ -532,8 +546,10 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); // Get type from dex cache assuming it was populated by the verifier. - mirror::Class* resolved_class = - GetClassFromDexCache(soa.Self(), instr->GetDexFile(), instr->GetTypeIndex()); + mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(), + instr->GetDexFile(), + instr->GetTypeIndex(), + hint_dex_cache_); if (resolved_class != nullptr) { instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( handle_cache_->NewHandle(resolved_class), /* is_exact */ true)); @@ -567,7 +583,6 @@ void ReferenceTypePropagation::RTPVisitor::VisitLoadException(HLoadException* in } void ReferenceTypePropagation::RTPVisitor::VisitNullCheck(HNullCheck* instr) { - ScopedObjectAccess soa(Thread::Current()); ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo(); if (parent_rti.IsValid()) { instr->SetReferenceTypeInfo(parent_rti); @@ -575,10 +590,9 @@ void ReferenceTypePropagation::RTPVisitor::VisitNullCheck(HNullCheck* instr) { } void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) { - ScopedObjectAccess soa(Thread::Current()); - ReferenceTypeInfo class_rti = instr->GetUpperBound(); if (class_rti.IsValid()) { + ScopedObjectAccess soa(Thread::Current()); // Narrow the type as much as possible. HInstruction* obj = instr->InputAt(0); ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo(); @@ -609,8 +623,6 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) { } void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { - ScopedObjectAccess soa(Thread::Current()); - HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); HBoundType* bound_type = check_cast->GetNext()->AsBoundType(); @@ -645,7 +657,6 @@ void ReferenceTypePropagation::VisitPhi(HPhi* phi) { // point the interpreter jumps to that loop header. return; } - ScopedObjectAccess soa(Thread::Current()); // Set the initial type for the phi. Use the non back edge input for reaching // a fixed point faster. HInstruction* first_input = phi->InputAt(0); @@ -760,7 +771,8 @@ void ReferenceTypePropagation::RTPVisitor::VisitInvoke(HInvoke* instr) { ScopedObjectAccess soa(Thread::Current()); ClassLinker* cl = Runtime::Current()->GetClassLinker(); - mirror::DexCache* dex_cache = cl->FindDexCache(soa.Self(), instr->GetDexFile()); + mirror::DexCache* dex_cache = + FindDexCacheWithHint(soa.Self(), instr->GetDexFile(), hint_dex_cache_); size_t pointer_size = cl->GetImagePointerSize(); ArtMethod* method = dex_cache->GetResolvedMethod(instr->GetDexMethodIndex(), pointer_size); mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size); diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 028a6fc514..7362544e93 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -32,6 +32,7 @@ namespace art { class ReferenceTypePropagation : public HOptimization { public: ReferenceTypePropagation(HGraph* graph, + Handle<mirror::DexCache> hint_dex_cache, StackHandleScopeCollection* handles, bool is_first_run, const char* name = kReferenceTypePropagationPassName); @@ -90,6 +91,10 @@ class ReferenceTypePropagation : public HOptimization { void ValidateTypes(); + // Note: hint_dex_cache_ is usually, but not necessarily, the dex cache associated with + // graph_->GetDexFile(). Since we may look up also in other dex files, it's used only + // as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache(). + Handle<mirror::DexCache> hint_dex_cache_; HandleCache handle_cache_; ArenaVector<HInstruction*> worklist_; diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index eeadbeb0d1..e43e33f0c0 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -506,7 +506,7 @@ GraphAnalysisResult SsaBuilder::BuildSsa() { // 4) Compute type of reference type instructions. The pass assumes that // NullConstant has been fixed up. - ReferenceTypePropagation(graph_, handles_, /* is_first_run */ true).Run(); + ReferenceTypePropagation(graph_, dex_cache_, handles_, /* is_first_run */ true).Run(); // 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type // (int/float or long/double) and marked ArraySets with ambiguous input type. diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index c37c28c801..d7360adef8 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -47,8 +47,11 @@ namespace art { */ class SsaBuilder : public ValueObject { public: - SsaBuilder(HGraph* graph, StackHandleScopeCollection* handles) + SsaBuilder(HGraph* graph, + Handle<mirror::DexCache> dex_cache, + StackHandleScopeCollection* handles) : graph_(graph), + dex_cache_(dex_cache), handles_(handles), agets_fixed_(false), ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)), @@ -112,6 +115,7 @@ class SsaBuilder : public ValueObject { void RemoveRedundantUninitializedStrings(); HGraph* graph_; + Handle<mirror::DexCache> dex_cache_; StackHandleScopeCollection* const handles_; // True if types of ambiguous ArrayGets have been resolved. diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 97f2aeeb1e..719feec468 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -969,6 +969,38 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> { return false; } + bool IsLinearOrderWellFormed(const HGraph& graph) { + for (HBasicBlock* header : graph.GetBlocks()) { + if (!header->IsLoopHeader()) { + continue; + } + + HLoopInformation* loop = header->GetLoopInformation(); + size_t num_blocks = loop->GetBlocks().NumSetBits(); + size_t found_blocks = 0u; + + for (HLinearOrderIterator it(graph); !it.Done(); it.Advance()) { + HBasicBlock* current = it.Current(); + if (loop->Contains(*current)) { + found_blocks++; + if (found_blocks == 1u && current != header) { + // First block is not the header. + return false; + } else if (found_blocks == num_blocks && !loop->IsBackEdge(*current)) { + // Last block is not a back edge. + return false; + } + } else if (found_blocks != 0u && found_blocks != num_blocks) { + // Blocks are not adjacent. + return false; + } + } + DCHECK_EQ(found_blocks, num_blocks); + } + + return true; + } + void AddBackEdgeUses(const HBasicBlock& block_at_use) { DCHECK(block_at_use.IsInLoop()); // Add synthesized uses at the back edge of loops to help the register allocator. @@ -995,12 +1027,30 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> { if ((first_use_ != nullptr) && (first_use_->GetPosition() <= back_edge_use_position)) { // There was a use already seen in this loop. Therefore the previous call to `AddUse` // already inserted the backedge use. We can stop going outward. - DCHECK(HasSynthesizeUseAt(back_edge_use_position)); + if (kIsDebugBuild) { + if (!HasSynthesizeUseAt(back_edge_use_position)) { + // There exists a use prior to `back_edge_use_position` but there is + // no synthesized use at the back edge. This can happen in the presence + // of irreducible loops, when blocks of the loop are not adjacent in + // linear order, i.e. when there is an out-of-loop block between + // `block_at_use` and `back_edge_position` that uses this interval. + DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops()); + DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph())); + } + } break; } - DCHECK(last_in_new_list == nullptr - || back_edge_use_position > last_in_new_list->GetPosition()); + if (last_in_new_list != nullptr && + back_edge_use_position <= last_in_new_list->GetPosition()) { + // Loops are not properly nested in the linear order, i.e. the back edge + // of an outer loop preceeds blocks of an inner loop. This can happen + // in the presence of irreducible loops. + DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops()); + DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph())); + // We must bail out, otherwise we would generate an unsorted use list. + break; + } UsePosition* new_use = new (allocator_) UsePosition( /* user */ nullptr, diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index 4ea85a2c18..f70fe04ed1 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -306,8 +306,10 @@ class DebugFrameOpCodeWriterForAssembler FINAL // Override the last delayed PC. The new PC can be out of order. void OverrideDelayedPC(size_t pc) { DCHECK(delay_emitting_advance_pc_); - DCHECK(!delayed_advance_pcs_.empty()); - delayed_advance_pcs_.back().pc = pc; + if (enabled_) { + DCHECK(!delayed_advance_pcs_.empty()); + delayed_advance_pcs_.back().pc = pc; + } } // Return the number of delayed advance PC entries. diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 4ba90c12ce..558e4435f0 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -153,6 +153,7 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { jit->hot_method_threshold_ = options->GetCompileThreshold(); jit->warm_method_threshold_ = options->GetWarmupThreshold(); jit->osr_method_threshold_ = options->GetOsrThreshold(); + jit->priority_thread_weight_ = options->GetPriorityThreadWeight(); jit->CreateThreadPool(); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 78e372ad02..3f95772b4f 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -492,10 +492,21 @@ bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) { const ImageInfo* image_info = GetImageInfo(); if (image_info == nullptr) { VLOG(oat) << "No image for oat image checksum to match against."; - return true; - } - if (file.GetOatHeader().GetImageFileLocationOatChecksum() != GetCombinedImageChecksum()) { + if (HasOriginalDexFiles()) { + return true; + } + + // If there is no original dex file to fall back to, grudgingly accept + // the oat file. This could technically lead to crashes, but there's no + // way we could find a better oat file to use for this dex location, + // and it's better than being stuck in a boot loop with no way out. + // The problem will hopefully resolve itself the next time the runtime + // starts up. + LOG(WARNING) << "Dex location " << dex_location_ << " does not seem to include dex file. " + << "Allow oat file use. This is potentially dangerous."; + } else if (file.GetOatHeader().GetImageFileLocationOatChecksum() + != GetCombinedImageChecksum()) { VLOG(oat) << "Oat image checksum does not match image checksum."; return true; } diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 94f6345bb0..3846605400 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -364,7 +364,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // However, if the app was part of /system and preopted, there is no original dex file // available. In that case grudgingly accept the oat file. - if (!DexFile::MaybeDex(dex_location)) { + if (!oat_file_assistant.HasOriginalDexFiles()) { accept_oat_file = true; LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. " << "Allow oat file use. This is potentially dangerous."; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index d5319fdb0c..a0987b5161 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -4577,8 +4577,17 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class // of C1. For resolution to occur the declared class of the field must be compatible with // obj_type, we've discovered this wasn't so, so report the field didn't exist. - Fail(VERIFY_ERROR_NO_FIELD) << "cannot access instance field " << PrettyField(field) - << " from object of type " << obj_type; + VerifyError type; + bool is_aot = Runtime::Current()->IsAotCompiler(); + if (is_aot && (field_klass.IsUnresolvedTypes() || obj_type.IsUnresolvedTypes())) { + // Compiler & unresolved types involved, retry at runtime. + type = VerifyError::VERIFY_ERROR_NO_CLASS; + } else { + // Classes known, or at compile time. This is a hard failure. + type = VerifyError::VERIFY_ERROR_BAD_CLASS_HARD; + } + Fail(type) << "cannot access instance field " << PrettyField(field) + << " from object of type " << obj_type; return nullptr; } else { return field; diff --git a/test/147-stripped-dex-fallback/expected.txt b/test/147-stripped-dex-fallback/expected.txt new file mode 100644 index 0000000000..af5626b4a1 --- /dev/null +++ b/test/147-stripped-dex-fallback/expected.txt @@ -0,0 +1 @@ +Hello, world! diff --git a/test/147-stripped-dex-fallback/info.txt b/test/147-stripped-dex-fallback/info.txt new file mode 100644 index 0000000000..72a2ca8d4c --- /dev/null +++ b/test/147-stripped-dex-fallback/info.txt @@ -0,0 +1,2 @@ +Verify that we fallback to running out of dex code in the oat file if there is +no image and the original dex code has been stripped. diff --git a/test/147-stripped-dex-fallback/run b/test/147-stripped-dex-fallback/run new file mode 100755 index 0000000000..e594010b9e --- /dev/null +++ b/test/147-stripped-dex-fallback/run @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright (C) 2014 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. + +# ensure flags includes prebuild. +flags="$@" +if [[ "${flags}" == *--no-prebuild* ]] ; then + echo "Test 147-stripped-dex-fallback is not intended to run in no-prebuild mode." + exit 1 +fi + +${RUN} ${flags} --strip-dex --no-dex2oat diff --git a/test/147-stripped-dex-fallback/src/Main.java b/test/147-stripped-dex-fallback/src/Main.java new file mode 100644 index 0000000000..1ef6289559 --- /dev/null +++ b/test/147-stripped-dex-fallback/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2011 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 { + public static void main(String[] args) { + System.out.println("Hello, world!"); + } +} diff --git a/test/594-checker-irreducible-linorder/expected.txt b/test/594-checker-irreducible-linorder/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/594-checker-irreducible-linorder/expected.txt diff --git a/test/594-checker-irreducible-linorder/info.txt b/test/594-checker-irreducible-linorder/info.txt new file mode 100644 index 0000000000..a1783f8ec1 --- /dev/null +++ b/test/594-checker-irreducible-linorder/info.txt @@ -0,0 +1,2 @@ +Regression test for a failing DCHECK in SSA liveness analysis in the presence +of irreducible loops. diff --git a/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali new file mode 100644 index 0000000000..8e01084841 --- /dev/null +++ b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali @@ -0,0 +1,64 @@ +# Copyright (C) 2016 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 LIrreducibleLoop; +.super Ljava/lang/Object; + +# Test case where liveness analysis produces linear order where loop blocks are +# not adjacent. + +## CHECK-START: int IrreducibleLoop.liveness(boolean, boolean, boolean, int) builder (after) +## CHECK-DAG: Add loop:none +## CHECK-DAG: Mul loop:<<Loop:B\d+>> +## CHECK-DAG: Not loop:<<Loop>> + +## CHECK-START: int IrreducibleLoop.liveness(boolean, boolean, boolean, int) liveness (after) +## CHECK-DAG: Add liveness:<<LPreEntry:\d+>> +## CHECK-DAG: Mul liveness:<<LHeader:\d+>> +## CHECK-DAG: Not liveness:<<LBackEdge:\d+>> +## CHECK-EVAL: (<<LHeader>> < <<LPreEntry>>) and (<<LPreEntry>> < <<LBackEdge>>) + +.method public static liveness(ZZZI)I + .registers 10 + const/16 v0, 42 + + if-eqz p0, :header + + :pre_entry + add-int/2addr p3, p3 + invoke-static {v0}, Ljava/lang/System;->exit(I)V + goto :body1 + + :header + mul-int/2addr p3, p3 + if-eqz p1, :body2 + + :body1 + goto :body_merge + + :body2 + invoke-static {v0}, Ljava/lang/System;->exit(I)V + goto :body_merge + + :body_merge + if-eqz p2, :exit + + :back_edge + not-int p3, p3 + goto :header + + :exit + return p3 + +.end method diff --git a/test/594-checker-irreducible-linorder/src/Main.java b/test/594-checker-irreducible-linorder/src/Main.java new file mode 100644 index 0000000000..38b2ab4384 --- /dev/null +++ b/test/594-checker-irreducible-linorder/src/Main.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 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 { + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) { + // Nothing to run. This regression test merely makes sure the smali test + // case successfully compiles. + } +} diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt index c2a9a31aeb..11150c296d 100644 --- a/test/800-smali/expected.txt +++ b/test/800-smali/expected.txt @@ -66,4 +66,5 @@ b/27799205 (3) b/27799205 (4) b/27799205 (5) b/27799205 (6) +b/28187158 Done! diff --git a/test/800-smali/smali/b_28187158.smali b/test/800-smali/smali/b_28187158.smali new file mode 100644 index 0000000000..7dd2022483 --- /dev/null +++ b/test/800-smali/smali/b_28187158.smali @@ -0,0 +1,11 @@ +.class public LB28187158; + +# Regression test for iget with wrong classes. + +.super Ljava/lang/Object; + +.method public static run(Ljava/lang/Integer;)V + .registers 2 + iget v0, p0, Ljava/lang/String;->length:I +.end method + diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java index 2001cb4abb..c883b7f0f5 100644 --- a/test/800-smali/src/Main.java +++ b/test/800-smali/src/Main.java @@ -174,6 +174,8 @@ public class Main { testCases.add(new TestCase("b/27799205 (5)", "B27799205Helper", "run5", null, new VerifyError(), null)); testCases.add(new TestCase("b/27799205 (6)", "B27799205Helper", "run6", null, null, null)); + testCases.add(new TestCase("b/28187158", "B28187158", "run", new Object[] { null} , + new VerifyError(), null)); } public void runTests() { diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 1edc5993eb..b750524baa 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -235,8 +235,11 @@ ifdef dist_goal $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(ALL_ADDRESS_SIZES)) endif +# 147-stripped-dex-fallback isn't supported on device because --strip-dex +# requires the zip command. # 569-checker-pattern-replacement tests behaviour present only on host. TEST_ART_BROKEN_TARGET_TESTS := \ + 147-stripped-dex-fallback \ 569-checker-pattern-replacement ifneq (,$(filter target,$(TARGET_TYPES))) @@ -287,6 +290,7 @@ TEST_ART_BROKEN_PREBUILD_RUN_TESTS := # 529 and 555: b/27784033 TEST_ART_BROKEN_NO_PREBUILD_TESTS := \ 117-nopatchoat \ + 147-stripped-dex-fallback \ 554-jit-profile-file \ 529-checker-unresolved \ 555-checker-regression-x86const diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 28a99de099..d61fc8f8fc 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -37,6 +37,7 @@ PATCHOAT="" PREBUILD="y" QUIET="n" RELOCATE="y" +STRIP_DEX="n" SECONDARY_DEX="" TIME_OUT="gdb" # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb) # Value in seconds @@ -118,6 +119,9 @@ while true; do elif [ "x$1" = "x--prebuild" ]; then PREBUILD="y" shift + elif [ "x$1" = "x--strip-dex" ]; then + STRIP_DEX="y" + shift elif [ "x$1" = "x--host" ]; then HOST="y" ANDROID_ROOT="$ANDROID_HOST_OUT" @@ -380,6 +384,7 @@ fi dex2oat_cmdline="true" mkdir_cmdline="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA" +strip_cmdline="true" # Pick a base that will force the app image to get relocated. app_image="--base=0x4000 --app-image-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.art" @@ -409,6 +414,10 @@ if [ "$PREBUILD" = "y" ]; then fi fi +if [ "$STRIP_DEX" = "y" ]; then + strip_cmdline="zip --quiet --delete $DEX_LOCATION/$TEST_NAME.jar classes.dex" +fi + DALVIKVM_ISA_FEATURES_ARGS="" if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then DALVIKVM_ISA_FEATURES_ARGS="-Xcompiler-option --instruction-set-features=${INSTRUCTION_SET_FEATURES}" @@ -478,6 +487,7 @@ if [ "$HOST" = "n" ]; then export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \ export PATH=$ANDROID_ROOT/bin:$PATH && \ $dex2oat_cmdline && \ + $strip_cmdline && \ $dalvikvm_cmdline" cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME") @@ -548,13 +558,7 @@ else fi if [ "$DEV_MODE" = "y" ]; then - if [ "$PREBUILD" = "y" ]; then - echo "$mkdir_cmdline && $dex2oat_cmdline && $cmdline" - elif [ "$RELOCATE" = "y" ]; then - echo "$mkdir_cmdline && $cmdline" - else - echo $cmdline - fi + echo "$mkdir_cmdline && $dex2oat_cmdline && $strip_cmdline && $cmdline" fi cd $ANDROID_BUILD_TOP @@ -562,6 +566,7 @@ else rm -rf ${DEX_LOCATION}/dalvik-cache/ $mkdir_cmdline || exit 1 $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; } + $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; } # For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use # the same defaults as for prebuilt: everything when --dev, otherwise errors and above only. diff --git a/test/run-test b/test/run-test index 013fc63e83..fc57d0914f 100755 --- a/test/run-test +++ b/test/run-test @@ -190,6 +190,9 @@ while true; do run_args="${run_args} --prebuild" prebuild_mode="yes" shift; + elif [ "x$1" = "x--strip-dex" ]; then + run_args="${run_args} --strip-dex" + shift; elif [ "x$1" = "x--debuggable" ]; then run_args="${run_args} -Xcompiler-option --debuggable" debuggable="yes" @@ -449,7 +452,7 @@ if [ "$runtime" = "dalvik" ]; then if [ "$target_mode" = "no" ]; then framework="${ANDROID_PRODUCT_OUT}/system/framework" bpath="${framework}/core-libart.jar:${framework}/core-oj.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/bouncycastle.jar:${framework}/ext.jar" - run_args="${run_args} --boot -Xbootclasspath:${bpath}" + run_args="${run_args} --boot --runtime-option -Xbootclasspath:${bpath}" else true # defaults to using target BOOTCLASSPATH fi @@ -571,6 +574,7 @@ if [ "$usage" = "yes" ]; then echo " --prebuild Run dex2oat on the files before starting test. (default)" echo " --no-prebuild Do not run dex2oat on the files before starting" echo " the test." + echo " --strip-dex Strip the dex files before starting test." echo " --relocate Force the use of relocating in the test, making" echo " the image and oat files be relocated to a random" echo " address before running. (default)" |