diff options
Diffstat (limited to 'compiler/optimizing')
39 files changed, 1042 insertions, 444 deletions
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index b7c24ff207..dfa150406d 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -70,9 +70,9 @@ TEST_F(BoundsCheckEliminationTest, NarrowingRangeArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); // array + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); // array HInstruction* parameter2 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); // i + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); // i entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); @@ -167,9 +167,9 @@ TEST_F(BoundsCheckEliminationTest, OverflowArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); // array + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); // array HInstruction* parameter2 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); // i + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); // i entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); @@ -231,9 +231,9 @@ TEST_F(BoundsCheckEliminationTest, UnderflowArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); // array + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); // array HInstruction* parameter2 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); // i + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); // i entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); @@ -295,7 +295,7 @@ TEST_F(BoundsCheckEliminationTest, ConstantArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter = new (&allocator_) HParameterValue( - graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_5 = graph_->GetIntConstant(5); @@ -364,7 +364,7 @@ static HInstruction* BuildSSAGraph1(HGraph* graph, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_initial = graph->GetIntConstant(initial); @@ -479,7 +479,7 @@ static HInstruction* BuildSSAGraph2(HGraph *graph, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_initial = graph->GetIntConstant(initial); @@ -600,7 +600,7 @@ static HInstruction* BuildSSAGraph3(HGraph* graph, constant_10, graph->GetCurrentMethod(), 0, - Primitive::kPrimInt, + dex::TypeIndex(static_cast<uint16_t>(Primitive::kPrimInt)), graph->GetDexFile(), kQuickAllocArray); block->AddInstruction(new_array); @@ -692,7 +692,7 @@ static HInstruction* BuildSSAGraph4(HGraph* graph, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_initial = graph->GetIntConstant(initial); @@ -795,7 +795,7 @@ TEST_F(BoundsCheckEliminationTest, BubbleSortArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter = new (&allocator_) HParameterValue( - graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_0 = graph_->GetIntConstant(0); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 8b450e11dc..9f6b78a82c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1375,4 +1375,33 @@ uint32_t CodeGenerator::GetReferenceDisableFlagOffset() const { return klass->GetDisableIntrinsicFlagOffset().Uint32Value(); } +void CodeGenerator::EmitJitRoots(uint8_t* code, + Handle<mirror::ObjectArray<mirror::Object>> roots, + const uint8_t* roots_data, + Handle<mirror::DexCache> outer_dex_cache) { + DCHECK_EQ(static_cast<size_t>(roots->GetLength()), GetNumberOfJitRoots()); + StackHandleScope<1> hs(Thread::Current()); + MutableHandle<mirror::DexCache> h_dex_cache(hs.NewHandle<mirror::DexCache>(nullptr)); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + size_t index = 0; + for (auto& entry : jit_string_roots_) { + const DexFile& entry_dex_file = *entry.first.dex_file; + // Avoid the expensive FindDexCache call by checking if the string is + // in the compiled method's dex file. + h_dex_cache.Assign(IsSameDexFile(*outer_dex_cache->GetDexFile(), entry_dex_file) + ? outer_dex_cache.Get() + : class_linker->FindDexCache(hs.Self(), entry_dex_file)); + mirror::String* string = class_linker->LookupString( + entry_dex_file, entry.first.string_index, h_dex_cache); + DCHECK(string != nullptr) << "JIT roots require strings to have been loaded"; + // Ensure the string is strongly interned. This is a requirement on how the JIT + // handles strings. b/32995596 + class_linker->GetInternTable()->InternStrong(string); + roots->Set(index, string); + entry.second = index; + ++index; + } + EmitJitRootPatches(code, roots_data); +} + } // namespace art diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index bf246ad309..a5d19abe92 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -32,6 +32,7 @@ #include "optimizing_compiler_stats.h" #include "read_barrier_option.h" #include "stack_map_stream.h" +#include "string_reference.h" #include "utils/label.h" namespace art { @@ -335,6 +336,17 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { void BuildStackMaps(MemoryRegion region, const DexFile::CodeItem& code_item); size_t ComputeStackMapsSize(); + size_t GetNumberOfJitRoots() const { + return jit_string_roots_.size(); + } + + // Fills the `literals` array with literals collected during code generation. + // Also emits literal patches. + void EmitJitRoots(uint8_t* code, + Handle<mirror::ObjectArray<mirror::Object>> roots, + const uint8_t* roots_data, + Handle<mirror::DexCache> outer_dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); bool IsLeafMethod() const { return is_leaf_; @@ -515,6 +527,26 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { virtual HLoadClass::LoadKind GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) = 0; + static LocationSummary::CallKind GetLoadStringCallKind(HLoadString* load) { + switch (load->GetLoadKind()) { + case HLoadString::LoadKind::kBssEntry: + DCHECK(load->NeedsEnvironment()); + return LocationSummary::kCallOnSlowPath; + case HLoadString::LoadKind::kDexCacheViaMethod: + DCHECK(load->NeedsEnvironment()); + return LocationSummary::kCallOnMainOnly; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(!load->NeedsEnvironment()); + return kEmitCompilerReadBarrier + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + break; + default: + DCHECK(!load->NeedsEnvironment()); + return LocationSummary::kNoCall; + } + } + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch( @@ -571,6 +603,8 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { fpu_callee_save_mask_(fpu_callee_save_mask), stack_map_stream_(graph->GetArena()), block_order_(nullptr), + jit_string_roots_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), disasm_info_(nullptr), stats_(stats), graph_(graph), @@ -637,6 +671,12 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { return current_slow_path_; } + // Emit the patches assocatied with JIT roots. Only applies to JIT compiled code. + virtual void EmitJitRootPatches(uint8_t* code ATTRIBUTE_UNUSED, + const uint8_t* roots_data ATTRIBUTE_UNUSED) { + DCHECK_EQ(jit_string_roots_.size(), 0u); + } + // Frame size required for this method. uint32_t frame_size_; uint32_t core_spill_mask_; @@ -662,6 +702,11 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { // The order to use for code generation. const ArenaVector<HBasicBlock*>* block_order_; + // Maps a StringReference (dex_file, string_index) to the index in the literal table. + // Entries are intially added with a 0 index, and `EmitJitRoots` will compute all the + // indices. + ArenaSafeMap<StringReference, size_t, StringReferenceValueComparator> jit_string_roots_; + DisassemblyInformation* disasm_info_; private: diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index c6363d1708..8a6b94e0ea 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -383,7 +383,7 @@ class LoadClassSlowPathARM : public SlowPathCodeARM { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); + __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); @@ -1214,7 +1214,9 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_string_patches_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Always save the LR register to mimic Quick. AddAllocatedRegister(Location::RegisterLocation(LR)); } @@ -3951,7 +3953,7 @@ void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; - __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -5741,7 +5743,7 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -5828,7 +5830,7 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { current_method, ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); + size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option); generate_null_check = !cls->IsInDexCache(); } @@ -5893,6 +5895,9 @@ HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind( case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; case HLoadString::LoadKind::kDexCacheViaMethod: break; } @@ -5900,13 +5905,8 @@ HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind( } void LocationsBuilderARM::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = load->NeedsEnvironment() - ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) - ? LocationSummary::kCallOnMainOnly - : LocationSummary::kCallOnSlowPath) - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); - HLoadString::LoadKind load_kind = load->GetLoadKind(); if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) { locations->SetOut(Location::RegisterLocation(R0)); @@ -5979,6 +5979,13 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { __ Bind(slow_path->GetExitLabel()); return; } + case HLoadString::LoadKind::kJitTableAddress: { + __ LoadLiteral(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(), + load->GetStringIndex())); + // /* GcRoot<mirror::String> */ out = *out + GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + return; + } default: break; } @@ -6490,12 +6497,9 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { iftable_offset, maybe_temp2_loc, kWithoutReadBarrier); - // Null iftable means it is empty and will always fail the check. - __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel()); - - // Loop through the iftable and check if any class matches. + // Iftable is never null. __ ldr(maybe_temp2_loc.AsRegister<Register>(), Address(temp, array_length_offset)); - + // Loop through the iftable and check if any class matches. Label start_loop; __ Bind(&start_loop); __ CompareAndBranchIfZero(maybe_temp2_loc.AsRegister<Register>(), @@ -7320,8 +7324,8 @@ CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatc } CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch( - const DexFile& dex_file, uint32_t type_index) { - return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_); + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_); } CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch( @@ -7343,7 +7347,7 @@ Literal* CodeGeneratorARM::DeduplicateBootImageStringLiteral(const DexFile& dex_ } Literal* CodeGeneratorARM::DeduplicateBootImageTypeLiteral(const DexFile& dex_file, - uint32_t type_index) { + dex::TypeIndex type_index) { return boot_image_type_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); @@ -7359,6 +7363,14 @@ Literal* CodeGeneratorARM::DeduplicateDexCacheAddressLiteral(uint32_t address) { return DeduplicateUint32Literal(address, &uint32_literals_); } +Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file, + uint32_t string_index) { + jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u); + return jit_string_patches_.GetOrCreate( + StringReference(&dex_file, string_index), + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorARM::EmitPcRelativeLinkerPatches( const ArenaDeque<PcRelativePatchInfo>& infos, @@ -7440,7 +7452,7 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche uint32_t literal_offset = literal->GetLabel()->Position(); linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, target_type.dex_file, - target_type.type_index)); + target_type.type_index.index_)); } EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, linker_patches); @@ -7675,6 +7687,21 @@ void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction } } +void CodeGeneratorARM::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { + for (const auto& entry : jit_string_patches_) { + const auto& it = jit_string_roots_.find(entry.first); + DCHECK(it != jit_string_roots_.end()); + size_t index_in_table = it->second; + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = literal->GetLabel()->Position(); + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + uint8_t* data = code + literal_offset; + reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address); + } +} + #undef __ #undef QUICK_ENTRY_POINT diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index f95dd573cb..a4ccb57c1f 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -19,6 +19,7 @@ #include "base/enums.h" #include "code_generator.h" +#include "dex_file_types.h" #include "driver/compiler_options.h" #include "nodes.h" #include "string_reference.h" @@ -481,16 +482,19 @@ class CodeGeneratorARM : public CodeGenerator { }; PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); - PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, uint32_t type_index); + PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); - Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, uint32_t type_index); + Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); Literal* DeduplicateBootImageAddressLiteral(uint32_t address); Literal* DeduplicateDexCacheAddressLiteral(uint32_t address); + Literal* DeduplicateJitStringLiteral(const DexFile& dex_file, uint32_t string_index); void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -591,9 +595,9 @@ class CodeGeneratorARM : public CodeGenerator { using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>; using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>; - using BootStringToLiteralMap = ArenaSafeMap<StringReference, - Literal*, - StringReferenceValueComparator>; + using StringToLiteralMap = ArenaSafeMap<StringReference, + Literal*, + StringReferenceValueComparator>; using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, Literal*, TypeReferenceValueComparator>; @@ -605,7 +609,6 @@ class CodeGeneratorARM : public CodeGenerator { PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches); - template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> static void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos, ArenaVector<LinkerPatch>* linker_patches); @@ -630,7 +633,7 @@ class CodeGeneratorARM : public CodeGenerator { // PC-relative patch info for each HArmDexCacheArraysBase. ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; // Deduplication map for boot string literals for kBootImageLinkTimeAddress. - BootStringToLiteralMap boot_image_string_patches_; + StringToLiteralMap boot_image_string_patches_; // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC). ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. @@ -640,6 +643,9 @@ class CodeGeneratorARM : public CodeGenerator { // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; + // Patches for string literals in JIT compiled code. + StringToLiteralMap jit_string_patches_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM); }; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 4ab6065819..a78b3da455 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -288,7 +288,7 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex()); + __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; arm64_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); @@ -572,8 +572,10 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) { // We are about to use the assembler to place literals directly. Make sure we have enough // underlying code buffer and we have generated the jump table with right size. - CodeBufferCheckScope scope(codegen->GetVIXLAssembler(), num_entries * sizeof(int32_t), - CodeBufferCheckScope::kCheck, CodeBufferCheckScope::kExactSize); + vixl::CodeBufferCheckScope scope(codegen->GetVIXLAssembler(), + num_entries * sizeof(int32_t), + vixl::CodeBufferCheckScope::kReserveBufferSpace, + vixl::CodeBufferCheckScope::kExactSize); __ Bind(&table_start_); const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors(); @@ -1158,7 +1160,9 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_string_patches_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Save the link register (containing the return address) to mimic Quick. AddAllocatedRegister(LocationFrom(lr)); } @@ -2258,10 +2262,10 @@ void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* masm->GetCursorAddress<vixl::aarch64::Instruction*>() - kInstructionSize; if (prev->IsLoadOrStore()) { // Make sure we emit only exactly one nop. - vixl::aarch64::CodeBufferCheckScope scope(masm, - kInstructionSize, - vixl::aarch64::CodeBufferCheckScope::kCheck, - vixl::aarch64::CodeBufferCheckScope::kExactSize); + vixl::CodeBufferCheckScope scope(masm, + kInstructionSize, + vixl::CodeBufferCheckScope::kReserveBufferSpace, + vixl::CodeBufferCheckScope::kExactSize); __ nop(); } } @@ -3802,12 +3806,9 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { iftable_offset, maybe_temp2_loc, kWithoutReadBarrier); - // Null iftable means it is empty and will always fail the check. - __ Cbz(temp, type_check_slow_path->GetEntryLabel()); - - // Loop through the iftable and check if any class matches. + // Iftable is never null. __ Ldr(WRegisterFrom(maybe_temp2_loc), HeapOperand(temp.W(), array_length_offset)); - + // Loop through the iftable and check if any class matches. vixl::aarch64::Label start_loop; __ Bind(&start_loop); __ Cbz(WRegisterFrom(maybe_temp2_loc), type_check_slow_path->GetEntryLabel()); @@ -4037,7 +4038,8 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok vixl::aarch64::Label* label = &relative_call_patches_.back().label; SingleEmissionCheckScope guard(GetVIXLAssembler()); __ Bind(label); - __ bl(0); // Branch and link to itself. This will be overriden at link time. + // Branch and link to itself. This will be overriden at link time. + __ bl(static_cast<int64_t>(0)); break; } case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: @@ -4103,9 +4105,9 @@ vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeStringPatch( vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeTypePatch( const DexFile& dex_file, - uint32_t type_index, + dex::TypeIndex type_index, vixl::aarch64::Label* adrp_label) { - return NewPcRelativePatch(dex_file, type_index, adrp_label, &pc_relative_type_patches_); + return NewPcRelativePatch(dex_file, type_index.index_, adrp_label, &pc_relative_type_patches_); } vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeDexCacheArrayPatch( @@ -4137,7 +4139,7 @@ vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageString } vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageTypeLiteral( - const DexFile& dex_file, uint32_t type_index) { + const DexFile& dex_file, dex::TypeIndex type_index) { return boot_image_type_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); @@ -4155,12 +4157,20 @@ vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateDexCacheAddress return DeduplicateUint64Literal(address); } +vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLiteral( + const DexFile& dex_file, uint32_t string_index) { + jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u); + return jit_string_patches_.GetOrCreate( + StringReference(&dex_file, string_index), + [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); +} + void CodeGeneratorARM64::EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg) { DCHECK(reg.IsX()); SingleEmissionCheckScope guard(GetVIXLAssembler()); __ Bind(fixup_label); - __ adrp(reg, /* offset placeholder */ 0); + __ adrp(reg, /* offset placeholder */ static_cast<int64_t>(0)); } void CodeGeneratorARM64::EmitAddPlaceholder(vixl::aarch64::Label* fixup_label, @@ -4250,7 +4260,7 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc vixl::aarch64::Literal<uint32_t>* literal = entry.second; linker_patches->push_back(LinkerPatch::TypePatch(literal->GetOffset(), target_type.dex_file, - target_type.type_index)); + target_type.type_index.index_)); } EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, linker_patches); @@ -4374,7 +4384,7 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -4410,7 +4420,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); // Add ADRP with its PC-relative type patch. const DexFile& dex_file = cls->GetDexFile(); - uint32_t type_index = cls->GetTypeIndex(); + dex::TypeIndex type_index = cls->GetTypeIndex(); vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeTypePatch(dex_file, type_index); codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); // Add ADD with its PC-relative type patch. @@ -4478,7 +4488,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { GenerateGcRootFieldLoad(cls, out_loc, out.X(), - CodeGenerator::GetCacheOffset(cls->GetTypeIndex()), + CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_), /* fixup_label */ nullptr, read_barrier_option); generate_null_check = !cls->IsInDexCache(); @@ -4540,16 +4550,15 @@ HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind( break; case HLoadString::LoadKind::kDexCacheViaMethod: break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; } return desired_string_load_kind; } void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = load->NeedsEnvironment() - ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) - ? LocationSummary::kCallOnMainOnly - : LocationSummary::kCallOnSlowPath) - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) { InvokeRuntimeCallingConvention calling_convention; @@ -4575,6 +4584,7 @@ void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { Register out = OutputRegister(load); + Location out_loc = load->GetLocations()->Out(); switch (load->GetLoadKind()) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: @@ -4611,9 +4621,9 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { // Add LDR with its PC-relative String patch. vixl::aarch64::Label* ldr_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index, adrp_label); - // /* GcRoot<mirror::Class> */ out = *(base_address + offset) /* PC-relative */ + // /* GcRoot<mirror::String> */ out = *(base_address + offset) /* PC-relative */ GenerateGcRootFieldLoad(load, - load->GetLocations()->Out(), + out_loc, temp, /* offset placeholder */ 0u, ldr_label, @@ -4625,6 +4635,17 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { __ Bind(slow_path->GetExitLabel()); return; } + case HLoadString::LoadKind::kJitTableAddress: { + __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(), + load->GetStringIndex())); + GenerateGcRootFieldLoad(load, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + kCompilerReadBarrierOption); + return; + } default: break; } @@ -4757,7 +4778,7 @@ void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt); DCHECK(type_index.Is(w0)); - __ Mov(type_index, instruction->GetTypeIndex()); + __ Mov(type_index, instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -5744,7 +5765,19 @@ void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instructi } } - +void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { + for (const auto& entry : jit_string_patches_) { + const auto& it = jit_string_roots_.find(entry.first); + DCHECK(it != jit_string_roots_.end()); + size_t index_in_table = it->second; + vixl::aarch64::Literal<uint32_t>* literal = entry.second; + uint32_t literal_offset = literal->GetOffset(); + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + uint8_t* data = code + literal_offset; + reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address); + } +} #undef __ #undef QUICK_ENTRY_POINT diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 0e8d4fd549..1545fd3860 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -20,6 +20,7 @@ #include "arch/arm64/quick_method_frame_info_arm64.h" #include "code_generator.h" #include "common_arm64.h" +#include "dex_file_types.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" @@ -547,7 +548,7 @@ class CodeGeneratorARM64 : public CodeGenerator { // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing // to the associated ADRP patch label). vixl::aarch64::Label* NewPcRelativeTypePatch(const DexFile& dex_file, - uint32_t type_index, + dex::TypeIndex type_index, vixl::aarch64::Label* adrp_label = nullptr); // Add a new PC-relative dex cache array patch for an instruction and return @@ -562,9 +563,11 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, - uint32_t type_index); + dex::TypeIndex type_index); vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address); vixl::aarch64::Literal<uint64_t>* DeduplicateDexCacheAddressLiteral(uint64_t address); + vixl::aarch64::Literal<uint32_t>* DeduplicateJitStringLiteral(const DexFile& dex_file, + uint32_t string_index); void EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg); void EmitAddPlaceholder(vixl::aarch64::Label* fixup_label, @@ -576,6 +579,8 @@ class CodeGeneratorARM64 : public CodeGenerator { void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -673,9 +678,9 @@ class CodeGeneratorARM64 : public CodeGenerator { using MethodToLiteralMap = ArenaSafeMap<MethodReference, vixl::aarch64::Literal<uint64_t>*, MethodReferenceComparator>; - using BootStringToLiteralMap = ArenaSafeMap<StringReference, - vixl::aarch64::Literal<uint32_t>*, - StringReferenceValueComparator>; + using StringToLiteralMap = ArenaSafeMap<StringReference, + vixl::aarch64::Literal<uint32_t>*, + StringReferenceValueComparator>; using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, vixl::aarch64::Literal<uint32_t>*, TypeReferenceValueComparator>; @@ -739,7 +744,7 @@ class CodeGeneratorARM64 : public CodeGenerator { // PC-relative DexCache access info. ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; // Deduplication map for boot string literals for kBootImageLinkTimeAddress. - BootStringToLiteralMap boot_image_string_patches_; + StringToLiteralMap boot_image_string_patches_; // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC). ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. @@ -749,6 +754,9 @@ class CodeGeneratorARM64 : public CodeGenerator { // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; + // Patches for string literals in JIT compiled code. + StringToLiteralMap jit_string_patches_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64); }; diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index e9827e8620..e399f3228e 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -47,6 +47,7 @@ using helpers::InputRegister; using helpers::InputRegisterAt; using helpers::InputSRegisterAt; using helpers::InputVRegisterAt; +using helpers::Int32ConstantFrom; using helpers::LocationFrom; using helpers::LowRegisterFrom; using helpers::LowSRegisterFrom; @@ -132,7 +133,7 @@ static size_t SaveContiguousSRegisterList(size_t first, vixl32::Register base = sp; if (stack_offset != 0) { base = temps.Acquire(); - __ Add(base, sp, stack_offset); + __ Add(base, sp, Operand::From(stack_offset)); } __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs)); } @@ -180,7 +181,7 @@ static size_t RestoreContiguousSRegisterList(size_t first, vixl32::Register base = sp; if (stack_offset != 0) { base = temps.Acquire(); - __ Add(base, sp, stack_offset); + __ Add(base, sp, Operand::From(stack_offset)); } __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs)); } @@ -398,7 +399,7 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConventionARMVIXL calling_convention; - __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); + __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); @@ -673,13 +674,22 @@ void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) { DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold); // We are about to use the assembler to place literals directly. Make sure we have enough - // underlying code buffer and we have generated the jump table with right size. - codegen->GetVIXLAssembler()->GetBuffer().Align(); + // underlying code buffer and we have generated a jump table of the right size, using + // codegen->GetVIXLAssembler()->GetBuffer().Align(); AssemblerAccurateScope aas(codegen->GetVIXLAssembler(), num_entries * sizeof(int32_t), CodeBufferCheckScope::kMaximumSize); // TODO(VIXL): Check that using lower case bind is fine here. codegen->GetVIXLAssembler()->bind(&table_start_); + for (uint32_t i = 0; i < num_entries; i++) { + codegen->GetVIXLAssembler()->place(bb_addresses_[i].get()); + } +} + +void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) { + uint32_t num_entries = switch_instr_->GetNumEntries(); + DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold); + const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors(); for (uint32_t i = 0; i < num_entries; i++) { vixl32::Label* target_label = codegen->GetLabelOf(successors[i]); @@ -691,21 +701,21 @@ void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) { } DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min()); DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max()); - vixl32::Literal<int32_t> literal(jump_offset); - codegen->GetVIXLAssembler()->place(&literal); + + bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer()); } } -void CodeGeneratorARMVIXL::EmitJumpTables() { +void CodeGeneratorARMVIXL::FixJumpTables() { for (auto&& jump_table : jump_tables_) { - jump_table->EmitTable(this); + jump_table->FixTable(this); } } #define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) { - EmitJumpTables(); + FixJumpTables(); GetAssembler()->FinalizeCode(); CodeGenerator::Finalize(allocator); } @@ -1143,7 +1153,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* c void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, vixl32::Label* true_target, - vixl32::Label* false_target) { + vixl32::Label* false_target, + bool far_target) { HInstruction* cond = instruction->InputAt(condition_input_index); if (true_target == nullptr && false_target == nullptr) { @@ -1179,9 +1190,13 @@ void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instru DCHECK(cond_val.IsRegister()); } if (true_target == nullptr) { - __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target); + __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index), + false_target, + far_target); } else { - __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target); + __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index), + true_target, + far_target); } } else { // Condition has not been materialized. Use its inputs as the comparison and @@ -1276,7 +1291,8 @@ void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) { GenerateTestAndBranch(select, /* condition_input_index */ 2, /* true_target */ nullptr, - &false_target); + &false_target, + /* far_target */ false); codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); __ Bind(&false_target); } @@ -1652,7 +1668,20 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* inv // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other // instruction from clobbering it as they might use r12 as a scratch register. DCHECK(hidden_reg.Is(r12)); - __ Mov(hidden_reg, invoke->GetDexMethodIndex()); + + { + // The VIXL macro assembler may clobber any of the scratch registers that are available to it, + // so it checks if the application is using them (by passing them to the macro assembler + // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of + // what is available, and is the opposite of the standard usage: Instead of requesting a + // temporary location, it imposes an external constraint (i.e. a specific register is reserved + // for the hidden argument). Note that this works even if VIXL needs a scratch register itself + // (to materialize the constant), since the destination register becomes available for such use + // internally for the duration of the macro instruction. + UseScratchRegisterScope temps(GetVIXLAssembler()); + temps.Exclude(hidden_reg); + __ Mov(hidden_reg, invoke->GetDexMethodIndex()); + } { AssemblerAccurateScope aas(GetVIXLAssembler(), @@ -2016,7 +2045,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimFloat: { // Processing a Dex `float-to-int' instruction. vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0)); - __ Vcvt(I32, F32, temp, InputSRegisterAt(conversion, 0)); + __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0)); __ Vmov(OutputRegister(conversion), temp); break; } @@ -2024,7 +2053,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimDouble: { // Processing a Dex `double-to-int' instruction. vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0)); - __ Vcvt(I32, F64, temp_s, DRegisterFrom(in)); + __ Vcvt(S32, F64, temp_s, DRegisterFrom(in)); __ Vmov(OutputRegister(conversion), temp_s); break; } @@ -2100,7 +2129,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimChar: { // Processing a Dex `int-to-float' instruction. __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0)); - __ Vcvt(F32, I32, OutputSRegister(conversion), OutputSRegister(conversion)); + __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion)); break; } @@ -2131,7 +2160,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimChar: { // Processing a Dex `int-to-double' instruction. __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0)); - __ Vcvt(F64, I32, DRegisterFrom(out), LowSRegisterFrom(out)); + __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out)); break; } @@ -2139,18 +2168,15 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve // Processing a Dex `long-to-double' instruction. vixl32::Register low = LowRegisterFrom(in); vixl32::Register high = HighRegisterFrom(in); - vixl32::SRegister out_s = LowSRegisterFrom(out); vixl32::DRegister out_d = DRegisterFrom(out); - vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0)); vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0)); - - vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(0)); + vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1)); // temp_d = int-to-double(high) __ Vmov(temp_s, high); - __ Vcvt(F64, I32, temp_d, temp_s); + __ Vcvt(F64, S32, temp_d, temp_s); // constant_d = k2Pow32EncodingForDouble __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble)); // out_d = unsigned-to-double(low) @@ -2446,13 +2472,13 @@ void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOpera vixl32::Register dividend = InputRegisterAt(instruction, 0); vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0)); vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1)); - int64_t imm = second.GetConstant()->AsIntConstant()->GetValue(); + int32_t imm = Int32ConstantFrom(second); int64_t magic; int shift; CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift); - __ Mov(temp1, magic); + __ Mov(temp1, Operand::From(magic)); __ Smull(temp2, temp1, dividend, temp1); if (imm > 0 && magic < 0) { @@ -2769,7 +2795,7 @@ void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instructi case Primitive::kPrimShort: case Primitive::kPrimInt: { if (value.IsRegister()) { - __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); } else { DCHECK(value.IsConstant()) << value; if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { @@ -2845,9 +2871,9 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { } // Rotate, or mov to out for zero or word size rotations. if (rot != 0u) { - __ Lsr(out_reg_hi, in_reg_hi, rot); + __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot)); __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot)); - __ Lsr(out_reg_lo, in_reg_lo, rot); + __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot)); __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot)); } else { __ Mov(out_reg_lo, in_reg_lo); @@ -2862,7 +2888,7 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { __ And(shift_right, RegisterFrom(rhs), 0x1F); __ Lsrs(shift_left, RegisterFrom(rhs), 6); // TODO(VIXL): Check that flags are kept after "vixl32::LeaveFlags" enabled. - __ Rsb(shift_left, shift_right, kArmBitsPerWord); + __ Rsb(shift_left, shift_right, Operand::From(kArmBitsPerWord)); __ B(cc, &shift_by_32_plus_shift_right); // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right). @@ -3028,11 +3054,11 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { // Shift the high part __ Lsl(o_h, high, o_l); // Shift the low part and `or` what overflew on the high part - __ Rsb(temp, o_l, kArmBitsPerWord); + __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord)); __ Lsr(temp, low, temp); __ Orr(o_h, o_h, temp); // If the shift is > 32 bits, override the high part - __ Subs(temp, o_l, kArmBitsPerWord); + __ Subs(temp, o_l, Operand::From(kArmBitsPerWord)); { AssemblerAccurateScope guard(GetVIXLAssembler(), 3 * kArmInstrMaxSizeInBytes, @@ -3047,11 +3073,11 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { // Shift the low part __ Lsr(o_l, low, o_h); // Shift the high part and `or` what underflew on the low part - __ Rsb(temp, o_h, kArmBitsPerWord); + __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord)); __ Lsl(temp, high, temp); __ Orr(o_l, o_l, temp); // If the shift is > 32 bits, override the low part - __ Subs(temp, o_h, kArmBitsPerWord); + __ Subs(temp, o_h, Operand::From(kArmBitsPerWord)); { AssemblerAccurateScope guard(GetVIXLAssembler(), 3 * kArmInstrMaxSizeInBytes, @@ -3065,10 +3091,10 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { __ And(o_h, second_reg, kMaxLongShiftDistance); // same as Shr except we use `Lsr`s and not `Asr`s __ Lsr(o_l, low, o_h); - __ Rsb(temp, o_h, kArmBitsPerWord); + __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord)); __ Lsl(temp, high, temp); __ Orr(o_l, o_l, temp); - __ Subs(temp, o_h, kArmBitsPerWord); + __ Subs(temp, o_h, Operand::From(kArmBitsPerWord)); { AssemblerAccurateScope guard(GetVIXLAssembler(), 3 * kArmInstrMaxSizeInBytes, @@ -3211,7 +3237,7 @@ void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConventionARMVIXL calling_convention; - __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -3412,7 +3438,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register ad __ Add(temp, addr, offset); addr = temp; } - __ Ldrexd(out_lo, out_hi, addr); + __ Ldrexd(out_lo, out_hi, MemOperand(addr)); } void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr, @@ -3432,10 +3458,10 @@ void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register a __ Bind(&fail); // We need a load followed by store. (The address used in a STREX instruction must // be the same as the address in the most recently executed LDREX instruction.) - __ Ldrexd(temp1, temp2, addr); + __ Ldrexd(temp1, temp2, MemOperand(addr)); codegen_->MaybeRecordImplicitNullCheck(instruction); - __ Strexd(temp1, value_lo, value_hi, addr); - __ Cbnz(temp1, &fail); + __ Strexd(temp1, value_lo, value_hi, MemOperand(addr)); + __ CompareAndBranchIfNonZero(temp1, &fail); } void LocationsBuilderARMVIXL::HandleFieldSet( @@ -3951,7 +3977,7 @@ void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) { NullCheckSlowPathARMVIXL* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction); AddSlowPath(slow_path); - __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); } void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) { @@ -4164,7 +4190,14 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { vixl32::Register temp = temps.Acquire(); if (has_intermediate_address) { - TODO_VIXL32(FATAL); + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); + DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset); + } + temp = obj; } else { __ Add(temp, obj, data_offset); } @@ -4209,7 +4242,14 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { vixl32::Register temp = temps.Acquire(); if (has_intermediate_address) { - TODO_VIXL32(FATAL); + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); + DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset); + } + temp = obj; } else { __ Add(temp, obj, data_offset); } @@ -4337,7 +4377,14 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { vixl32::Register temp = temps.Acquire(); if (has_intermediate_address) { - TODO_VIXL32(FATAL); + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); + DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset); + } + temp = array; } else { __ Add(temp, array, data_offset); } @@ -4386,7 +4433,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { codegen_->AddSlowPath(slow_path); if (instruction->GetValueCanBeNull()) { vixl32::Label non_zero; - __ Cbnz(value, &non_zero); + __ CompareAndBranchIfNonZero(value, &non_zero); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; @@ -4434,7 +4481,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset); // If heap poisoning is enabled, no need to unpoison // `temp1`, as we are comparing against null below. - __ Cbnz(temp1, slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); __ Bind(&do_put); } else { __ B(ne, slow_path->GetEntryLabel()); @@ -4556,6 +4603,32 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction } } +void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) { + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!kEmitCompilerReadBarrier); + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset())); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) { + vixl32::Register out = OutputRegister(instruction); + vixl32::Register first = InputRegisterAt(instruction, 0); + Location second = instruction->GetLocations()->InAt(1); + + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!kEmitCompilerReadBarrier); + + if (second.IsRegister()) { + __ Add(out, first, RegisterFrom(second)); + } else { + __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue()); + } +} + void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) { RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConventionARMVIXL calling_convention; @@ -4585,11 +4658,11 @@ void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp, bool can_be_null) { vixl32::Label is_null; if (can_be_null) { - __ Cbz(value, &is_null); + __ CompareAndBranchIfZero(value, &is_null); } GetAssembler()->LoadFromOffset( kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value()); - __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); + __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift)); __ Strb(card, MemOperand(card, temp)); if (can_be_null) { __ Bind(&is_null); @@ -4644,10 +4717,10 @@ void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instru GetAssembler()->LoadFromOffset( kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value()); if (successor == nullptr) { - __ Cbnz(temp, slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel()); __ Bind(slow_path->GetReturnLabel()); } else { - __ Cbz(temp, codegen_->GetLabelOf(successor)); + __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor)); __ B(slow_path->GetEntryLabel()); } } @@ -4915,7 +4988,7 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -4947,7 +5020,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value(); GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset); // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); + size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); GenerateGcRootFieldLoad(cls, out_loc, out, offset, kEmitCompilerReadBarrier); generate_null_check = !cls->IsInDexCache(); break; @@ -4962,7 +5035,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); codegen_->AddSlowPath(slow_path); if (generate_null_check) { - __ Cbz(out, slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); } if (cls->MustGenerateClinitCheck()) { GenerateClassInitializationCheck(slow_path, out); @@ -5153,14 +5226,17 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) // Return 0 if `obj` is null. // avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ Cbz(obj, &zero); + __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false); } - // /* HeapReference<Class> */ out = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); - switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); __ Cmp(out, cls); // Classes must be equal for the instanceof to succeed. __ B(ne, &zero); @@ -5170,6 +5246,12 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kAbstractClassCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. vixl32::Label loop; @@ -5177,7 +5259,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) // /* HeapReference<Class> */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); // If `out` is null, we use it for the result, and jump to `done`. - __ Cbz(out, &done); + __ CompareAndBranchIfZero(out, &done, /* far_target */ false); __ Cmp(out, cls); __ B(ne, &loop); __ Mov(out, 1); @@ -5188,6 +5270,12 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kClassHierarchyCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); // Walk over the class hierarchy to find a match. vixl32::Label loop, success; __ Bind(&loop); @@ -5195,7 +5283,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) __ B(eq, &success); // /* HeapReference<Class> */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); - __ Cbnz(out, &loop); + __ CompareAndBranchIfNonZero(out, &loop); // If `out` is null, we use it for the result, and jump to `done`. __ B(&done); __ Bind(&success); @@ -5207,6 +5295,12 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kArrayObjectCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); // Do an exact check. vixl32::Label exact_check; __ Cmp(out, cls); @@ -5215,10 +5309,10 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) // /* HeapReference<Class> */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); // If `out` is null, we use it for the result, and jump to `done`. - __ Cbz(out, &done); + __ CompareAndBranchIfZero(out, &done, /* far_target */ false); GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(out, &zero); + __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false); __ Bind(&exact_check); __ Mov(out, 1); __ B(&done); @@ -5226,6 +5320,12 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kArrayCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); __ Cmp(out, cls); DCHECK(locations->OnlyCallsOnSlowPath()); slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction, @@ -5348,7 +5448,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { vixl32::Label done; // Avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ Cbz(obj, &done); + __ CompareAndBranchIfZero(obj, &done, /* far_target */ false); } // /* HeapReference<Class> */ temp = obj->klass_ @@ -5374,7 +5474,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // If the class reference currently in `temp` is null, jump to the slow path to throw the // exception. - __ Cbz(temp, type_check_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel()); // Otherwise, compare the classes. __ Cmp(temp, cls); @@ -5394,7 +5494,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // If the class reference currently in `temp` is null, jump to the slow path to throw the // exception. - __ Cbz(temp, type_check_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel()); // Otherwise, jump to the beginning of the loop. __ B(&loop); break; @@ -5409,12 +5509,12 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // /* HeapReference<Class> */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); // If the component type is null, jump to the slow path to throw the exception. - __ Cbz(temp, type_check_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel()); // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type` // to further check that this component type is not a primitive type. GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset); static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot"); - __ Cbnz(temp, type_check_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel()); break; } @@ -5491,6 +5591,70 @@ void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); } +void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + DCHECK(instruction->GetResultType() == Primitive::kPrimInt + || instruction->GetResultType() == Primitive::kPrimLong); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + Location out = locations->Out(); + + if (instruction->GetResultType() == Primitive::kPrimInt) { + vixl32::Register first_reg = RegisterFrom(first); + vixl32::Register second_reg = RegisterFrom(second); + vixl32::Register out_reg = RegisterFrom(out); + + switch (instruction->GetOpKind()) { + case HInstruction::kAnd: + __ Bic(out_reg, first_reg, second_reg); + break; + case HInstruction::kOr: + __ Orn(out_reg, first_reg, second_reg); + break; + // There is no EON on arm. + case HInstruction::kXor: + default: + LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); + UNREACHABLE(); + } + return; + + } else { + DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); + vixl32::Register first_low = LowRegisterFrom(first); + vixl32::Register first_high = HighRegisterFrom(first); + vixl32::Register second_low = LowRegisterFrom(second); + vixl32::Register second_high = HighRegisterFrom(second); + vixl32::Register out_low = LowRegisterFrom(out); + vixl32::Register out_high = HighRegisterFrom(out); + + switch (instruction->GetOpKind()) { + case HInstruction::kAnd: + __ Bic(out_low, first_low, second_low); + __ Bic(out_high, first_high, second_high); + break; + case HInstruction::kOr: + __ Orn(out_low, first_low, second_low); + __ Orn(out_high, first_high, second_high); + break; + // There is no EON on arm. + case HInstruction::kXor: + default: + LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); + UNREACHABLE(); + } + } +} + // TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl. void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out, vixl32::Register first, @@ -5858,6 +6022,32 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location __ Blx(lr); } +void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + vixl32::Register res = OutputRegister(instr); + vixl32::Register accumulator = + InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); + vixl32::Register mul_left = + InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex); + vixl32::Register mul_right = + InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex); + + if (instr->GetOpKind() == HInstruction::kAdd) { + __ Mla(res, mul_left, mul_right, accumulator); + } else { + __ Mls(res, mul_left, mul_right, accumulator); + } +} + void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. LOG(FATAL) << "Unreachable"; @@ -5952,6 +6142,8 @@ void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_in vixl32::Register target_address = table_base; __ Add(target_address, table_base, jump_offset); __ Bx(target_address); + + jump_table->EmitTable(codegen_); } } diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index ccd866c367..38c756fb0f 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -112,6 +112,7 @@ class LoadClassSlowPathARMVIXL; M(ArraySet) \ M(Below) \ M(BelowOrEqual) \ + M(BitwiseNegatedRight) \ M(BooleanNot) \ M(BoundsCheck) \ M(BoundType) \ @@ -136,6 +137,7 @@ class LoadClassSlowPathARMVIXL; M(InstanceFieldSet) \ M(InstanceOf) \ M(IntConstant) \ + M(IntermediateAddress) \ M(InvokeInterface) \ M(InvokeStaticOrDirect) \ M(InvokeUnresolved) \ @@ -149,6 +151,7 @@ class LoadClassSlowPathARMVIXL; M(MemoryBarrier) \ M(MonitorOperation) \ M(Mul) \ + M(MultiplyAccumulate) \ M(NativeDebugInfo) \ M(Neg) \ M(NewArray) \ @@ -186,24 +189,33 @@ class LoadClassSlowPathARMVIXL; // TODO: Remove once the VIXL32 backend is implemented completely. #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ M(ArmDexCacheArraysBase) \ - M(BitwiseNegatedRight) \ - M(IntermediateAddress) \ - M(MultiplyAccumulate) \ class CodeGeneratorARMVIXL; class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> { public: + typedef vixl::aarch32::Literal<int32_t> IntLiteral; + explicit JumpTableARMVIXL(HPackedSwitch* switch_instr) - : switch_instr_(switch_instr), table_start_() {} + : switch_instr_(switch_instr), + table_start_(), + bb_addresses_(switch_instr->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + uint32_t num_entries = switch_instr_->GetNumEntries(); + for (uint32_t i = 0; i < num_entries; i++) { + IntLiteral *lit = new IntLiteral(0); + bb_addresses_.emplace_back(lit); + } + } vixl::aarch32::Label* GetTableStartLabel() { return &table_start_; } void EmitTable(CodeGeneratorARMVIXL* codegen); + void FixTable(CodeGeneratorARMVIXL* codegen); private: HPackedSwitch* const switch_instr_; vixl::aarch32::Label table_start_; + ArenaVector<std::unique_ptr<IntLiteral>> bb_addresses_; DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL); }; @@ -357,7 +369,7 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR) ArmVIXLAssembler* GetAssembler() const { return assembler_; } - vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); } + ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); } private: void VisitUnimplemementedInstruction(HInstruction* instruction) { @@ -439,7 +451,8 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, vixl::aarch32::Label* true_target, - vixl::aarch32::Label* false_target); + vixl::aarch32::Label* false_target, + bool far_target = true); void GenerateCompareTestAndBranch(HCondition* condition, vixl::aarch32::Label* true_target, vixl::aarch32::Label* false_target); @@ -492,7 +505,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { const ArmVIXLAssembler& GetAssembler() const OVERRIDE { return assembler_; } - vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); } + ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); } size_t GetWordSize() const OVERRIDE { return kArmWordSize; } @@ -513,7 +526,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; } - void EmitJumpTables(); + void FixJumpTables(); void GenerateMemoryBarrier(MemBarrierKind kind); void Finalize(CodeAllocator* allocator) OVERRIDE; void SetupBlockedRegisters() const OVERRIDE; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 573bb507f2..8f94834333 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -224,7 +224,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); + __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; @@ -1056,7 +1056,7 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, target_type.dex_file, - target_type.type_index)); + target_type.type_index.index_)); } for (const auto& entry : boot_image_address_patches_) { DCHECK(GetCompilerOptions().GetIncludePatchInformation()); @@ -1073,8 +1073,8 @@ CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeStringPa } CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeTypePatch( - const DexFile& dex_file, uint32_t type_index) { - return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_); + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_); } CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeDexCacheArrayPatch( @@ -1117,7 +1117,7 @@ Literal* CodeGeneratorMIPS::DeduplicateBootImageStringLiteral(const DexFile& dex } Literal* CodeGeneratorMIPS::DeduplicateBootImageTypeLiteral(const DexFile& dex_file, - uint32_t type_index) { + dex::TypeIndex type_index) { return boot_image_type_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); @@ -5207,6 +5207,11 @@ HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind( case HLoadString::LoadKind::kDexCacheViaMethod: fallback_load = false; break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + // TODO: implement. + fallback_load = true; + break; } if (fallback_load) { desired_string_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; @@ -5534,7 +5539,7 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -5628,7 +5633,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { base_or_current_method_reg, ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); + size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); GenerateGcRootFieldLoad(cls, out_loc, out, offset); generate_null_check = !cls->IsInDexCache(); } @@ -5970,7 +5975,7 @@ void InstructionCodeGeneratorMIPS::VisitNewArray(HNewArray* instruction) { Register current_method_register = calling_convention.GetRegisterAt(2); __ Lw(current_method_register, SP, kCurrentMethodStackOffset); // Move an uint16_t value to a register. - __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_); codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index e132819c24..e225d20094 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -449,11 +449,11 @@ class CodeGeneratorMIPS : public CodeGenerator { }; PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); - PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, uint32_t type_index); + PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); - Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, uint32_t type_index); + Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); Literal* DeduplicateBootImageAddressLiteral(uint32_t address); void EmitPcRelativeAddressPlaceholder(PcRelativePatchInfo* info, Register out, Register base); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 1a54935a25..02b01c85e5 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -180,7 +180,7 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); + __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; mips64_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); @@ -3157,7 +3157,7 @@ void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -3174,7 +3174,7 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { __ LoadFromOffset(kLoadDoubleword, out, current_method, ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value()); __ LoadFromOffset( - kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); + kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)); // TODO: We will need a read barrier here. if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { DCHECK(cls->CanCallRuntime()); @@ -3382,7 +3382,8 @@ void LocationsBuilderMIPS64::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) { LocationSummary* locations = instruction->GetLocations(); // Move an uint16_t value to a register. - __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), instruction->GetTypeIndex()); + __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), + instruction->GetTypeIndex().index_); codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 2451b8d247..51e902a824 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -265,7 +265,7 @@ class LoadClassSlowPathX86 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ movl(calling_convention.GetRegisterAt(0), Immediate(cls_->GetTypeIndex())); + __ movl(calling_convention.GetRegisterAt(0), Immediate(cls_->GetTypeIndex().index_)); x86_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType, at_, dex_pc_, this); @@ -1012,6 +1012,7 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), constant_area_start_(-1), fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), method_address_offset_(-1) { @@ -4167,7 +4168,7 @@ void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; - __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex())); + __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex().index_)); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -4611,7 +4612,7 @@ void CodeGeneratorX86::RecordBootStringPatch(HLoadString* load_string) { } void CodeGeneratorX86::RecordTypePatch(HLoadClass* load_class) { - type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex()); + type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_); __ Bind(&type_patches_.back().label); } @@ -6059,7 +6060,7 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -6141,7 +6142,8 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { // /* GcRoot<mirror::Class> */ out = out[type_index] GenerateGcRootFieldLoad(cls, out_loc, - Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())), + Address(out, + CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)), /* fixup_label */ nullptr, read_barrier_option); generate_null_check = !cls->IsInDexCache(); @@ -6218,16 +6220,15 @@ HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( break; case HLoadString::LoadKind::kDexCacheViaMethod: break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; } return desired_string_load_kind; } void LocationsBuilderX86::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier) - ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) - ? LocationSummary::kCallOnMainOnly - : LocationSummary::kCallOnSlowPath) - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); HLoadString::LoadKind load_kind = load->GetLoadKind(); if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative || @@ -6252,6 +6253,14 @@ void LocationsBuilderX86::VisitLoadString(HLoadString* load) { } } +Label* CodeGeneratorX86::NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index) { + jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u); + // Add a patch entry and return the label. + jit_string_patches_.emplace_back(dex_file, dex_index); + PatchInfo<Label>* info = &jit_string_patches_.back(); + return &info->label; +} + void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) { LocationSummary* locations = load->GetLocations(); Location out_loc = locations->Out(); @@ -6280,7 +6289,7 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) { Register method_address = locations->InAt(0).AsRegister<Register>(); Address address = Address(method_address, CodeGeneratorX86::kDummy32BitOffset); Label* fixup_label = codegen_->NewStringBssEntryPatch(load); - // /* GcRoot<mirror::Class> */ out = *address /* PC-relative */ + // /* GcRoot<mirror::String> */ out = *address /* PC-relative */ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption); SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load); codegen_->AddSlowPath(slow_path); @@ -6289,6 +6298,14 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) { __ Bind(slow_path->GetExitLabel()); return; } + case HLoadString::LoadKind::kJitTableAddress: { + Address address = Address::Absolute(CodeGeneratorX86::kDummy32BitOffset); + Label* fixup_label = codegen_->NewJitRootStringPatch( + load->GetDexFile(), load->GetStringIndex()); + // /* GcRoot<mirror::String> */ out = *address + GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption); + return; + } default: break; } @@ -6849,24 +6866,24 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { temp_loc, iftable_offset, kWithoutReadBarrier); - // Null iftable means it is empty. - __ testl(temp, temp); - __ j(kZero, type_check_slow_path->GetEntryLabel()); - - // Loop through the iftable and check if any class matches. + // Iftable is never null. __ movl(maybe_temp2_loc.AsRegister<Register>(), Address(temp, array_length_offset)); - + // Loop through the iftable and check if any class matches. NearLabel start_loop; __ Bind(&start_loop); - __ cmpl(cls.AsRegister<Register>(), Address(temp, object_array_data_offset)); - __ j(kEqual, &done); // Return if same class. - // Go to next interface. - __ addl(temp, Immediate(2 * kHeapReferenceSize)); + // Need to subtract first to handle the empty array case. __ subl(maybe_temp2_loc.AsRegister<Register>(), Immediate(2)); - __ j(kNotZero, &start_loop); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister<Register>(), + CodeGeneratorX86::ArrayAddress(temp, + maybe_temp2_loc, + TIMES_4, + object_array_data_offset)); + __ j(kNotEqual, &start_loop); + } else { + __ jmp(type_check_slow_path->GetEntryLabel()); } - - __ jmp(type_check_slow_path->GetEntryLabel()); break; } } @@ -7562,7 +7579,7 @@ class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenera // The value to patch is the distance from the offset in the constant area // from the address computed by the HX86ComputeBaseMethodAddress instruction. int32_t constant_offset = codegen_->ConstantAreaStart() + offset_into_constant_area_; - int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset();; + int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset(); // Patch in the right value. region.StoreUnaligned<int32_t>(pos - 4, relative_position); @@ -7736,6 +7753,20 @@ void CodeGeneratorX86::MoveFromReturnRegister(Location target, Primitive::Type t } } +void CodeGeneratorX86::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { + for (const PatchInfo<Label>& info : jit_string_patches_) { + const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, info.index)); + DCHECK(it != jit_string_roots_.end()); + size_t index_in_table = it->second; + uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; + reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] = + dchecked_integral_cast<uint32_t>(address); + } +} + #undef __ } // namespace x86 diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 164231b4e5..16ea6b55d6 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -414,12 +414,15 @@ class CodeGeneratorX86 : public CodeGenerator { void RecordTypePatch(HLoadClass* load_class); Label* NewStringBssEntryPatch(HLoadString* load_string); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); + Label* NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; // Emit linker patches. void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Emit a write barrier. void MarkGCCard(Register temp, Register card, @@ -616,6 +619,9 @@ class CodeGeneratorX86 : public CodeGenerator { // Type patch locations. ArenaDeque<PatchInfo<Label>> type_patches_; + // Patches for string root accesses in JIT compiled code. + ArenaDeque<PatchInfo<Label>> jit_string_patches_; + // Offset to the start of the constant area in the assembled code. // Used for fixups to the constant area. int32_t constant_area_start_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 2425a4c3cb..34673138bf 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -246,7 +246,8 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(cls_->GetTypeIndex())); + __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), + Immediate(cls_->GetTypeIndex().index_)); x86_64_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType, at_, dex_pc_, @@ -1110,7 +1111,7 @@ void CodeGeneratorX86_64::RecordBootStringPatch(HLoadString* load_string) { } void CodeGeneratorX86_64::RecordTypePatch(HLoadClass* load_class) { - type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex()); + type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_); __ Bind(&type_patches_.back().label); } @@ -1258,7 +1259,8 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister)); } @@ -4097,7 +4099,7 @@ void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)), - instruction->GetTypeIndex()); + instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -5484,7 +5486,7 @@ void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -5567,7 +5569,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { GenerateGcRootFieldLoad( cls, out_loc, - Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())), + Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)), /* fixup_label */ nullptr, read_barrier_option); generate_null_check = !cls->IsInDexCache(); @@ -5630,16 +5632,15 @@ HLoadString::LoadKind CodeGeneratorX86_64::GetSupportedLoadStringKind( break; case HLoadString::LoadKind::kDexCacheViaMethod: break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; } return desired_string_load_kind; } void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = load->NeedsEnvironment() - ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) - ? LocationSummary::kCallOnMainOnly - : LocationSummary::kCallOnSlowPath) - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) { locations->SetOut(Location::RegisterLocation(RAX)); @@ -5659,6 +5660,14 @@ void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) { } } +Label* CodeGeneratorX86_64::NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index) { + jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u); + // Add a patch entry and return the label. + jit_string_patches_.emplace_back(dex_file, dex_index); + PatchInfo<Label>* info = &jit_string_patches_.back(); + return &info->label; +} + void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) { LocationSummary* locations = load->GetLocations(); Location out_loc = locations->Out(); @@ -5690,6 +5699,15 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) { __ Bind(slow_path->GetExitLabel()); return; } + case HLoadString::LoadKind::kJitTableAddress: { + Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, + /* no_rip */ true); + Label* fixup_label = + codegen_->NewJitRootStringPatch(load->GetDexFile(), load->GetStringIndex()); + // /* GcRoot<mirror::String> */ out = *address + GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption); + return; + } default: break; } @@ -6259,23 +6277,24 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { temp_loc, iftable_offset, kWithoutReadBarrier); - // Null iftable means it is empty. - __ testl(temp, temp); - __ j(kZero, type_check_slow_path->GetEntryLabel()); - - // Loop through the iftable and check if any class matches. + // Iftable is never null. __ movl(maybe_temp2_loc.AsRegister<CpuRegister>(), Address(temp, array_length_offset)); - + // Loop through the iftable and check if any class matches. NearLabel start_loop; __ Bind(&start_loop); - __ cmpl(cls.AsRegister<CpuRegister>(), Address(temp, object_array_data_offset)); - __ j(kEqual, &done); // Return if same class. - // Go to next interface. - __ addl(temp, Immediate(2 * kHeapReferenceSize)); + // Need to subtract first to handle the empty array case. __ subl(maybe_temp2_loc.AsRegister<CpuRegister>(), Immediate(2)); - __ j(kNotZero, &start_loop); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister<CpuRegister>(), + CodeGeneratorX86_64::ArrayAddress(temp, + maybe_temp2_loc, + TIMES_4, + object_array_data_offset)); + __ j(kNotEqual, &start_loop); // Return if same class. + } else { + __ jmp(type_check_slow_path->GetEntryLabel()); } - __ jmp(type_check_slow_path->GetEntryLabel()); break; } @@ -7090,6 +7109,20 @@ void CodeGeneratorX86_64::MoveInt64ToAddress(const Address& addr_low, } } +void CodeGeneratorX86_64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { + for (const PatchInfo<Label>& info : jit_string_patches_) { + const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, info.index)); + DCHECK(it != jit_string_roots_.end()); + size_t index_in_table = it->second; + uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; + reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] = + dchecked_integral_cast<uint32_t>(address); + } +} + #undef __ } // namespace x86_64 diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index e5a4152517..0f70b15787 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -412,11 +412,14 @@ class CodeGeneratorX86_64 : public CodeGenerator { void RecordTypePatch(HLoadClass* load_class); Label* NewStringBssEntryPatch(HLoadString* load_string); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); + Label* NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + const X86_64InstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; } @@ -602,6 +605,9 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Fixups for jump tables need to be handled specially. ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_; + // Patches for string literals in JIT compiled code. + ArenaDeque<PatchInfo<Label>> jit_string_patches_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86_64); }; diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h index 5129dafca1..d3623f17d1 100644 --- a/compiler/optimizing/common_arm.h +++ b/compiler/optimizing/common_arm.h @@ -139,9 +139,14 @@ inline int32_t Int32ConstantFrom(Location location) { HConstant* instr = location.GetConstant(); if (instr->IsIntConstant()) { return instr->AsIntConstant()->GetValue(); - } else { - DCHECK(instr->IsNullConstant()) << instr->DebugName(); + } else if (instr->IsNullConstant()) { return 0; + } else { + DCHECK(instr->IsLongConstant()) << instr->DebugName(); + const int64_t ret = instr->AsLongConstant()->GetValue(); + DCHECK_GE(ret, std::numeric_limits<int32_t>::min()); + DCHECK_LE(ret, std::numeric_limits<int32_t>::max()); + return ret; } } @@ -161,7 +166,7 @@ inline vixl::aarch32::Operand OperandFrom(Location location, Primitive::Type typ if (location.IsRegister()) { return vixl::aarch32::Operand(RegisterFrom(location, type)); } else { - return vixl::aarch32::Operand(Int64ConstantFrom(location)); + return vixl::aarch32::Operand(Int32ConstantFrom(location)); } } diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index 5fac3acb8a..7ef28ed910 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -756,7 +756,7 @@ TEST_F(ConstantFoldingTest, UnsignedComparisonsWithZero) { // Make various unsigned comparisons with zero against a parameter. HInstruction* parameter = new (&allocator_) HParameterValue( - graph_->GetDexFile(), 0, 0, Primitive::kPrimInt, true); + graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt, true); entry_block->AddInstruction(parameter); entry_block->AddInstruction(new (&allocator_) HGoto()); diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc new file mode 100644 index 0000000000..c80e19ef15 --- /dev/null +++ b/compiler/optimizing/escape.cc @@ -0,0 +1,100 @@ +/* + * 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. + */ + +#include "escape.h" + +#include "nodes.h" + +namespace art { + +void CalculateEscape(HInstruction* reference, + bool (*no_escape)(HInstruction*, HInstruction*), + /*out*/ bool* is_singleton, + /*out*/ bool* is_singleton_and_non_escaping) { + // For references not allocated in the method, don't assume anything. + if (!reference->IsNewInstance() && !reference->IsNewArray()) { + *is_singleton = false; + *is_singleton_and_non_escaping = false; + return; + } + // Assume the best until proven otherwise. + *is_singleton = true; + *is_singleton_and_non_escaping = true; + // Visit all uses to determine if this reference can escape into the heap, + // a method call, an alias, etc. + for (const HUseListNode<HInstruction*>& use : reference->GetUses()) { + HInstruction* user = use.GetUser(); + if (no_escape != nullptr && (*no_escape)(reference, user)) { + // Client supplied analysis says there is no escape. + continue; + } else if (user->IsBoundType() || user->IsNullCheck()) { + // BoundType shouldn't normally be necessary for an allocation. Just be conservative + // for the uncommon cases. Similarly, null checks are eventually eliminated for explicit + // allocations, but if we see one before it is simplified, assume an alias. + *is_singleton = false; + *is_singleton_and_non_escaping = false; + return; + } else if (user->IsPhi() || user->IsSelect() || user->IsInvoke() || + (user->IsInstanceFieldSet() && (reference == user->InputAt(1))) || + (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(1))) || + (user->IsStaticFieldSet() && (reference == user->InputAt(1))) || + (user->IsUnresolvedStaticFieldSet() && (reference == user->InputAt(0))) || + (user->IsArraySet() && (reference == user->InputAt(2)))) { + // The reference is merged to HPhi/HSelect, passed to a callee, or stored to heap. + // Hence, the reference is no longer the only name that can refer to its value. + *is_singleton = false; + *is_singleton_and_non_escaping = false; + return; + } else if ((user->IsUnresolvedInstanceFieldGet() && (reference == user->InputAt(0))) || + (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(0)))) { + // The field is accessed in an unresolved way. We mark the object as a non-singleton. + // Note that we could optimize this case and still perform some optimizations until + // we hit the unresolved access, but the conservative assumption is the simplest. + *is_singleton = false; + *is_singleton_and_non_escaping = false; + return; + } else if (user->IsReturn()) { + *is_singleton_and_non_escaping = false; + } + } + + // Need for further analysis? + if (!*is_singleton_and_non_escaping) { + return; + } + + // Look at the environment uses and if it's for HDeoptimize, it's treated the + // same as a return which escapes at the end of executing the compiled code. + // Other environment uses are fine, as long as all client optimizations that + // rely on this informations are disabled for debuggable. + for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) { + HEnvironment* user = use.GetUser(); + if (user->GetHolder()->IsDeoptimize()) { + *is_singleton_and_non_escaping = false; + break; + } + } +} + +bool IsNonEscapingSingleton(HInstruction* reference, + bool (*no_escape)(HInstruction*, HInstruction*)) { + bool is_singleton = true; + bool is_singleton_and_non_escaping = true; + CalculateEscape(reference, no_escape, &is_singleton, &is_singleton_and_non_escaping); + return is_singleton_and_non_escaping; +} + +} // namespace art diff --git a/compiler/optimizing/escape.h b/compiler/optimizing/escape.h new file mode 100644 index 0000000000..6514843247 --- /dev/null +++ b/compiler/optimizing/escape.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_OPTIMIZING_ESCAPE_H_ +#define ART_COMPILER_OPTIMIZING_ESCAPE_H_ + +namespace art { + +class HInstruction; + +/* + * Methods related to escape analysis, i.e. determining whether an object + * allocation is visible outside ('escapes') its immediate method context. + */ + +/* + * Performs escape analysis on the given instruction, typically a reference to an + * allocation. The method assigns true to parameter 'is_singleton' if the reference + * is the only name that can refer to its value during the lifetime of the method, + * meaning that the reference is not aliased with something else, is not stored to + * heap memory, and not passed to another method. The method assigns true to parameter + * 'is_singleton_and_non_escaping' if the reference is a singleton and is not returned + * to the caller or used as an environment local of an HDeoptimize instruction. + * + * When set, the no_escape function is applied to any use of the allocation instruction + * prior to any built-in escape analysis. This allows clients to define better escape + * analysis in certain case-specific circumstances. If 'no_escape(reference, user)' + * returns true, the user is assumed *not* to cause any escape right away. The return + * value false means the client cannot provide a definite answer and built-in escape + * analysis is applied to the user instead. + */ +void CalculateEscape(HInstruction* reference, + bool (*no_escape)(HInstruction*, HInstruction*), + /*out*/ bool* is_singleton, + /*out*/ bool* is_singleton_and_non_escaping); + +/* + * Convenience method for testing singleton and non-escaping property at once. + * Callers should be aware that this method invokes the full analysis at each call. + */ +bool IsNonEscapingSingleton(HInstruction* reference, + bool (*no_escape)(HInstruction*, HInstruction*)); + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_ESCAPE_H_ diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc index 6abf00e21a..437d35ccb7 100644 --- a/compiler/optimizing/gvn_test.cc +++ b/compiler/optimizing/gvn_test.cc @@ -35,7 +35,7 @@ TEST_F(GVNTest, LocalFieldElimination) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(), - 0, + dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); @@ -120,7 +120,7 @@ TEST_F(GVNTest, GlobalFieldElimination) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(), - 0, + dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); @@ -204,7 +204,7 @@ TEST_F(GVNTest, LoopFieldElimination) { graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(), - 0, + dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); @@ -352,7 +352,7 @@ TEST_F(GVNTest, LoopSideEffects) { inner_loop_exit->AddSuccessor(outer_loop_header); HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(), - 0, + dex::TypeIndex(0), 0, Primitive::kPrimBoolean); entry->AddInstruction(parameter); diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index 031f1d74a8..3425b88260 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -80,7 +80,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest { // Provide entry and exit instructions. parameter_ = new (&allocator_) HParameterValue( - graph_->GetDexFile(), 0, 0, Primitive::kPrimNot, true); + graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot, true); entry_->AddInstruction(parameter_); constant0_ = graph_->GetIntConstant(0); constant1_ = graph_->GetIntConstant(1); diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index 8bbdd4acb7..4c99e3cb6e 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -62,9 +62,15 @@ class InductionVarRangeTest : public CommonCompilerTest { graph_->SetEntryBlock(entry_block_); graph_->SetExitBlock(exit_block_); // Two parameters. - x_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); + x_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + Primitive::kPrimInt); entry_block_->AddInstruction(x_); - y_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); + y_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + Primitive::kPrimInt); entry_block_->AddInstruction(y_); // Set arbitrary range analysis hint while testing private methods. SetHint(x_); @@ -572,7 +578,8 @@ TEST_F(InductionVarRangeTest, ArrayLengthAndHints) { HInstruction* new_array = new (&allocator_) HNewArray(x_, graph_->GetCurrentMethod(), - 0, Primitive::kPrimInt, + 0, + dex::TypeIndex(Primitive::kPrimInt), graph_->GetDexFile(), kQuickAllocArray); entry_block_->AddInstruction(new_array); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 7fe54b9045..16a465a43d 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -197,15 +197,15 @@ static uint32_t FindMethodIndexIn(ArtMethod* method, } } -static uint32_t FindClassIndexIn(mirror::Class* cls, - const DexFile& dex_file, - Handle<mirror::DexCache> dex_cache) +static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, + const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { - uint32_t index = DexFile::kDexNoIndex; + dex::TypeIndex index; if (cls->GetDexCache() == nullptr) { DCHECK(cls->IsArrayClass()) << cls->PrettyClass(); index = cls->FindTypeIndexInOtherDexFile(dex_file); - } else if (cls->GetDexTypeIndex() == DexFile::kDexNoIndex16) { + } else if (!cls->GetDexTypeIndex().IsValid()) { DCHECK(cls->IsProxyClass()) << cls->PrettyClass(); // TODO: deal with proxy classes. } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) { @@ -223,8 +223,8 @@ static uint32_t FindClassIndexIn(mirror::Class* cls, // 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 in the dex cache already. - if (index != DexFile::kDexNoIndex && dex_cache->GetResolvedType(index) != cls) { - index = DexFile::kDexNoIndex; + if (index.IsValid() && dex_cache->GetResolvedType(index) != cls) { + index = dex::TypeIndex::Invalid(); } } @@ -363,9 +363,9 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, << invoke_instruction->DebugName(); const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); - uint32_t class_index = FindClassIndexIn( + dex::TypeIndex class_index = FindClassIndexIn( ic.GetMonomorphicType(), caller_dex_file, caller_compilation_unit_.GetDexCache()); - if (class_index == DexFile::kDexNoIndex) { + if (!class_index.IsValid()) { VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method) << " from inline cache is not inlined because its class is not" << " accessible to the caller"; @@ -417,7 +417,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, HInstruction* cursor, HBasicBlock* bb_cursor, - uint32_t class_index, + dex::TypeIndex class_index, bool is_referrer, HInstruction* invoke_instruction, bool with_deoptimization) { @@ -489,10 +489,10 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - uint32_t class_index = FindClassIndexIn( + dex::TypeIndex class_index = FindClassIndexIn( ic.GetTypeAt(i), caller_dex_file, caller_compilation_unit_.GetDexCache()); HInstruction* return_replacement = nullptr; - if (class_index == DexFile::kDexNoIndex || + if (!class_index.IsValid() || !TryBuildAndInline(invoke_instruction, method, &return_replacement)) { all_targets_inlined = false; } else { diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index a1dcd58a84..682393e697 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_INLINER_H_ #define ART_COMPILER_OPTIMIZING_INLINER_H_ +#include "dex_file_types.h" #include "invoke_type.h" #include "optimization.h" @@ -150,7 +151,7 @@ class HInliner : public HOptimization { HInstruction* AddTypeGuard(HInstruction* receiver, HInstruction* cursor, HBasicBlock* bb_cursor, - uint32_t class_index, + dex::TypeIndex class_index, bool is_referrer, HInstruction* invoke_instruction, bool with_deoptimization) diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index c8c4ca76fd..40de5ce0cc 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -908,7 +908,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, false /* is_unresolved */); } -bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) { +bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); @@ -1004,7 +1004,7 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass())); // The index at which the method's class is stored in the DexCache's type array. - uint32_t storage_index = DexFile::kDexNoIndex; + dex::TypeIndex storage_index; bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get()); if (is_outer_class) { storage_index = outer_class->GetDexTypeIndex(); @@ -1021,7 +1021,7 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( if (IsInitialized(resolved_method_class)) { *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; - } else if (storage_index != DexFile::kDexNoIndex) { + } else if (storage_index.IsValid()) { *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit; HLoadClass* load_class = new (arena_) HLoadClass( graph_->GetCurrentMethod(), @@ -1297,7 +1297,7 @@ mirror::Class* HInstructionBuilder::GetCompilingClass() const { return GetClassFrom(compiler_driver_, *dex_compilation_unit_); } -bool HInstructionBuilder::IsOutermostCompilingClass(uint16_t type_index) const { +bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) const { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<3> hs(soa.Self()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); @@ -1360,7 +1360,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); // The index at which the field's class is stored in the DexCache's type array. - uint32_t storage_index; + dex::TypeIndex storage_index; bool is_outer_class = (outer_class.Get() == resolved_field->GetDeclaringClass()); if (is_outer_class) { storage_index = outer_class->GetDexTypeIndex(); @@ -1497,7 +1497,7 @@ void HInstructionBuilder::BuildArrayAccess(const Instruction& instruction, } void HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc, - uint32_t type_index, + dex::TypeIndex type_index, uint32_t number_of_vreg_arguments, bool is_range, uint32_t* args, @@ -1644,7 +1644,7 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, - uint16_t type_index, + dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); @@ -1684,14 +1684,14 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, } } -bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, +bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, Handle<mirror::DexCache> dex_cache, bool* finalizable) const { return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks( dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable); } -bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const { +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); @@ -1723,7 +1723,10 @@ uint16_t HInstructionBuilder::LookupQuickenedInfo(uint32_t dex_pc) { if (dex_pc_in_map == dex_pc) { return value_in_map; } else { - skipped_interpreter_metadata_.Put(dex_pc_in_map, value_in_map); + // Overwrite and not Put, as quickened CHECK-CAST has two entries with + // the same dex_pc. This is OK, because the compiler does not care about those + // entries. + skipped_interpreter_metadata_.Overwrite(dex_pc_in_map, value_in_map); } } } @@ -2446,7 +2449,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::NEW_INSTANCE: { - if (!BuildNewInstance(instruction.VRegB_21c(), dex_pc)) { + if (!BuildNewInstance(dex::TypeIndex(instruction.VRegB_21c()), dex_pc)) { return false; } UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); @@ -2454,7 +2457,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::NEW_ARRAY: { - uint16_t type_index = instruction.VRegC_22c(); + dex::TypeIndex type_index(instruction.VRegC_22c()); HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt); bool finalizable; QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable) @@ -2472,7 +2475,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::FILLED_NEW_ARRAY: { uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); - uint32_t type_index = instruction.VRegB_35c(); + dex::TypeIndex type_index(instruction.VRegB_35c()); uint32_t args[5]; instruction.GetVarArgs(args); BuildFilledNewArray(dex_pc, type_index, number_of_vreg_arguments, false, args, 0); @@ -2481,7 +2484,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::FILLED_NEW_ARRAY_RANGE: { uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); - uint32_t type_index = instruction.VRegB_3rc(); + dex::TypeIndex type_index(instruction.VRegB_3rc()); uint32_t register_index = instruction.VRegC_3rc(); BuildFilledNewArray( dex_pc, type_index, number_of_vreg_arguments, true, nullptr, register_index); @@ -2638,7 +2641,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::CONST_CLASS: { - uint16_t type_index = instruction.VRegB_21c(); + dex::TypeIndex type_index(instruction.VRegB_21c()); // `CanAccessTypeWithoutChecks` will tell whether the method being // built is trying to access its own class, so that the generated // code can optimize for this case. However, the optimization does not @@ -2679,14 +2682,14 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::INSTANCE_OF: { uint8_t destination = instruction.VRegA_22c(); uint8_t reference = instruction.VRegB_22c(); - uint16_t type_index = instruction.VRegC_22c(); + dex::TypeIndex type_index(instruction.VRegC_22c()); BuildTypeCheck(instruction, destination, reference, type_index, dex_pc); break; } case Instruction::CHECK_CAST: { uint8_t reference = instruction.VRegA_21c(); - uint16_t type_index = instruction.VRegB_21c(); + dex::TypeIndex type_index(instruction.VRegB_21c()); BuildTypeCheck(instruction, -1, reference, type_index, dex_pc); break; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index aa34ddd1d1..f29e522040 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -20,6 +20,7 @@ #include "base/arena_containers.h" #include "base/arena_object.h" #include "block_builder.h" +#include "dex_file_types.h" #include "driver/compiler_driver.h" #include "driver/compiler_driver-inl.h" #include "driver/dex_compilation_unit.h" @@ -100,11 +101,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(uint32_t type_index, + bool NeedsAccessCheck(dex::TypeIndex type_index, Handle<mirror::DexCache> dex_cache, /*out*/bool* finalizable) const REQUIRES_SHARED(Locks::mutator_lock_); - bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const; + 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); @@ -176,7 +177,7 @@ class HInstructionBuilder : public ValueObject { // Builds a new array node and the instructions that fill it. void BuildFilledNewArray(uint32_t dex_pc, - uint32_t type_index, + dex::TypeIndex type_index, uint32_t number_of_vreg_arguments, bool is_range, uint32_t* args, @@ -205,7 +206,7 @@ class HInstructionBuilder : public ValueObject { void BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, - uint16_t type_index, + dex::TypeIndex type_index, uint32_t dex_pc); // Builds an instruction sequence for a switch statement. @@ -218,7 +219,7 @@ class HInstructionBuilder : public ValueObject { mirror::Class* GetCompilingClass() const; // Returns whether `type_index` points to the outer-most compiling method's class. - bool IsOutermostCompilingClass(uint16_t type_index) const; + bool IsOutermostCompilingClass(dex::TypeIndex type_index) const; void PotentiallySimplifyFakeString(uint16_t original_dex_register, uint32_t dex_pc, @@ -258,7 +259,7 @@ class HInstructionBuilder : public ValueObject { REQUIRES_SHARED(Locks::mutator_lock_); // Build a HNewInstance instruction. - bool BuildNewInstance(uint16_t type_index, uint32_t dex_pc); + bool BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc); // Return whether the compiler can assume `cls` is initialized. bool IsInitialized(Handle<mirror::Class> cls) const diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 451abc56d3..17a97da6cc 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -2165,11 +2165,11 @@ void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) { __ Cbz(dst, slow_path->GetEntryLabel()); if (!length.IsConstant()) { - // If the length is negative, bail out. - __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel()); - // If the length > 32 then (currently) prefer libcore's native implementation. + // Merge the following two comparisons into one: + // If the length is negative, bail out (delegate to libcore's native implementation). + // If the length > 32 then (currently) prefer libcore's native implementation. __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold); - __ B(slow_path->GetEntryLabel(), gt); + __ B(slow_path->GetEntryLabel(), hi); } else { // We have already checked in the LocationsBuilder for the constant case. DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0); @@ -2379,11 +2379,11 @@ void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) { if (!length.IsConstant() && !optimizations.GetCountIsSourceLength() && !optimizations.GetCountIsDestinationLength()) { - // If the length is negative, bail out. - __ Tbnz(WRegisterFrom(length), kWRegSize - 1, intrinsic_slow_path->GetEntryLabel()); - // If the length >= 128 then (currently) prefer native implementation. + // Merge the following two comparisons into one: + // If the length is negative, bail out (delegate to libcore's native implementation). + // If the length >= 128 then (currently) prefer native implementation. __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold); - __ B(intrinsic_slow_path->GetEntryLabel(), ge); + __ B(intrinsic_slow_path->GetEntryLabel(), hs); } // Validity checks: source. CheckSystemArrayCopyPosition(masm, diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index e4bef3446c..c8e3534164 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -303,7 +303,7 @@ static void GenNumberOfLeadingZeros(LocationSummary* locations, vixl32::Register in_reg_hi = HighRegisterFrom(in); vixl32::Label end; __ Clz(out, in_reg_hi); - __ Cbnz(in_reg_hi, &end); + __ CompareAndBranchIfNonZero(in_reg_hi, &end, /* far_target */ false); __ Clz(out, in_reg_lo); __ Add(out, out, 32); __ Bind(&end); @@ -345,7 +345,7 @@ static void GenNumberOfTrailingZeros(LocationSummary* locations, vixl32::Label end; __ Rbit(out, in_reg_lo); __ Clz(out, out); - __ Cbnz(in_reg_lo, &end); + __ CompareAndBranchIfNonZero(in_reg_lo, &end, /* far_target */ false); __ Rbit(out, in_reg_hi); __ Clz(out, out); __ Add(out, out, 32); @@ -518,7 +518,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) { void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); // Ignore upper 4B of long address. - __ Ldrsb(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Ldrsb(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) { @@ -528,7 +528,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); // Ignore upper 4B of long address. - __ Ldr(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Ldr(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) { @@ -545,9 +545,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) { vixl32::Register hi = HighRegisterFrom(invoke->GetLocations()->Out()); if (addr.Is(lo)) { __ Ldr(hi, MemOperand(addr, 4)); - __ Ldr(lo, addr); + __ Ldr(lo, MemOperand(addr)); } else { - __ Ldr(lo, addr); + __ Ldr(lo, MemOperand(addr)); __ Ldr(hi, MemOperand(addr, 4)); } } @@ -559,7 +559,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekShortNative(HInvoke* invok void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); // Ignore upper 4B of long address. - __ Ldrsh(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Ldrsh(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) { @@ -576,7 +576,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) { void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); - __ Strb(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Strb(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) { @@ -585,7 +585,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); - __ Str(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Str(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) { @@ -598,7 +598,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) { vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0)); // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor // exception. So we can't use ldrd as addr may be unaligned. - __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), addr); + __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr)); __ Str(HighRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr, 4)); } @@ -608,7 +608,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeShortNative(HInvoke* invok void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); - __ Strh(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Strh(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) { @@ -842,8 +842,8 @@ static void GenUnsafePut(LocationSummary* locations, __ Add(temp_reg, base, offset); vixl32::Label loop_head; __ Bind(&loop_head); - __ Ldrexd(temp_lo, temp_hi, temp_reg); - __ Strexd(temp_lo, value_lo, value_hi, temp_reg); + __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg)); + __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg)); __ Cmp(temp_lo, 0); __ B(ne, &loop_head); } else { @@ -1042,7 +1042,7 @@ static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARMVIXL* vixl32::Label loop_head; __ Bind(&loop_head); - __ Ldrex(tmp, tmp_ptr); + __ Ldrex(tmp, MemOperand(tmp_ptr)); __ Subs(tmp, tmp, expected); @@ -1052,7 +1052,7 @@ static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARMVIXL* CodeBufferCheckScope::kMaximumSize); __ itt(eq); - __ strex(eq, tmp, value, tmp_ptr); + __ strex(eq, tmp, value, MemOperand(tmp_ptr)); __ cmp(eq, tmp, 1); } @@ -1158,7 +1158,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { if (can_slow_path) { slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke); codegen_->AddSlowPath(slow_path); - __ Cbz(arg, slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel()); } // Reference equality check, return 0 if same reference. @@ -1191,7 +1191,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { } // Shorter string is empty? - __ Cbz(temp0, &end); + // Note that mirror::kUseStringCompression==true introduces lots of instructions, + // which makes &end label far away from this branch and makes it not 'CBZ-encodable'. + __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression); if (mirror::kUseStringCompression) { // Check if both strings using same compression style to use this comparison loop. @@ -1218,7 +1220,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { static_assert(IsAligned<8>(kObjectAlignment), "String data must be 8-byte aligned for unrolled CompareTo loop."); - const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + const unsigned char_size = Primitive::ComponentSize(Primitive::kPrimChar); DCHECK_EQ(char_size, 2u); UseScratchRegisterScope temps(assembler->GetVIXLAssembler()); @@ -1281,20 +1283,20 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`. // The compression flag is now in the highest bit of temp3, so let's play some tricks. - __ orr(temp3, temp3, 0xffu << 23); // uncompressed ? 0xff800000u : 0x7ff80000u - __ bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u) + __ Orr(temp3, temp3, 0xffu << 23); // uncompressed ? 0xff800000u : 0x7ff80000u + __ Bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u) __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u. __ Lsr(temp2, temp2, temp1); // Extract second character. __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu __ Lsr(out, temp_reg, temp1); // Extract first character. - __ and_(temp2, temp2, temp3); - __ and_(out, out, temp3); + __ And(temp2, temp2, temp3); + __ And(out, out, temp3); } else { - __ bic(temp1, temp1, 0xf); + __ Bic(temp1, temp1, 0xf); __ Lsr(temp2, temp2, temp1); __ Lsr(out, temp_reg, temp1); - __ movt(temp2, 0); - __ movt(out, 0); + __ Movt(temp2, 0); + __ Movt(out, 0); } __ Sub(out, out, temp2); @@ -1313,10 +1315,10 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { // need to treat as unsigned. Start by freeing the bit with an ADD and continue // further down by a LSRS+SBC which will flip the meaning of the flag but allow // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition. - __ add(temp0, temp0, temp0); // Unlike LSL, this ADD is always 16-bit. + __ Add(temp0, temp0, temp0); // Unlike LSL, this ADD is always 16-bit. // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer. - __ mov(temp1, str); - __ mov(temp2, arg); + __ Mov(temp1, str); + __ Mov(temp2, arg); __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag. { AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), @@ -1326,11 +1328,11 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { __ mov(cs, temp1, arg); // Preserves flags. __ mov(cs, temp2, str); // Preserves flags. } - __ sbc(temp0, temp0, 0); // Complete the move of the compression flag. + __ Sbc(temp0, temp0, 0); // Complete the move of the compression flag. // Adjust temp1 and temp2 from string pointers to data pointers. - __ add(temp1, temp1, value_offset); - __ add(temp2, temp2, value_offset); + __ Add(temp1, temp1, value_offset); + __ Add(temp2, temp2, value_offset); vixl32::Label different_compression_loop; vixl32::Label different_compression_diff; @@ -1340,7 +1342,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { __ Bind(&different_compression_loop); __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex)); __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex)); - __ cmp(temp_reg, temp3); + __ Cmp(temp_reg, temp3); __ B(ne, &different_compression_diff); __ Subs(temp0, temp0, 2); __ B(hi, &different_compression_loop); @@ -1414,7 +1416,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { StringEqualsOptimizations optimizations(invoke); if (!optimizations.GetArgumentNotNull()) { // Check if input is null, return false if it is. - __ Cbz(arg, &return_false); + __ CompareAndBranchIfZero(arg, &return_false, /* far_target */ false); } // Reference equality check, return true if same reference. @@ -1442,7 +1444,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { // 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); + __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false); // Assertions that must hold in order to compare strings 4 bytes at a time. DCHECK_ALIGNED(value_offset, 4); @@ -1467,7 +1469,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { __ Bind(&loop); __ Ldr(out, MemOperand(str, temp1)); __ Ldr(temp2, MemOperand(arg, temp1)); - __ Add(temp1, temp1, sizeof(uint32_t)); + __ Add(temp1, temp1, Operand::From(sizeof(uint32_t))); __ Cmp(out, temp2); __ B(ne, &return_false); // With string compression, we have compared 4 bytes, otherwise 2 chars. @@ -1718,7 +1720,7 @@ static void CheckPosition(ArmVIXLAssembler* assembler, } else if (length_is_input_length) { // The only way the copy can succeed is if pos is zero. vixl32::Register pos_reg = RegisterFrom(pos); - __ Cbnz(pos_reg, slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel()); } else { // Check that pos >= 0. vixl32::Register pos_reg = RegisterFrom(pos); @@ -1815,12 +1817,12 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { if (!optimizations.GetSourceIsNotNull()) { // Bail out if the source is null. - __ Cbz(src, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel()); } if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) { // Bail out if the destination is null. - __ Cbz(dest, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel()); } // If the length is negative, bail out. @@ -1865,13 +1867,13 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // /* HeapReference<Class> */ temp1 = temp1->component_type_ codegen_->GenerateFieldLoadWithBakerReadBarrier( invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false); - __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel()); // If heap poisoning is enabled, `temp1` has been unpoisoned // by the the previous call to GenerateFieldLoadWithBakerReadBarrier. // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_); __ Ldrh(temp1, MemOperand(temp1, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel()); } // /* HeapReference<Class> */ temp1 = dest->klass_ @@ -1889,13 +1891,13 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // /* HeapReference<Class> */ temp2 = temp1->component_type_ codegen_->GenerateFieldLoadWithBakerReadBarrier( invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false); - __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel()); // If heap poisoning is enabled, `temp2` has been unpoisoned // by the the previous call to GenerateFieldLoadWithBakerReadBarrier. // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_); __ Ldrh(temp2, MemOperand(temp2, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel()); } // For the same reason given earlier, `temp1` is not trashed by the @@ -1918,7 +1920,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // comparison with null below, and this reference is not // kept afterwards. __ Ldr(temp1, MemOperand(temp1, super_offset)); - __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel()); __ Bind(&do_copy); } else { __ B(ne, intrinsic_slow_path->GetEntryLabel()); @@ -1944,24 +1946,24 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // Bail out if the destination is not a non primitive array. // /* HeapReference<Class> */ temp3 = temp1->component_type_ __ Ldr(temp3, MemOperand(temp1, component_offset)); - __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel()); assembler->MaybeUnpoisonHeapReference(temp3); // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_); __ Ldrh(temp3, MemOperand(temp3, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel()); } if (!optimizations.GetSourceIsNonPrimitiveArray()) { // Bail out if the source is not a non primitive array. // /* HeapReference<Class> */ temp3 = temp2->component_type_ __ Ldr(temp3, MemOperand(temp2, component_offset)); - __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel()); assembler->MaybeUnpoisonHeapReference(temp3); // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_); __ Ldrh(temp3, MemOperand(temp3, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel()); } __ Cmp(temp1, temp2); @@ -1978,7 +1980,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // /* HeapReference<Class> */ temp1 = temp1->super_class_ __ Ldr(temp1, MemOperand(temp1, super_offset)); // No need to unpoison the result, we're comparing against null. - __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel()); __ Bind(&do_copy); } else { __ B(ne, intrinsic_slow_path->GetEntryLabel()); @@ -1994,7 +1996,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // /* HeapReference<Class> */ temp3 = temp1->component_type_ codegen_->GenerateFieldLoadWithBakerReadBarrier( invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false); - __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel()); // If heap poisoning is enabled, `temp3` has been unpoisoned // by the the previous call to GenerateFieldLoadWithBakerReadBarrier. } else { @@ -2003,13 +2005,13 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { assembler->MaybeUnpoisonHeapReference(temp1); // /* HeapReference<Class> */ temp3 = temp1->component_type_ __ Ldr(temp3, MemOperand(temp1, component_offset)); - __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel()); assembler->MaybeUnpoisonHeapReference(temp3); } // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_); __ Ldrh(temp3, MemOperand(temp3, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel()); } int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot); diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index 2a62643465..8c34dc6a86 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -63,7 +63,10 @@ class LICMTest : public CommonCompilerTest { return_->AddSuccessor(exit_); // Provide boiler-plate instructions. - parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); + parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + Primitive::kPrimNot); entry_->AddInstruction(parameter_); int_constant_ = graph_->GetIntConstant(42); float_constant_ = graph_->GetFloatConstant(42.0f); diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 15e605971e..edecf17f33 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -15,6 +15,8 @@ */ #include "load_store_elimination.h" + +#include "escape.h" #include "side_effects_analysis.h" #include <iostream> @@ -31,70 +33,12 @@ constexpr size_t kMaxNumberOfHeapLocations = 32; // whether it's a singleton, returned, etc. class ReferenceInfo : public ArenaObject<kArenaAllocMisc> { public: - ReferenceInfo(HInstruction* reference, size_t pos) : reference_(reference), position_(pos) { - is_singleton_ = true; - is_singleton_and_non_escaping_ = true; - if (!reference_->IsNewInstance() && !reference_->IsNewArray()) { - // For references not allocated in the method, don't assume anything. - is_singleton_ = false; - is_singleton_and_non_escaping_ = false; - return; - } - - // Visit all uses to determine if this reference can spread into the heap, - // a method call, etc. - for (const HUseListNode<HInstruction*>& use : reference_->GetUses()) { - HInstruction* user = use.GetUser(); - DCHECK(!user->IsNullCheck()) << "NullCheck should have been eliminated"; - if (user->IsBoundType()) { - // BoundType shouldn't normally be necessary for a NewInstance. - // Just be conservative for the uncommon cases. - is_singleton_ = false; - is_singleton_and_non_escaping_ = false; - return; - } - if (user->IsPhi() || user->IsSelect() || user->IsInvoke() || - (user->IsInstanceFieldSet() && (reference_ == user->InputAt(1))) || - (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(1))) || - (user->IsStaticFieldSet() && (reference_ == user->InputAt(1))) || - (user->IsUnresolvedStaticFieldSet() && (reference_ == user->InputAt(0))) || - (user->IsArraySet() && (reference_ == user->InputAt(2)))) { - // reference_ is merged to HPhi/HSelect, passed to a callee, or stored to heap. - // reference_ isn't the only name that can refer to its value anymore. - is_singleton_ = false; - is_singleton_and_non_escaping_ = false; - return; - } - if ((user->IsUnresolvedInstanceFieldGet() && (reference_ == user->InputAt(0))) || - (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(0)))) { - // The field is accessed in an unresolved way. We mark the object as a non-singleton - // to disable load/store optimizations on it. - // Note that we could optimize this case and still perform some optimizations until - // we hit the unresolved access, but disabling is the simplest. - is_singleton_ = false; - is_singleton_and_non_escaping_ = false; - return; - } - if (user->IsReturn()) { - is_singleton_and_non_escaping_ = false; - } - } - - if (!is_singleton_ || !is_singleton_and_non_escaping_) { - return; - } - - // Look at Environment uses and if it's for HDeoptimize, it's treated the same - // as a return which escapes at the end of executing the compiled code. We don't - // do store elimination for singletons that escape through HDeoptimize. - // Other Environment uses are fine since LSE is disabled for debuggable. - for (const HUseListNode<HEnvironment*>& use : reference_->GetEnvUses()) { - HEnvironment* user = use.GetUser(); - if (user->GetHolder()->IsDeoptimize()) { - is_singleton_and_non_escaping_ = false; - break; - } - } + ReferenceInfo(HInstruction* reference, size_t pos) + : reference_(reference), + position_(pos), + is_singleton_(true), + is_singleton_and_non_escaping_(true) { + CalculateEscape(reference_, nullptr, &is_singleton_, &is_singleton_and_non_escaping_); } HInstruction* GetReference() const { diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc index 7805a69a06..9a6b4935b2 100644 --- a/compiler/optimizing/loop_optimization_test.cc +++ b/compiler/optimizing/loop_optimization_test.cc @@ -48,7 +48,10 @@ class LoopOptimizationTest : public CommonCompilerTest { graph_->AddBlock(exit_block_); graph_->SetEntryBlock(entry_block_); graph_->SetExitBlock(exit_block_); - parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); + parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + Primitive::kPrimInt); entry_block_->AddInstruction(parameter_); return_block_->AddInstruction(new (&allocator_) HReturnVoid()); exit_block_->AddInstruction(new (&allocator_) HExit()); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e0c582a76d..eebc49c991 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -29,6 +29,7 @@ #include "base/stl_util.h" #include "base/transform_array_ref.h" #include "dex_file.h" +#include "dex_file_types.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" @@ -800,7 +801,7 @@ class TryCatchInformation : public ArenaObject<kArenaAllocTryCatchInfo> { } // Catch block information constructor. - TryCatchInformation(uint16_t catch_type_index, const DexFile& dex_file) + TryCatchInformation(dex::TypeIndex catch_type_index, const DexFile& dex_file) : try_entry_(nullptr), catch_dex_file_(&dex_file), catch_type_index_(catch_type_index) {} @@ -816,10 +817,10 @@ class TryCatchInformation : public ArenaObject<kArenaAllocTryCatchInfo> { bool IsCatchAllTypeIndex() const { DCHECK(IsCatchBlock()); - return catch_type_index_ == DexFile::kDexNoIndex16; + return !catch_type_index_.IsValid(); } - uint16_t GetCatchTypeIndex() const { + dex::TypeIndex GetCatchTypeIndex() const { DCHECK(IsCatchBlock()); return catch_type_index_; } @@ -836,7 +837,7 @@ class TryCatchInformation : public ArenaObject<kArenaAllocTryCatchInfo> { // Exception type information. Only set for catch blocks. const DexFile* catch_dex_file_; - const uint16_t catch_type_index_; + const dex::TypeIndex catch_type_index_; }; static constexpr size_t kNoLifetime = -1; @@ -3671,7 +3672,7 @@ class HNewInstance FINAL : public HExpression<2> { HNewInstance(HInstruction* cls, HCurrentMethod* current_method, uint32_t dex_pc, - uint16_t type_index, + dex::TypeIndex type_index, const DexFile& dex_file, bool needs_access_check, bool finalizable, @@ -3686,7 +3687,7 @@ class HNewInstance FINAL : public HExpression<2> { SetRawInputAt(1, current_method); } - uint16_t GetTypeIndex() const { return type_index_; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } // Calls runtime so needs an environment. @@ -3719,7 +3720,7 @@ class HNewInstance FINAL : public HExpression<2> { static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - const uint16_t type_index_; + const dex::TypeIndex type_index_; const DexFile& dex_file_; QuickEntrypointEnum entrypoint_; @@ -4265,7 +4266,7 @@ class HNewArray FINAL : public HExpression<2> { HNewArray(HInstruction* length, HCurrentMethod* current_method, uint32_t dex_pc, - uint16_t type_index, + dex::TypeIndex type_index, const DexFile& dex_file, QuickEntrypointEnum entrypoint) : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), @@ -4276,7 +4277,7 @@ class HNewArray FINAL : public HExpression<2> { SetRawInputAt(1, current_method); } - uint16_t GetTypeIndex() const { return type_index_; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } // Calls runtime so needs an environment. @@ -4292,7 +4293,7 @@ class HNewArray FINAL : public HExpression<2> { DECLARE_INSTRUCTION(NewArray); private: - const uint16_t type_index_; + const dex::TypeIndex type_index_; const DexFile& dex_file_; const QuickEntrypointEnum entrypoint_; @@ -4829,7 +4830,7 @@ class HRor FINAL : public HBinaryOperation { class HParameterValue FINAL : public HExpression<0> { public: HParameterValue(const DexFile& dex_file, - uint16_t type_index, + dex::TypeIndex type_index, uint8_t index, Primitive::Type parameter_type, bool is_this = false) @@ -4842,7 +4843,7 @@ class HParameterValue FINAL : public HExpression<0> { } const DexFile& GetDexFile() const { return dex_file_; } - uint16_t GetTypeIndex() const { return type_index_; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } uint8_t GetIndex() const { return index_; } bool IsThis() const { return GetPackedFlag<kFlagIsThis>(); } @@ -4860,7 +4861,7 @@ class HParameterValue FINAL : public HExpression<0> { "Too many packed fields."); const DexFile& dex_file_; - const uint16_t type_index_; + const dex::TypeIndex type_index_; // The index of this parameter in the parameters list. Must be less // than HGraph::number_of_in_vregs_. const uint8_t index_; @@ -5455,7 +5456,7 @@ class HLoadClass FINAL : public HInstruction { }; HLoadClass(HCurrentMethod* current_method, - uint16_t type_index, + dex::TypeIndex type_index, const DexFile& dex_file, bool is_referrers_class, uint32_t dex_pc, @@ -5487,7 +5488,7 @@ class HLoadClass FINAL : public HInstruction { void SetLoadKindWithTypeReference(LoadKind load_kind, const DexFile& dex_file, - uint32_t type_index) { + dex::TypeIndex type_index) { DCHECK(HasTypeReference(load_kind)); DCHECK(IsSameDexFile(dex_file_, dex_file)); DCHECK_EQ(type_index_, type_index); @@ -5511,7 +5512,7 @@ class HLoadClass FINAL : public HInstruction { bool InstructionDataEquals(const HInstruction* other) const; - size_t ComputeHashCode() const OVERRIDE { return type_index_; } + size_t ComputeHashCode() const OVERRIDE { return type_index_.index_; } bool CanBeNull() const OVERRIDE { return false; } @@ -5547,7 +5548,7 @@ class HLoadClass FINAL : public HInstruction { loaded_class_rti_ = rti; } - uint32_t GetTypeIndex() const { return type_index_; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } uint32_t GetDexCacheElementOffset() const; @@ -5630,7 +5631,7 @@ class HLoadClass FINAL : public HInstruction { // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative. HUserRecord<HInstruction*> special_input_; - const uint16_t type_index_; + const dex::TypeIndex type_index_; const DexFile& dex_file_; union { @@ -5690,7 +5691,10 @@ class HLoadString FINAL : public HInstruction { // all other types are unavailable. kDexCacheViaMethod, - kLast = kDexCacheViaMethod + // Load from the root table associated with the JIT compiled method. + kJitTableAddress, + + kLast = kJitTableAddress, }; HLoadString(HCurrentMethod* current_method, @@ -5748,7 +5752,8 @@ class HLoadString FINAL : public HInstruction { LoadKind load_kind = GetLoadKind(); if (load_kind == LoadKind::kBootImageLinkTimeAddress || load_kind == LoadKind::kBootImageLinkTimePcRelative || - load_kind == LoadKind::kBootImageAddress) { + load_kind == LoadKind::kBootImageAddress || + load_kind == LoadKind::kJitTableAddress) { return false; } return !IsInDexCache(); @@ -5801,7 +5806,8 @@ class HLoadString FINAL : public HInstruction { return load_kind == LoadKind::kBootImageLinkTimeAddress || load_kind == LoadKind::kBootImageLinkTimePcRelative || load_kind == LoadKind::kBssEntry || - load_kind == LoadKind::kDexCacheViaMethod; + load_kind == LoadKind::kDexCacheViaMethod || + load_kind == LoadKind::kJitTableAddress; } static bool HasAddress(LoadKind load_kind) { diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc index d4e2a58103..5d9a6528ca 100644 --- a/compiler/optimizing/nodes_test.cc +++ b/compiler/optimizing/nodes_test.cc @@ -35,7 +35,7 @@ TEST(Node, RemoveInstruction) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); entry->AddInstruction(new (&allocator) HGoto()); @@ -78,9 +78,9 @@ TEST(Node, InsertInstruction) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); HInstruction* parameter2 = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); entry->AddInstruction(new (&allocator) HExit()); @@ -106,7 +106,7 @@ TEST(Node, AddInstruction) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); ASSERT_FALSE(parameter->HasUses()); @@ -127,7 +127,7 @@ TEST(Node, ParentEnvironment) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); HInstruction* with_environment = new (&allocator) HNullCheck(parameter1, 0); entry->AddInstruction(parameter1); entry->AddInstruction(with_environment); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 7a930cce71..2382b728df 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -61,6 +61,7 @@ #include "debug/method_debug_info.h" #include "dex/verification_results.h" #include "dex/verified_method.h" +#include "dex_file_types.h" #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" @@ -117,6 +118,7 @@ class CodeVectorAllocator FINAL : public CodeAllocator { size_t GetSize() const { return size_; } const ArenaVector<uint8_t>& GetMemory() const { return memory_; } + uint8_t* GetData() { return memory_.data(); } private: ArenaVector<uint8_t> memory_; @@ -624,17 +626,14 @@ void OptimizingCompiler::RunArchOptimizations(InstructionSet instruction_set, UNUSED(codegen); // To avoid compilation error when compiling for svelte OptimizingCompilerStats* stats = compilation_stats_.get(); ArenaAllocator* arena = graph->GetArena(); -#ifdef ART_USE_VIXL_ARM_BACKEND - UNUSED(arena); - UNUSED(pass_observer); - UNUSED(stats); -#endif switch (instruction_set) { -#if defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_USE_VIXL_ARM_BACKEND) +#if defined(ART_ENABLE_CODEGEN_arm) case kThumb2: case kArm: { +#ifndef ART_USE_VIXL_ARM_BACKEND arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, codegen, stats); +#endif arm::InstructionSimplifierArm* simplifier = new (arena) arm::InstructionSimplifierArm(graph, stats); SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph); @@ -643,7 +642,9 @@ void OptimizingCompiler::RunArchOptimizations(InstructionSet instruction_set, simplifier, side_effects, gvn, +#ifndef ART_USE_VIXL_ARM_BACKEND fixups +#endif }; RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); break; @@ -948,7 +949,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, graph->SetArtMethod(method); ScopedObjectAccess soa(Thread::Current()); interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize()); - uint16_t type_index = method->GetDeclaringClass()->GetDexTypeIndex(); + 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 @@ -1125,7 +1126,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, jit::JitCodeCache* code_cache, ArtMethod* method, bool osr) { - StackHandleScope<2> hs(self); + StackHandleScope<3> hs(self); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( method->GetDeclaringClass()->GetClassLoader())); Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); @@ -1171,25 +1172,46 @@ bool OptimizingCompiler::JitCompile(Thread* self, } size_t stack_map_size = codegen->ComputeStackMapsSize(); - uint8_t* stack_map_data = code_cache->ReserveData(self, stack_map_size, method); - if (stack_map_data == nullptr) { + size_t number_of_roots = codegen->GetNumberOfJitRoots(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + // We allocate an object array to ensure the JIT roots that we will collect in EmitJitRoots + // will be visible by the GC between EmitLiterals and CommitCode. Once CommitCode is + // executed, this array is not needed. + 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) { + // Out of memory, just clear the exception to avoid any Java exception uncaught problems. + DCHECK(self->IsExceptionPending()); + self->ClearException(); + return false; + } + uint8_t* stack_map_data = nullptr; + uint8_t* roots_data = nullptr; + code_cache->ReserveData( + self, stack_map_size, number_of_roots, method, &stack_map_data, &roots_data); + if (stack_map_data == nullptr || roots_data == nullptr) { return false; } MaybeRecordStat(MethodCompilationStat::kCompiled); codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size), *code_item); + codegen->EmitJitRoots(code_allocator.GetData(), roots, roots_data, dex_cache); + const void* code = code_cache->CommitCode( self, method, stack_map_data, + roots_data, codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(), codegen->GetCoreSpillMask(), codegen->GetFpuSpillMask(), code_allocator.GetMemory().data(), code_allocator.GetSize(), - osr); + osr, + roots); if (code == nullptr) { - code_cache->ClearData(self, stack_map_data); + code_cache->ClearData(self, stack_map_data, roots_data); return false; } diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index 0db60882db..f9ac3a0f72 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -143,7 +143,7 @@ void PrepareForRegisterAllocation::VisitNewInstance(HNewInstance* instruction) { // - or the load class has only one use. if (instruction->IsFinalizable() || has_only_one_use || load_class->NeedsAccessCheck()) { instruction->SetEntrypoint(kQuickAllocObject); - instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex()), 0); + instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex().index_), 0); if (has_only_one_use) { // We've just removed the only use of the HLoadClass. Since we don't run DCE after this pass, // do it manually if possible. diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index d588deaace..c191c6651f 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -96,7 +96,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitBoundType(HBoundType* instr) OVERRIDE; void VisitNullCheck(HNullCheck* instr) OVERRIDE; void UpdateReferenceTypeInfo(HInstruction* instr, - uint16_t type_idx, + dex::TypeIndex type_idx, const DexFile& dex_file, bool is_exact); @@ -463,7 +463,7 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst } void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr, - uint16_t type_idx, + dex::TypeIndex type_idx, const DexFile& dex_file, bool is_exact) { DCHECK_EQ(instr->GetType(), Primitive::kPrimNot); @@ -484,7 +484,7 @@ void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) { static mirror::Class* GetClassFromDexCache(Thread* self, const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, Handle<mirror::DexCache> hint_dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { mirror::DexCache* dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache); diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 55ea99e592..559f40923b 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -20,6 +20,7 @@ #include "code_generator.h" #include "code_generator_x86.h" #include "dex_file.h" +#include "dex_file_types.h" #include "dex_instruction.h" #include "driver/compiler_options.h" #include "nodes.h" @@ -495,7 +496,7 @@ static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HBasicBlock* block = new (allocator) HBasicBlock(graph); @@ -658,7 +659,7 @@ static HGraph* BuildFieldReturn(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HBasicBlock* block = new (allocator) HBasicBlock(graph); @@ -742,7 +743,7 @@ static HGraph* BuildTwoSubs(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); entry->AddInstruction(parameter); HInstruction* constant1 = graph->GetIntConstant(1); @@ -821,9 +822,9 @@ static HGraph* BuildDiv(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* first = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); HInstruction* second = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); entry->AddInstruction(first); entry->AddInstruction(second); @@ -883,13 +884,13 @@ TEST_F(RegisterAllocatorTest, SpillInactive) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* one = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); HInstruction* two = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); HInstruction* three = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); HInstruction* four = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); entry->AddInstruction(one); entry->AddInstruction(two); entry->AddInstruction(three); diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 63e4ca674e..a127708ab0 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -147,7 +147,7 @@ void HSharpening::ProcessLoadClass(HLoadClass* load_class) { DCHECK(!load_class->IsInBootImage()) << "HLoadClass should not be optimized before sharpening."; const DexFile& dex_file = load_class->GetDexFile(); - uint32_t type_index = load_class->GetTypeIndex(); + dex::TypeIndex type_index = load_class->GetTypeIndex(); bool is_in_dex_cache = false; bool is_in_boot_image = false; @@ -197,7 +197,7 @@ void HSharpening::ProcessLoadClass(HLoadClass* load_class) { // inlined frames are used correctly for OOM stack trace. // TODO: Write a test for this. Bug: 29416588 desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress; - void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index]; + void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index.index_]; address = reinterpret_cast64<uint64_t>(dex_cache_element_address); } // AOT app compilation. Check if the class is in the boot image. @@ -281,7 +281,8 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); if (codegen_->GetCompilerOptions().IsBootImage()) { - // Compiling boot image. Resolve the string and allocate it if needed. + // Compiling boot image. Resolve the string and allocate it if needed, to ensure + // the string will be added to the boot image. DCHECK(!runtime->UseJitCompilation()); mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache); CHECK(string != nullptr); @@ -297,10 +298,14 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { } else if (runtime->UseJitCompilation()) { // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); - mirror::String* string = dex_cache->GetResolvedString(string_index); - if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { - desired_load_kind = HLoadString::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(string); + mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache); + if (string != nullptr) { + if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { + desired_load_kind = HLoadString::LoadKind::kBootImageAddress; + address = reinterpret_cast64<uint64_t>(string); + } else { + desired_load_kind = HLoadString::LoadKind::kJitTableAddress; + } } } else { // AOT app compilation. Try to lookup the string without allocating if not found. @@ -322,6 +327,7 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: case HLoadString::LoadKind::kBssEntry: case HLoadString::LoadKind::kDexCacheViaMethod: + case HLoadString::LoadKind::kJitTableAddress: load_string->SetLoadKindWithStringReference(load_kind, dex_file, string_index); break; case HLoadString::LoadKind::kBootImageAddress: |