diff options
Diffstat (limited to 'compiler/optimizing')
| -rw-r--r-- | compiler/optimizing/builder.h | 15 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.cc | 2 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.cc | 118 | ||||
| -rw-r--r-- | compiler/optimizing/instruction_builder.cc | 67 | ||||
| -rw-r--r-- | compiler/optimizing/instruction_builder.h | 11 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_arm64.cc | 162 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.cc | 69 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 28 | ||||
| -rw-r--r-- | compiler/optimizing/reference_type_propagation.cc | 44 | ||||
| -rw-r--r-- | compiler/optimizing/reference_type_propagation.h | 3 | ||||
| -rw-r--r-- | compiler/optimizing/reference_type_propagation_test.cc | 1 | ||||
| -rw-r--r-- | compiler/optimizing/sharpening.cc | 6 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_builder.cc | 6 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_builder.h | 3 | ||||
| -rw-r--r-- | compiler/optimizing/stack_map_stream.cc | 225 | ||||
| -rw-r--r-- | compiler/optimizing/stack_map_stream.h | 52 | ||||
| -rw-r--r-- | compiler/optimizing/stack_map_test.cc | 94 |
17 files changed, 544 insertions, 362 deletions
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 3a4c9dbd16..e4ad4222fb 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -54,10 +54,7 @@ class HGraphBuilder : public ValueObject { compiler_driver_(driver), compilation_stats_(compiler_stats), block_builder_(graph, dex_file, code_item), - ssa_builder_(graph, - dex_compilation_unit->GetClassLoader(), - dex_compilation_unit->GetDexCache(), - handles), + ssa_builder_(graph, dex_compilation_unit->GetDexCache(), handles), instruction_builder_(graph, &block_builder_, &ssa_builder_, @@ -83,12 +80,10 @@ class HGraphBuilder : public ValueObject { code_item_(code_item), dex_compilation_unit_(nullptr), compiler_driver_(nullptr), + null_dex_cache_(), compilation_stats_(nullptr), block_builder_(graph, nullptr, code_item), - ssa_builder_(graph, - handles->NewHandle<mirror::ClassLoader>(nullptr), - handles->NewHandle<mirror::DexCache>(nullptr), - handles), + ssa_builder_(graph, null_dex_cache_, handles), instruction_builder_(graph, &block_builder_, &ssa_builder_, @@ -101,7 +96,7 @@ class HGraphBuilder : public ValueObject { /* code_generator */ nullptr, /* interpreter_metadata */ nullptr, /* compiler_stats */ nullptr, - handles->NewHandle<mirror::DexCache>(nullptr), + null_dex_cache_, handles) {} GraphAnalysisResult BuildGraph(); @@ -122,6 +117,8 @@ class HGraphBuilder : public ValueObject { CompilerDriver* const compiler_driver_; + ScopedNullHandle<mirror::DexCache> null_dex_cache_; + OptimizingCompilerStats* compilation_stats_; HBasicBlockBuilder block_builder_; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index d68aa51b1b..bac16cd5df 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1419,7 +1419,7 @@ void CodeGenerator::EmitJitRoots(uint8_t* code, QuickEntrypointEnum CodeGenerator::GetArrayAllocationEntrypoint(Handle<mirror::Class> array_klass) { ScopedObjectAccess soa(Thread::Current()); - if (array_klass.Get() == nullptr) { + if (array_klass == nullptr) { // This can only happen for non-primitive arrays, as primitive arrays can always // be resolved. return kQuickAllocArrayResolved32; diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index b56ef0f866..e012a4287f 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -69,38 +69,32 @@ void HInliner::Run() { // doing some logic in the runtime to discover if a method could have been inlined. return; } - const ArenaVector<HBasicBlock*>& blocks = graph_->GetReversePostOrder(); + // Keep a copy of all blocks when starting the visit. + ArenaVector<HBasicBlock*> blocks = graph_->GetReversePostOrder(); DCHECK(!blocks.empty()); - HBasicBlock* next_block = blocks[0]; - for (size_t i = 0; i < blocks.size(); ++i) { - // Because we are changing the graph when inlining, we need to remember the next block. - // This avoids doing the inlining work again on the inlined blocks. - if (blocks[i] != next_block) { - continue; - } - HBasicBlock* block = next_block; - next_block = (i == blocks.size() - 1) ? nullptr : blocks[i + 1]; + // Because we are changing the graph when inlining, + // we just iterate over the blocks of the outer method. + // This avoids doing the inlining work again on the inlined blocks. + for (HBasicBlock* block : blocks) { for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) { HInstruction* next = instruction->GetNext(); HInvoke* call = instruction->AsInvoke(); // As long as the call is not intrinsified, it is worth trying to inline. if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) { - // We use the original invoke type to ensure the resolution of the called method - // works properly. - if (!TryInline(call)) { - if (kIsDebugBuild && IsCompilingWithCoreImage()) { - std::string callee_name = - outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex()); - bool should_inline = callee_name.find("$inline$") != std::string::npos; - CHECK(!should_inline) << "Could not inline " << callee_name; + if (kIsDebugBuild && IsCompilingWithCoreImage()) { + // Debugging case: directives in method names control or assert on inlining. + std::string callee_name = outer_compilation_unit_.GetDexFile()->PrettyMethod( + call->GetDexMethodIndex(), /* with_signature */ false); + // Tests prevent inlining by having $noinline$ in their method names. + if (callee_name.find("$noinline$") == std::string::npos) { + if (!TryInline(call)) { + bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos); + CHECK(!should_have_inlined) << "Could not inline " << callee_name; + } } } else { - if (kIsDebugBuild && IsCompilingWithCoreImage()) { - std::string callee_name = - outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex()); - bool must_not_inline = callee_name.find("$noinline$") != std::string::npos; - CHECK(!must_not_inline) << "Should not have inlined " << callee_name; - } + // Normal case: try to inline. + TryInline(call); } } instruction = next; @@ -198,9 +192,9 @@ static uint32_t FindMethodIndexIn(ArtMethod* method, } static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, - const DexCompilationUnit& compilation_unit) + const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = *compilation_unit.GetDexFile(); dex::TypeIndex index; if (cls->GetDexCache() == nullptr) { DCHECK(cls->IsArrayClass()) << cls->PrettyClass(); @@ -209,19 +203,22 @@ static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, DCHECK(cls->IsProxyClass()) << cls->PrettyClass(); // TODO: deal with proxy classes. } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) { - DCHECK_EQ(cls->GetDexCache(), compilation_unit.GetDexCache().Get()); + DCHECK_EQ(cls->GetDexCache(), dex_cache.Get()); index = cls->GetDexTypeIndex(); + // Update the dex cache to ensure the class is in. The generated code will + // consider it is. We make it safe by updating the dex cache, as other + // dex files might also load the class, and there is no guarantee the dex + // cache of the dex file of the class will be updated. + if (dex_cache->GetResolvedType(index) == nullptr) { + dex_cache->SetResolvedType(index, cls); + } } else { index = cls->FindTypeIndexInOtherDexFile(dex_file); - // We cannot guarantee the entry will resolve to the same class, + // We cannot guarantee the entry in the dex cache will resolve to the same class, // as there may be different class loaders. So only return the index if it's - // the right class already resolved with the class loader. - if (index.IsValid()) { - ObjPtr<mirror::Class> resolved = ClassLinker::LookupResolvedType( - index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); - if (resolved != cls) { - index = dex::TypeIndex::Invalid(); - } + // the right class in the dex cache already. + if (index.IsValid() && dex_cache->GetResolvedType(index) != cls) { + index = dex::TypeIndex::Invalid(); } } @@ -377,7 +374,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { soa.Self(), class_linker->GetClassRoot(ClassLinker::kClassArrayClass), InlineCache::kIndividualCacheSize)); - if (inline_cache.Get() == nullptr) { + if (inline_cache == nullptr) { // We got an OOME. Just clear the exception, and don't inline. DCHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); @@ -448,8 +445,9 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface()) << invoke_instruction->DebugName(); + const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); dex::TypeIndex class_index = FindClassIndexIn( - GetMonomorphicType(classes), caller_compilation_unit_); + GetMonomorphicType(classes), caller_dex_file, caller_compilation_unit_.GetDexCache()); if (!class_index.IsValid()) { VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method) << " from inline cache is not inlined because its class is not" @@ -492,7 +490,6 @@ 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_, - outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false); @@ -563,7 +560,6 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, bb_cursor->InsertInstructionAfter(load_class, receiver_class); load_class->SetLoadKind(kind); - // TODO: Extend reference type propagation to understand the guard. HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class); bb_cursor->InsertInstructionAfter(compare, load_class); if (with_deoptimization) { @@ -587,6 +583,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); PointerSize pointer_size = class_linker->GetImagePointerSize(); + const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); bool all_targets_inlined = true; bool one_target_inlined = false; @@ -608,7 +605,8 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - dex::TypeIndex class_index = FindClassIndexIn(handle.Get(), caller_compilation_unit_); + dex::TypeIndex class_index = FindClassIndexIn( + handle.Get(), caller_dex_file, caller_compilation_unit_.GetDexCache()); HInstruction* return_replacement = nullptr; if (!class_index.IsValid() || !TryBuildAndInline(invoke_instruction, @@ -664,7 +662,6 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, // Run type propagation to get the guards typed. ReferenceTypePropagation rtp_fixup(graph_, - outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false); @@ -846,7 +843,6 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget( if (outermost_graph_->IsCompilingOsr()) { CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction); } else { - // TODO: Extend reference type propagation to understand the guard. HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize( compare, invoke_instruction->GetDexPc()); bb_cursor->InsertInstructionAfter(deoptimize, compare); @@ -859,7 +855,6 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget( // Run type propagation to get the guard typed. ReferenceTypePropagation rtp_fixup(graph_, - outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false); @@ -928,7 +923,6 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, // Actual return value has a more specific type than the method's declared // return type. Run RTP again on the outer graph to propagate it. ReferenceTypePropagation(graph_, - outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false).Run(); @@ -1181,11 +1175,7 @@ HInstanceFieldGet* HInliner::CreateInstanceFieldGet(Handle<mirror::DexCache> dex /* dex_pc */ 0); if (iget->GetType() == Primitive::kPrimNot) { // Use the same dex_cache that we used for field lookup as the hint_dex_cache. - ReferenceTypePropagation rtp(graph_, - outer_compilation_unit_.GetClassLoader(), - dex_cache, - handles_, - /* is_first_run */ false); + ReferenceTypePropagation rtp(graph_, dex_cache, handles_, /* is_first_run */ false); rtp.Visit(iget); } return iget; @@ -1231,7 +1221,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, resolved_method->GetDeclaringClass()->GetClassLoader())); DexCompilationUnit dex_compilation_unit( - class_loader, + class_loader.ToJObject(), class_linker, callee_dex_file, code_item, @@ -1348,7 +1338,6 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, // are more specific than the declared ones, run RTP again on the inner graph. if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) { ReferenceTypePropagation(callee_graph, - outer_compilation_unit_.GetClassLoader(), dex_compilation_unit.GetDexCache(), handles_, /* is_first_run */ false).Run(); @@ -1359,9 +1348,6 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, RunOptimizations(callee_graph, code_item, dex_compilation_unit); number_of_instructions_budget += number_of_inlined_instructions; - // TODO: We should abort only if all predecessors throw. However, - // HGraph::InlineInto currently does not handle an exit block with - // a throw predecessor. HBasicBlock* exit_block = callee_graph->GetExitBlock(); if (exit_block == nullptr) { VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) @@ -1369,16 +1355,30 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, return false; } - bool has_throw_predecessor = false; + bool has_one_return = false; for (HBasicBlock* predecessor : exit_block->GetPredecessors()) { if (predecessor->GetLastInstruction()->IsThrow()) { - has_throw_predecessor = true; - break; + if (invoke_instruction->GetBlock()->IsTryBlock()) { + // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto. + VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) + << " could not be inlined because one branch always throws and" + << " caller is in a try/catch block"; + return false; + } else if (graph_->GetExitBlock() == nullptr) { + // TODO(ngeoffray): Support adding HExit in the caller graph. + VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) + << " could not be inlined because one branch always throws and" + << " caller does not have an exit block"; + return false; + } + } else { + has_one_return = true; } } - if (has_throw_predecessor) { + + if (!has_one_return) { VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) - << " could not be inlined because one branch always throws"; + << " could not be inlined because it always throws"; return false; } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 3aaf2ca102..3374e42955 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -669,17 +669,18 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) { ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass())); // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache. Handle<mirror::Class> methods_class(hs.NewHandle(class_linker->ResolveReferencedClassOfMethod( method_idx, dex_compilation_unit_->GetDexCache(), class_loader))); - if (UNLIKELY(methods_class.Get() == nullptr)) { + if (UNLIKELY(methods_class == nullptr)) { // Clean up any exception left by type resolution. soa.Self()->ClearException(); return nullptr; @@ -701,7 +702,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in // Check access. The class linker has a fast path for looking into the dex cache // and does not check the access if it hits it. - if (compiling_class.Get() == nullptr) { + if (compiling_class == nullptr) { if (!resolved_method->IsPublic()) { return nullptr; } @@ -717,7 +718,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of // which require runtime handling. if (invoke_type == kSuper) { - if (compiling_class.Get() == nullptr) { + if (compiling_class == nullptr) { // We could not determine the method's class we need to wait until runtime. DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; @@ -953,7 +954,7 @@ bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t d } // Consider classes we haven't resolved as potentially finalizable. - bool finalizable = (klass.Get() == nullptr) || klass->IsFinalizable(); + bool finalizable = (klass == nullptr) || klass->IsFinalizable(); AppendInstruction(new (arena_) HNewInstance( cls, @@ -971,7 +972,7 @@ static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class) } bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const { - if (cls.Get() == nullptr) { + if (cls == nullptr) { return false; } @@ -1259,7 +1260,9 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio static mirror::Class* GetClassFrom(CompilerDriver* driver, const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); - Handle<mirror::ClassLoader> class_loader = compilation_unit.GetClassLoader(); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(compilation_unit.GetClassLoader()))); Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache(); return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit); @@ -1275,9 +1278,10 @@ mirror::Class* HInstructionBuilder::GetCompilingClass() const { bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) const { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass( soa, dex_cache, class_loader, type_index, dex_compilation_unit_))); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); @@ -1288,7 +1292,7 @@ bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) c // When this happens we cannot establish a direct relation between the current // class and the outer class, so we return false. // (Note that this is only used for optimizing invokes and field accesses) - return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get()); + return (cls != nullptr) && (outer_class.Get() == cls.Get()); } void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction, @@ -1313,7 +1317,8 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, StackHandleScope<2> hs(soa.Self()); ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass())); ArtField* resolved_field = class_linker->ResolveField(*dex_compilation_unit_->GetDexFile(), @@ -1335,7 +1340,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, } // Check access. - if (compiling_class.Get() == nullptr) { + if (compiling_class == nullptr) { if (!resolved_field->IsPublic()) { return nullptr; } @@ -1607,7 +1612,7 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object, static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { - if (cls.Get() == nullptr) { + if (cls == nullptr) { return TypeCheckKind::kUnresolvedCheck; } else if (cls->IsInterface()) { return TypeCheckKind::kInterfaceCheck; @@ -1630,13 +1635,15 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass( soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); bool needs_access_check = true; - if (klass.Get() != nullptr) { + if (klass != nullptr) { if (klass->IsPublic()) { needs_access_check = false; } else { @@ -1672,7 +1679,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, type_index, *actual_dex_file, klass, - klass.Get() != nullptr && (klass.Get() == GetOutermostCompilingClass()), + klass != nullptr && (klass.Get() == GetOutermostCompilingClass()), dex_pc, needs_access_check); @@ -1715,9 +1722,17 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, } } -bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const { +bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, + Handle<mirror::DexCache> dex_cache, + bool* finalizable) const { return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks( - LookupReferrerClass(), LookupResolvedType(type_index, *dex_compilation_unit_), finalizable); + dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable); +} + +bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const { + ScopedObjectAccess soa(Thread::Current()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); + return NeedsAccessCheck(type_index, dex_cache, finalizable); } bool HInstructionBuilder::CanDecodeQuickenedInfo() const { @@ -2757,18 +2772,4 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, return true; } // NOLINT(readability/fn_size) -ObjPtr<mirror::Class> HInstructionBuilder::LookupResolvedType( - dex::TypeIndex type_index, - const DexCompilationUnit& compilation_unit) const { - return ClassLinker::LookupResolvedType( - type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); -} - -ObjPtr<mirror::Class> HInstructionBuilder::LookupReferrerClass() const { - // TODO: Cache the result in a Handle<mirror::Class>. - const DexFile::MethodId& method_id = - dex_compilation_unit_->GetDexFile()->GetMethodId(dex_compilation_unit_->GetDexMethodIndex()); - return LookupResolvedType(method_id.class_idx_, *dex_compilation_unit_); -} - } // namespace art diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index e735a0c46d..3bb680ce44 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -106,8 +106,11 @@ class HInstructionBuilder : public ValueObject { // Returns whether the current method needs access check for the type. // Output parameter finalizable is set to whether the type is finalizable. - bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const + bool NeedsAccessCheck(dex::TypeIndex type_index, + Handle<mirror::DexCache> dex_cache, + /*out*/bool* finalizable) const REQUIRES_SHARED(Locks::mutator_lock_); + bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const; template<typename T> void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc); @@ -297,12 +300,6 @@ class HInstructionBuilder : public ValueObject { // be found. ArtField* ResolveField(uint16_t field_idx, bool is_static, bool is_put); - ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_index, - const DexCompilationUnit& compilation_unit) const - REQUIRES_SHARED(Locks::mutator_lock_); - - ObjPtr<mirror::Class> LookupReferrerClass() const REQUIRES_SHARED(Locks::mutator_lock_); - ArenaAllocator* const arena_; HGraph* const graph_; VariableSizedHandleScope* handles_; diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 1047d3beb6..86e54294ae 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -23,7 +23,7 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "intrinsics.h" #include "mirror/array-inl.h" -#include "mirror/string.h" +#include "mirror/string-inl.h" #include "thread.h" #include "utils/arm64/assembler_arm64.h" @@ -1450,16 +1450,47 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { } } +// The cut off for unrolling the loop in String.equals() intrinsic for const strings. +// The normal loop plus the pre-header is 9 instructions without string compression and 12 +// instructions with string compression. We can compare up to 8 bytes in 4 instructions +// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up +// to 10 instructions for the unrolled loop. +constexpr size_t kShortConstStringEqualsCutoffInBytes = 32; + +static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) { + if (candidate->IsLoadString()) { + HLoadString* load_string = candidate->AsLoadString(); + const DexFile& dex_file = load_string->GetDexFile(); + return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length); + } + return nullptr; +} + void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - // Temporary registers to store lengths of strings and for calculations. - locations->AddTemp(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); + // For the generic implementation and for long const strings we need a temporary. + // We do not need it for short const strings, up to 8 bytes, see code generation below. + uint32_t const_string_length = 0u; + const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length); + if (const_string == nullptr) { + const_string = GetConstString(invoke->InputAt(1), &const_string_length); + } + bool is_compressed = + mirror::kUseStringCompression && + const_string != nullptr && + mirror::String::DexFileStringAllASCII(const_string, const_string_length); + if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) { + locations->AddTemp(Location::RequiresRegister()); + } + + // TODO: If the String.equals() is used only for an immediately following HIf, we can + // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks. + // Then we shall need an extra temporary register instead of the output register. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } @@ -1473,8 +1504,7 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) { UseScratchRegisterScope scratch_scope(masm); Register temp = scratch_scope.AcquireW(); - Register temp1 = WRegisterFrom(locations->GetTemp(0)); - Register temp2 = WRegisterFrom(locations->GetTemp(1)); + Register temp1 = scratch_scope.AcquireW(); vixl::aarch64::Label loop; vixl::aarch64::Label end; @@ -1510,46 +1540,98 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) { __ B(&return_false, ne); } - // Load `count` fields of this and argument strings. - __ Ldr(temp, MemOperand(str.X(), count_offset)); - __ Ldr(temp1, MemOperand(arg.X(), count_offset)); - // Check if `count` fields are equal, return false if they're not. - // Also compares the compression style, if differs return false. - __ Cmp(temp, temp1); - __ B(&return_false, ne); - // Return true if both strings are empty. Even with string compression `count == 0` means empty. - static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, - "Expecting 0=compressed, 1=uncompressed"); - __ Cbz(temp, &return_true); + // Check if one of the inputs is a const string. Do not special-case both strings + // being const, such cases should be handled by constant folding if needed. + uint32_t const_string_length = 0u; + const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length); + if (const_string == nullptr) { + const_string = GetConstString(invoke->InputAt(1), &const_string_length); + if (const_string != nullptr) { + std::swap(str, arg); // Make sure the const string is in `str`. + } + } + bool is_compressed = + mirror::kUseStringCompression && + const_string != nullptr && + mirror::String::DexFileStringAllASCII(const_string, const_string_length); + + if (const_string != nullptr) { + // Load `count` field of the argument string and check if it matches the const string. + // Also compares the compression style, if differs return false. + __ Ldr(temp, MemOperand(arg.X(), count_offset)); + __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed))); + __ B(&return_false, ne); + } else { + // Load `count` fields of this and argument strings. + __ Ldr(temp, MemOperand(str.X(), count_offset)); + __ Ldr(temp1, MemOperand(arg.X(), count_offset)); + // Check if `count` fields are equal, return false if they're not. + // Also compares the compression style, if differs return false. + __ Cmp(temp, temp1); + __ B(&return_false, ne); + } // Assertions that must hold in order to compare strings 8 bytes at a time. DCHECK_ALIGNED(value_offset, 8); static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded"); - if (mirror::kUseStringCompression) { - // For string compression, calculate the number of bytes to compare (not chars). - // This could in theory exceed INT32_MAX, so treat temp as unsigned. - __ Lsr(temp, temp, 1u); // Extract length. - __ And(temp1, temp1, Operand(1)); // Extract compression flag. - __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare. - } - - // Store offset of string value in preparation for comparison loop - __ Mov(temp1, value_offset); + if (const_string != nullptr && + const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes + : kShortConstStringEqualsCutoffInBytes / 2u)) { + // Load and compare the contents. Though we know the contents of the short const string + // at compile time, materializing constants may be more code than loading from memory. + int32_t offset = value_offset; + size_t remaining_bytes = + RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u); + temp = temp.X(); + temp1 = temp1.X(); + while (remaining_bytes > 8u) { + Register temp2 = XRegisterFrom(locations->GetTemp(0)); + __ Ldp(temp, temp1, MemOperand(str.X(), offset)); + __ Ldp(temp2, out, MemOperand(arg.X(), offset)); + __ Cmp(temp, temp2); + __ Ccmp(temp1, out, NoFlag, eq); + __ B(&return_false, ne); + offset += 2u * sizeof(uint64_t); + remaining_bytes -= 2u * sizeof(uint64_t); + } + if (remaining_bytes != 0u) { + __ Ldr(temp, MemOperand(str.X(), offset)); + __ Ldr(temp1, MemOperand(arg.X(), offset)); + __ Cmp(temp, temp1); + __ B(&return_false, ne); + } + } else { + // Return true if both strings are empty. Even with string compression `count == 0` means empty. + static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, + "Expecting 0=compressed, 1=uncompressed"); + __ Cbz(temp, &return_true); + + if (mirror::kUseStringCompression) { + // For string compression, calculate the number of bytes to compare (not chars). + // This could in theory exceed INT32_MAX, so treat temp as unsigned. + __ And(temp1, temp, Operand(1)); // Extract compression flag. + __ Lsr(temp, temp, 1u); // Extract length. + __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare. + } - temp1 = temp1.X(); - temp2 = temp2.X(); - // Loop to compare strings 8 bytes at a time starting at the front of the string. - // Ok to do this because strings are zero-padded to kObjectAlignment. - __ Bind(&loop); - __ Ldr(out, MemOperand(str.X(), temp1)); - __ Ldr(temp2, MemOperand(arg.X(), temp1)); - __ Add(temp1, temp1, Operand(sizeof(uint64_t))); - __ Cmp(out, temp2); - __ B(&return_false, ne); - // With string compression, we have compared 8 bytes, otherwise 4 chars. - __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags); - __ B(&loop, hi); + // Store offset of string value in preparation for comparison loop + __ Mov(temp1, value_offset); + + temp1 = temp1.X(); + Register temp2 = XRegisterFrom(locations->GetTemp(0)); + // Loop to compare strings 8 bytes at a time starting at the front of the string. + // Ok to do this because strings are zero-padded to kObjectAlignment. + __ Bind(&loop); + __ Ldr(out, MemOperand(str.X(), temp1)); + __ Ldr(temp2, MemOperand(arg.X(), temp1)); + __ Add(temp1, temp1, Operand(sizeof(uint64_t))); + __ Cmp(out, temp2); + __ B(&return_false, ne); + // With string compression, we have compared 8 bytes, otherwise 4 chars. + __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags); + __ B(&loop, hi); + } // Return true and exit the function. // If loop does not result in returning false, we return true. diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index abbb91a1a9..71a26ebe79 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2038,6 +2038,8 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { HInstruction* return_value = nullptr; if (GetBlocks().size() == 3) { + // Inliner already made sure we don't inline methods that always throw. + DCHECK(!GetBlocks()[1]->GetLastInstruction()->IsThrow()); // Simple case of an entry block, a body block, and an exit block. // Put the body block's instruction into `invoke`'s block. HBasicBlock* body = GetBlocks()[1]; @@ -2119,33 +2121,60 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { UpdateLoopAndTryInformationOfNewBlock(to, at, /* replace_if_back_edge */ true); // Update all predecessors of the exit block (now the `to` block) - // to not `HReturn` but `HGoto` instead. - bool returns_void = to->GetPredecessors()[0]->GetLastInstruction()->IsReturnVoid(); - if (to->GetPredecessors().size() == 1) { - HBasicBlock* predecessor = to->GetPredecessors()[0]; + // to not `HReturn` but `HGoto` instead. Special case throwing blocks + // to now get the outer graph exit block as successor. Note that the inliner + // currently doesn't support inlining methods with try/catch. + HPhi* return_value_phi = nullptr; + bool rerun_dominance = false; + bool rerun_loop_analysis = false; + for (size_t pred = 0; pred < to->GetPredecessors().size(); ++pred) { + HBasicBlock* predecessor = to->GetPredecessors()[pred]; HInstruction* last = predecessor->GetLastInstruction(); - if (!returns_void) { - return_value = last->InputAt(0); - } - predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); - predecessor->RemoveInstruction(last); - } else { - if (!returns_void) { - // There will be multiple returns. - return_value = new (allocator) HPhi( - allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc()); - to->AddPhi(return_value->AsPhi()); - } - for (HBasicBlock* predecessor : to->GetPredecessors()) { - HInstruction* last = predecessor->GetLastInstruction(); - if (!returns_void) { + if (last->IsThrow()) { + DCHECK(!at->IsTryBlock()); + predecessor->ReplaceSuccessor(to, outer_graph->GetExitBlock()); + --pred; + // We need to re-run dominance information, as the exit block now has + // a new dominator. + rerun_dominance = true; + if (predecessor->GetLoopInformation() != nullptr) { + // The exit block and blocks post dominated by the exit block do not belong + // to any loop. Because we do not compute the post dominators, we need to re-run + // loop analysis to get the loop information correct. + rerun_loop_analysis = true; + } + } else { + if (last->IsReturnVoid()) { + DCHECK(return_value == nullptr); + DCHECK(return_value_phi == nullptr); + } else { DCHECK(last->IsReturn()); - return_value->AsPhi()->AddInput(last->InputAt(0)); + if (return_value_phi != nullptr) { + return_value_phi->AddInput(last->InputAt(0)); + } else if (return_value == nullptr) { + return_value = last->InputAt(0); + } else { + // There will be multiple returns. + return_value_phi = new (allocator) HPhi( + allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc()); + to->AddPhi(return_value_phi); + return_value_phi->AddInput(return_value); + return_value_phi->AddInput(last->InputAt(0)); + return_value = return_value_phi; + } } predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); predecessor->RemoveInstruction(last); } } + if (rerun_loop_analysis) { + outer_graph->ClearLoopInformation(); + outer_graph->ClearDominanceInformation(); + outer_graph->BuildDominatorTree(); + } else if (rerun_dominance) { + outer_graph->ClearDominanceInformation(); + outer_graph->ComputeDominanceInformation(); + } } // Walk over the entry block and: diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 0375c66e42..8638e346fb 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -306,7 +306,7 @@ class OptimizingCompiler FINAL : public Compiler { InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const OVERRIDE; @@ -375,7 +375,7 @@ class OptimizingCompiler FINAL : public Compiler { InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, ArtMethod* method, @@ -875,7 +875,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, ArtMethod* method, @@ -946,8 +946,11 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, const uint8_t* interpreter_metadata = nullptr; if (method == nullptr) { ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(class_loader))); method = compiler_driver->ResolveMethod( - soa, dex_cache, class_loader, &dex_compilation_unit, method_idx, invoke_type); + soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type); } // For AOT compilation, we may not get a method, for example if its class is erroneous. // JIT should always have a method. @@ -956,6 +959,16 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, graph->SetArtMethod(method); ScopedObjectAccess soa(Thread::Current()); interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize()); + dex::TypeIndex type_index = method->GetDeclaringClass()->GetDexTypeIndex(); + + // Update the dex cache if the type is not in it yet. Note that under AOT, + // the verifier must have set it, but under JIT, there's no guarantee, as we + // don't necessarily run the verifier. + // The compiler and the compiler driver assume the compiling class is + // in the dex cache. + if (dex_cache->GetResolvedType(type_index) == nullptr) { + dex_cache->SetResolvedType(type_index, method->GetDeclaringClass()); + } } std::unique_ptr<CodeGenerator> codegen( @@ -1036,7 +1049,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> jclass_loader, + jobject jclass_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const { CompilerDriver* compiler_driver = GetCompilerDriver(); @@ -1150,6 +1163,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); DCHECK(method->IsCompilable()); + jobject jclass_loader = class_loader.ToJObject(); const DexFile* dex_file = method->GetDexFile(); const uint16_t class_def_idx = method->GetClassDefIndex(); const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); @@ -1173,7 +1187,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, invoke_type, class_def_idx, method_idx, - class_loader, + jclass_loader, *dex_file, dex_cache, method, @@ -1200,7 +1214,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Handle<mirror::ObjectArray<mirror::Object>> roots( hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc( self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots))); - if (roots.Get() == nullptr) { + if (roots == nullptr) { // Out of memory, just clear the exception to avoid any Java exception uncaught problems. DCHECK(self->IsExceptionPending()); self->ClearException(); diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 6e332ca59b..c55fccc7d3 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -65,13 +65,11 @@ ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetThrowabl class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { public: RTPVisitor(HGraph* graph, - Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> hint_dex_cache, HandleCache* handle_cache, ArenaVector<HInstruction*>* worklist, bool is_first_run) : HGraphDelegateVisitor(graph), - class_loader_(class_loader), hint_dex_cache_(hint_dex_cache), handle_cache_(handle_cache), worklist_(worklist), @@ -103,7 +101,6 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { bool is_exact); private: - Handle<mirror::ClassLoader> class_loader_; Handle<mirror::DexCache> hint_dex_cache_; HandleCache* handle_cache_; ArenaVector<HInstruction*>* worklist_; @@ -111,13 +108,11 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { }; ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph, - Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> hint_dex_cache, VariableSizedHandleScope* handles, bool is_first_run, const char* name) : HOptimization(graph, name), - class_loader_(class_loader), hint_dex_cache_(hint_dex_cache), handle_cache_(handles), worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)), @@ -152,12 +147,7 @@ void ReferenceTypePropagation::ValidateTypes() { } void ReferenceTypePropagation::Visit(HInstruction* instruction) { - RTPVisitor visitor(graph_, - class_loader_, - hint_dex_cache_, - &handle_cache_, - &worklist_, - is_first_run_); + RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_); instruction->Accept(&visitor); } @@ -331,12 +321,7 @@ void ReferenceTypePropagation::Run() { } void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) { - RTPVisitor visitor(graph_, - class_loader_, - hint_dex_cache_, - &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()); @@ -557,9 +542,8 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* ScopedObjectAccess soa(Thread::Current()); ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get()); - SetClassAsTypeInfo(instr, klass, is_exact); + // Get type from dex cache assuming it was populated by the verifier. + SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact); } void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) { @@ -572,13 +556,25 @@ void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) { SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true); } +static mirror::Class* GetClassFromDexCache(Thread* self, + const DexFile& dex_file, + dex::TypeIndex type_idx, + Handle<mirror::DexCache> hint_dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<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) { // We check if the existing type is valid: the inliner may have set it. if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) { - UpdateReferenceTypeInfo(instr, - instr->GetTypeIndex(), - instr->GetDexFile(), - /* is_exact */ false); + 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); } } diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 215e96786b..4663471729 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -33,7 +33,6 @@ namespace art { class ReferenceTypePropagation : public HOptimization { public: ReferenceTypePropagation(HGraph* graph, - Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> hint_dex_cache, VariableSizedHandleScope* handles, bool is_first_run, @@ -106,8 +105,6 @@ class ReferenceTypePropagation : public HOptimization { void ValidateTypes(); - Handle<mirror::ClassLoader> class_loader_; - // 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(). diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc index 84a4bab1a9..b061c871b0 100644 --- a/compiler/optimizing/reference_type_propagation_test.cc +++ b/compiler/optimizing/reference_type_propagation_test.cc @@ -38,7 +38,6 @@ class ReferenceTypePropagationTest : public CommonCompilerTest { void SetupPropagation(VariableSizedHandleScope* handles) { graph_->InitializeInexactObjectRTI(handles); propagation_ = new (&allocator_) ReferenceTypePropagation(graph_, - Handle<mirror::ClassLoader>(), Handle<mirror::DexCache>(), handles, true, diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index f07f02a719..be400925d5 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -163,7 +163,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, if (!compiler_driver->GetSupportBootImageFixup()) { // compiler_driver_test. Do not sharpen. desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; - } else if ((klass.Get() != nullptr) && compiler_driver->IsImageClass( + } else if ((klass != nullptr) && compiler_driver->IsImageClass( dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) { is_in_boot_image = true; desired_load_kind = codegen->GetCompilerOptions().GetCompilePic() @@ -175,7 +175,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, desired_load_kind = HLoadClass::LoadKind::kBssEntry; } } else { - is_in_boot_image = (klass.Get() != nullptr) && + is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get()); if (runtime->UseJitCompilation()) { // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. @@ -183,7 +183,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, if (is_in_boot_image) { // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; - } else if (klass.Get() != nullptr) { + } else if (klass != nullptr) { desired_load_kind = HLoadClass::LoadKind::kJitTableAddress; } else { // Class not loaded yet. This happens when the dex code requesting diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 50ab11bc23..487e4dd498 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -499,11 +499,7 @@ GraphAnalysisResult SsaBuilder::BuildSsa() { // 4) Compute type of reference type instructions. The pass assumes that // NullConstant has been fixed up. - ReferenceTypePropagation(graph_, - class_loader_, - dex_cache_, - 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 978f113ec4..45dac54115 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -48,11 +48,9 @@ namespace art { class SsaBuilder : public ValueObject { public: SsaBuilder(HGraph* graph, - Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> dex_cache, VariableSizedHandleScope* handles) : graph_(graph), - class_loader_(class_loader), dex_cache_(dex_cache), handles_(handles), agets_fixed_(false), @@ -117,7 +115,6 @@ class SsaBuilder : public ValueObject { void RemoveRedundantUninitializedStrings(); HGraph* graph_; - Handle<mirror::ClassLoader> class_loader_; Handle<mirror::DexCache> dex_cache_; VariableSizedHandleScope* const handles_; diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index f8e01b7537..1bcc8e1ace 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -38,19 +38,14 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); current_entry_.register_mask = register_mask; current_entry_.sp_mask = sp_mask; - current_entry_.num_dex_registers = num_dex_registers; current_entry_.inlining_depth = inlining_depth; - current_entry_.dex_register_locations_start_index = dex_register_locations_.size(); current_entry_.inline_infos_start_index = inline_infos_.size(); - current_entry_.dex_register_map_hash = 0; - current_entry_.same_dex_register_map_as_ = kNoSameDexMapFound; current_entry_.stack_mask_index = 0; - if (num_dex_registers != 0) { - current_entry_.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - } else { - current_entry_.live_dex_registers_mask = nullptr; - } + current_entry_.dex_register_entry.num_dex_registers = num_dex_registers; + current_entry_.dex_register_entry.locations_start_index = dex_register_locations_.size(); + current_entry_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0) + ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream) + : nullptr; if (sp_mask != nullptr) { stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); @@ -65,7 +60,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, } void StackMapStream::EndStackMapEntry() { - current_entry_.same_dex_register_map_as_ = FindEntryWithTheSameDexMap(); + current_entry_.dex_register_map_index = AddDexRegisterMapEntry(current_entry_.dex_register_entry); stack_maps_.push_back(current_entry_); current_entry_ = StackMapEntry(); } @@ -91,19 +86,15 @@ void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t dex_register_locations_.push_back(index); location_catalog_entries_indices_.Insert(std::make_pair(location, index)); } - - if (in_inline_frame_) { - // TODO: Support sharing DexRegisterMap across InlineInfo. - DCHECK_LT(current_dex_register_, current_inline_info_.num_dex_registers); - current_inline_info_.live_dex_registers_mask->SetBit(current_dex_register_); - } else { - DCHECK_LT(current_dex_register_, current_entry_.num_dex_registers); - current_entry_.live_dex_registers_mask->SetBit(current_dex_register_); - current_entry_.dex_register_map_hash += (1 << - (current_dex_register_ % (sizeof(current_entry_.dex_register_map_hash) * kBitsPerByte))); - current_entry_.dex_register_map_hash += static_cast<uint32_t>(value); - current_entry_.dex_register_map_hash += static_cast<uint32_t>(kind); - } + DexRegisterMapEntry* const entry = in_inline_frame_ + ? ¤t_inline_info_.dex_register_entry + : ¤t_entry_.dex_register_entry; + DCHECK_LT(current_dex_register_, entry->num_dex_registers); + entry->live_dex_registers_mask->SetBit(current_dex_register_); + entry->hash += (1 << + (current_dex_register_ % (sizeof(DexRegisterMapEntry::hash) * kBitsPerByte))); + entry->hash += static_cast<uint32_t>(value); + entry->hash += static_cast<uint32_t>(kind); } current_dex_register_++; } @@ -124,20 +115,19 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, current_inline_info_.method_index = method->GetDexMethodIndexUnchecked(); } current_inline_info_.dex_pc = dex_pc; - current_inline_info_.num_dex_registers = num_dex_registers; - current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size(); - if (num_dex_registers != 0) { - current_inline_info_.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - } else { - current_inline_info_.live_dex_registers_mask = nullptr; - } + current_inline_info_.dex_register_entry.num_dex_registers = num_dex_registers; + current_inline_info_.dex_register_entry.locations_start_index = dex_register_locations_.size(); + current_inline_info_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0) + ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream) + : nullptr; current_dex_register_ = 0; } void StackMapStream::EndInlineInfoEntry() { + current_inline_info_.dex_register_map_index = + AddDexRegisterMapEntry(current_inline_info_.dex_register_entry); DCHECK(in_inline_frame_); - DCHECK_EQ(current_dex_register_, current_inline_info_.num_dex_registers) + DCHECK_EQ(current_dex_register_, current_inline_info_.dex_register_entry.num_dex_registers) << "Inline information contains less registers than expected"; in_inline_frame_ = false; inline_infos_.push_back(current_inline_info_); @@ -193,8 +183,7 @@ size_t StackMapStream::ComputeDexRegisterLocationCatalogSize() const { return size; } -size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers, - const BitVector* live_dex_registers_mask) const { +size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) const { // For num_dex_registers == 0u live_dex_registers_mask may be null. if (num_dex_registers == 0u) { return 0u; // No register map will be emitted. @@ -208,8 +197,7 @@ size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers, // Compute the size of the set of live Dex register entries. size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits(); size_t map_entries_size_in_bits = - DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.size()) - * number_of_live_dex_registers; + DexRegisterMap::SingleEntrySizeInBits(catalog_size) * number_of_live_dex_registers; size_t map_entries_size_in_bytes = RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte; size += map_entries_size_in_bytes; @@ -218,18 +206,8 @@ size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers, size_t StackMapStream::ComputeDexRegisterMapsSize() const { size_t size = 0; - size_t inline_info_index = 0; - for (const StackMapEntry& entry : stack_maps_) { - if (entry.same_dex_register_map_as_ == kNoSameDexMapFound) { - size += ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask); - } else { - // Entries with the same dex map will have the same offset. - } - for (size_t j = 0; j < entry.inlining_depth; ++j) { - InlineInfoEntry inline_entry = inline_infos_[inline_info_index++]; - size += ComputeDexRegisterMapSize(inline_entry.num_dex_registers, - inline_entry.live_dex_registers_mask); - } + for (const DexRegisterMapEntry& entry : dex_register_entries_) { + size += entry.ComputeSize(location_catalog_entries_.size()); } return size; } @@ -264,6 +242,30 @@ void StackMapStream::ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, encoding->SetFromSizes(method_index_max, dex_pc_max, extra_data_max, dex_register_maps_bytes); } +size_t StackMapStream::MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, + size_t* current_offset, + MemoryRegion dex_register_locations_region) { + DCHECK(current_offset != nullptr); + if ((entry.num_dex_registers == 0) || (entry.live_dex_registers_mask->NumSetBits() == 0)) { + // No dex register map needed. + return StackMap::kNoDexRegisterMap; + } + if (entry.offset == DexRegisterMapEntry::kOffsetUnassigned) { + // Not already copied, need to copy and and assign an offset. + entry.offset = *current_offset; + const size_t entry_size = entry.ComputeSize(location_catalog_entries_.size()); + DexRegisterMap dex_register_map( + dex_register_locations_region.Subregion(entry.offset, entry_size)); + *current_offset += entry_size; + // Fill in the map since it was just added. + FillInDexRegisterMap(dex_register_map, + entry.num_dex_registers, + *entry.live_dex_registers_mask, + entry.locations_start_index); + } + return entry.offset; +} + void StackMapStream::FillIn(MemoryRegion region) { DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before FillIn"; @@ -311,35 +313,10 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetRegisterMaskIndex(encoding.stack_map.encoding, entry.register_mask_index); stack_map.SetStackMaskIndex(encoding.stack_map.encoding, entry.stack_mask_index); - if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) { - // No dex map available. - stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, StackMap::kNoDexRegisterMap); - } else { - // Search for an entry with the same dex map. - if (entry.same_dex_register_map_as_ != kNoSameDexMapFound) { - // If we have a hit reuse the offset. - stack_map.SetDexRegisterMapOffset( - encoding.stack_map.encoding, - code_info.GetStackMapAt(entry.same_dex_register_map_as_, encoding) - .GetDexRegisterMapOffset(encoding.stack_map.encoding)); - } else { - // New dex registers maps should be added to the stack map. - MemoryRegion register_region = dex_register_locations_region.Subregion( - next_dex_register_map_offset, - ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask)); - next_dex_register_map_offset += register_region.size(); - DexRegisterMap dex_register_map(register_region); - stack_map.SetDexRegisterMapOffset( - encoding.stack_map.encoding, - register_region.begin() - dex_register_locations_region.begin()); - - // Set the dex register location. - FillInDexRegisterMap(dex_register_map, - entry.num_dex_registers, - *entry.live_dex_registers_mask, - entry.dex_register_locations_start_index); - } - } + size_t offset = MaybeCopyDexRegisterMap(dex_register_entries_[entry.dex_register_map_index], + &next_dex_register_map_offset, + dex_register_locations_region); + stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, offset); // Set the inlining info. if (entry.inlining_depth != 0) { @@ -371,29 +348,13 @@ void StackMapStream::FillIn(MemoryRegion region) { inline_info.SetExtraDataAtDepth(encoding.inline_info.encoding, depth, 1); } inline_info.SetDexPcAtDepth(encoding.inline_info.encoding, depth, inline_entry.dex_pc); - if (inline_entry.num_dex_registers == 0) { - // No dex map available. - inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, - depth, - StackMap::kNoDexRegisterMap); - DCHECK(inline_entry.live_dex_registers_mask == nullptr); - } else { - MemoryRegion register_region = dex_register_locations_region.Subregion( - next_dex_register_map_offset, - ComputeDexRegisterMapSize(inline_entry.num_dex_registers, - inline_entry.live_dex_registers_mask)); - next_dex_register_map_offset += register_region.size(); - DexRegisterMap dex_register_map(register_region); - inline_info.SetDexRegisterMapOffsetAtDepth( - encoding.inline_info.encoding, - depth, - register_region.begin() - dex_register_locations_region.begin()); - - FillInDexRegisterMap(dex_register_map, - inline_entry.num_dex_registers, - *inline_entry.live_dex_registers_mask, - inline_entry.dex_register_locations_start_index); - } + size_t dex_register_map_offset = MaybeCopyDexRegisterMap( + dex_register_entries_[inline_entry.dex_register_map_index], + &next_dex_register_map_offset, + dex_register_locations_region); + inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, + depth, + dex_register_map_offset); } } else if (encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) { stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, StackMap::kNoInlineInfo); @@ -448,34 +409,31 @@ void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, } } -size_t StackMapStream::FindEntryWithTheSameDexMap() { - size_t current_entry_index = stack_maps_.size(); - auto entries_it = dex_map_hash_to_stack_map_indices_.find(current_entry_.dex_register_map_hash); +size_t StackMapStream::AddDexRegisterMapEntry(const DexRegisterMapEntry& entry) { + const size_t current_entry_index = dex_register_entries_.size(); + auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.hash); if (entries_it == dex_map_hash_to_stack_map_indices_.end()) { // We don't have a perfect hash functions so we need a list to collect all stack maps // which might have the same dex register map. ArenaVector<uint32_t> stack_map_indices(allocator_->Adapter(kArenaAllocStackMapStream)); stack_map_indices.push_back(current_entry_index); - dex_map_hash_to_stack_map_indices_.Put(current_entry_.dex_register_map_hash, - std::move(stack_map_indices)); - return kNoSameDexMapFound; - } - - // We might have collisions, so we need to check whether or not we really have a match. - for (uint32_t test_entry_index : entries_it->second) { - if (HaveTheSameDexMaps(GetStackMap(test_entry_index), current_entry_)) { - return test_entry_index; + dex_map_hash_to_stack_map_indices_.Put(entry.hash, std::move(stack_map_indices)); + } else { + // We might have collisions, so we need to check whether or not we really have a match. + for (uint32_t test_entry_index : entries_it->second) { + if (DexRegisterMapEntryEquals(dex_register_entries_[test_entry_index], entry)) { + return test_entry_index; + } } + entries_it->second.push_back(current_entry_index); } - entries_it->second.push_back(current_entry_index); - return kNoSameDexMapFound; + dex_register_entries_.push_back(entry); + return current_entry_index; } -bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const { - if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) { - return true; - } - if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) { +bool StackMapStream::DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, + const DexRegisterMapEntry& b) const { + if ((a.live_dex_registers_mask == nullptr) != (b.live_dex_registers_mask == nullptr)) { return false; } if (a.num_dex_registers != b.num_dex_registers) { @@ -489,12 +447,12 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn } size_t number_of_live_dex_registers = a.live_dex_registers_mask->NumSetBits(); DCHECK_LE(number_of_live_dex_registers, dex_register_locations_.size()); - DCHECK_LE(a.dex_register_locations_start_index, + DCHECK_LE(a.locations_start_index, dex_register_locations_.size() - number_of_live_dex_registers); - DCHECK_LE(b.dex_register_locations_start_index, + DCHECK_LE(b.locations_start_index, dex_register_locations_.size() - number_of_live_dex_registers); - auto a_begin = dex_register_locations_.begin() + a.dex_register_locations_start_index; - auto b_begin = dex_register_locations_.begin() + b.dex_register_locations_start_index; + auto a_begin = dex_register_locations_.begin() + a.locations_start_index; + auto b_begin = dex_register_locations_.begin() + b.locations_start_index; if (!std::equal(a_begin, a_begin + number_of_live_dex_registers, b_begin)) { return false; } @@ -597,10 +555,10 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CheckDexRegisterMap(code_info, code_info.GetDexRegisterMapOf( - stack_map, encoding, entry.num_dex_registers), - entry.num_dex_registers, - entry.live_dex_registers_mask, - entry.dex_register_locations_start_index); + stack_map, encoding, entry.dex_register_entry.num_dex_registers), + entry.dex_register_entry.num_dex_registers, + entry.dex_register_entry.live_dex_registers_mask, + entry.dex_register_entry.locations_start_index); // Check inline info. DCHECK_EQ(stack_map.HasInlineInfo(stack_map_encoding), (entry.inlining_depth != 0)); @@ -623,10 +581,13 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CheckDexRegisterMap(code_info, code_info.GetDexRegisterMapAtDepth( - d, inline_info, encoding, inline_entry.num_dex_registers), - inline_entry.num_dex_registers, - inline_entry.live_dex_registers_mask, - inline_entry.dex_register_locations_start_index); + d, + inline_info, + encoding, + inline_entry.dex_register_entry.num_dex_registers), + inline_entry.dex_register_entry.num_dex_registers, + inline_entry.dex_register_entry.live_dex_registers_mask, + inline_entry.dex_register_entry.locations_start_index); } } } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 08c1d3e3c0..bba3d51e62 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -70,6 +70,7 @@ class StackMapStream : public ValueObject { inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), register_masks_(allocator->Adapter(kArenaAllocStackMapStream)), + dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), stack_mask_max_(-1), dex_pc_max_(0), register_mask_max_(0), @@ -89,30 +90,42 @@ class StackMapStream : public ValueObject { code_info_encoding_.reserve(16); } + // A dex register map entry for a single stack map entry, contains what registers are live as + // well as indices into the location catalog. + class DexRegisterMapEntry { + public: + static const size_t kOffsetUnassigned = -1; + + BitVector* live_dex_registers_mask; + uint32_t num_dex_registers; + size_t locations_start_index; + // Computed fields + size_t hash = 0; + size_t offset = kOffsetUnassigned; + + size_t ComputeSize(size_t catalog_size) const; + }; + // See runtime/stack_map.h to know what these fields contain. struct StackMapEntry { uint32_t dex_pc; CodeOffset native_pc_code_offset; uint32_t register_mask; BitVector* sp_mask; - uint32_t num_dex_registers; uint8_t inlining_depth; - size_t dex_register_locations_start_index; size_t inline_infos_start_index; - BitVector* live_dex_registers_mask; - uint32_t dex_register_map_hash; - size_t same_dex_register_map_as_; uint32_t stack_mask_index; uint32_t register_mask_index; + DexRegisterMapEntry dex_register_entry; + size_t dex_register_map_index; }; struct InlineInfoEntry { uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods. ArtMethod* method; uint32_t method_index; - uint32_t num_dex_registers; - BitVector* live_dex_registers_mask; - size_t dex_register_locations_start_index; + DexRegisterMapEntry dex_register_entry; + size_t dex_register_map_index; }; void BeginStackMapEntry(uint32_t dex_pc, @@ -140,7 +153,8 @@ class StackMapStream : public ValueObject { } void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) { - stack_maps_[i].native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); + stack_maps_[i].native_pc_code_offset = + CodeOffset::FromOffset(native_pc_offset, instruction_set_); } // Prepares the stream to fill in a memory region. Must be called before FillIn. @@ -150,8 +164,6 @@ class StackMapStream : public ValueObject { private: size_t ComputeDexRegisterLocationCatalogSize() const; - size_t ComputeDexRegisterMapSize(uint32_t num_dex_registers, - const BitVector* live_dex_registers_mask) const; size_t ComputeDexRegisterMapsSize() const; void ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, size_t dex_register_maps_bytes); @@ -164,15 +176,24 @@ class StackMapStream : public ValueObject { // Returns the number of unique register masks. size_t PrepareRegisterMasks(); - // Returns the index of an entry with the same dex register map as the current_entry, - // or kNoSameDexMapFound if no such entry exists. - size_t FindEntryWithTheSameDexMap(); - bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const; + // Deduplicate entry if possible and return the corresponding index into dex_register_entries_ + // array. If entry is not a duplicate, a new entry is added to dex_register_entries_. + size_t AddDexRegisterMapEntry(const DexRegisterMapEntry& entry); + + // Return true if the two dex register map entries are equal. + bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const; + + // Fill in the corresponding entries of a register map. void FillInDexRegisterMap(DexRegisterMap dex_register_map, uint32_t num_dex_registers, const BitVector& live_dex_registers_mask, uint32_t start_index_in_dex_register_locations) const; + // Returns the offset for the dex register inside of the dex register location region. See FillIn. + // Only copies the dex register map if the offset for the entry is not already assigned. + size_t MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, + size_t* current_offset, + MemoryRegion dex_register_locations_region); void CheckDexRegisterMap(const CodeInfo& code_info, const DexRegisterMap& dex_register_map, size_t num_dex_registers, @@ -199,6 +220,7 @@ class StackMapStream : public ValueObject { ArenaVector<InlineInfoEntry> inline_infos_; ArenaVector<uint8_t> stack_masks_; ArenaVector<uint32_t> register_masks_; + ArenaVector<DexRegisterMapEntry> dex_register_entries_; int stack_mask_max_; uint32_t dex_pc_max_; uint32_t register_mask_max_; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index bd0aa6dea7..041695187b 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -410,6 +410,100 @@ TEST(StackMapTest, Test2) { } } +TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { + ArenaPool pool; + ArenaAllocator arena(&pool); + StackMapStream stream(&arena, kRuntimeISA); + ArtMethod art_method; + + ArenaBitVector sp_mask1(&arena, 0, true); + sp_mask1.SetBit(2); + sp_mask1.SetBit(4); + const size_t number_of_dex_registers = 2; + const size_t number_of_dex_registers_in_inline_info = 2; + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 1); + stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. + stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info); + stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. + stream.EndInlineInfoEntry(); + stream.EndStackMapEntry(); + + size_t size = stream.PrepareForFillIn(); + void* memory = arena.Alloc(size, kArenaAllocMisc); + MemoryRegion region(memory, size); + stream.FillIn(region); + + CodeInfo code_info(region); + CodeInfoEncoding encoding = code_info.ExtractEncoding(); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding)); + + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + ASSERT_EQ(2u, number_of_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); + // The Dex register location catalog contains: + // - one 1-byte short Dex register locations, and + // - one 5-byte large Dex register location. + const size_t expected_location_catalog_size = 1u + 5u; + ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); + + // First stack map. + { + StackMap stack_map = code_info.GetStackMapAt(0, encoding); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); + ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); + + ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); + DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers)); + ASSERT_TRUE(map.IsDexRegisterLive(0)); + ASSERT_TRUE(map.IsDexRegisterLive(1)); + ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask, and + // - one 1-byte set of location catalog entry indices composed of two 2-bit values. + size_t expected_map_size = 1u + 1u; + ASSERT_EQ(expected_map_size, map.Size()); + + ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstant, + map.GetLocationKind(1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kInStack, + map.GetLocationInternalKind(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstantLargeValue, + map.GetLocationInternalKind(1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info, encoding)); + + const size_t index0 = + map.GetLocationCatalogEntryIndex(0, number_of_dex_registers, number_of_catalog_entries); + const size_t index1 = + map.GetLocationCatalogEntryIndex(1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(0u, index0); + ASSERT_EQ(1u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kInStack, location0.GetKind()); + ASSERT_EQ(Kind::kConstant, location1.GetKind()); + ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); + ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); + ASSERT_EQ(0, location0.GetValue()); + ASSERT_EQ(-2, location1.GetValue()); + + // Test that the inline info dex register map deduplicated to the same offset as the stack map + // one. + ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, 0), + stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding)); + } +} + TEST(StackMapTest, TestNonLiveDexRegisters) { ArenaPool pool; ArenaAllocator arena(&pool); |