diff options
64 files changed, 1850 insertions, 309 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index a4434872da..288bdddfe7 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -295,10 +295,6 @@ else art_cflags += -DIMT_SIZE=64 endif -ifeq ($(ART_USE_OPTIMIZING_COMPILER),true) - art_cflags += -DART_USE_OPTIMIZING_COMPILER=1 -endif - ifeq ($(ART_HEAP_POISONING),true) art_cflags += -DART_HEAP_POISONING=1 art_asflags += -DART_HEAP_POISONING=1 diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 6e17ed38d6..d71ae29f01 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -52,18 +52,12 @@ define create-core-oat-host-rules core_pic_infix := core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY) - # With the optimizing compiler, we want to rerun dex2oat whenever there is - # a dex2oat change to catch regressions early. - ifeq ($(ART_USE_OPTIMIZING_COMPILER), true) - core_dex2oat_dependency := $(DEX2OAT) - endif - ifeq ($(1),default) core_compile_options += --compiler-backend=Quick endif ifeq ($(1),optimizing) core_compile_options += --compiler-backend=Optimizing - core_dex2oat_dependency := $(DEX2OAT) + core_dex2oat_dependency += $(DEX2OAT) core_infix := -optimizing endif ifeq ($(1),interpreter) @@ -178,18 +172,12 @@ define create-core-oat-target-rules core_pic_infix := core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY) - # With the optimizing compiler, we want to rerun dex2oat whenever there is - # a dex2oat change to catch regressions early. - ifeq ($(ART_USE_OPTIMIZING_COMPILER), true) - core_dex2oat_dependency := $(DEX2OAT) - endif - ifeq ($(1),default) core_compile_options += --compiler-backend=Quick endif ifeq ($(1),optimizing) core_compile_options += --compiler-backend=Optimizing - core_dex2oat_dependency := $(DEX2OAT) + core_dex2oat_dependency += $(DEX2OAT) core_infix := -optimizing endif ifeq ($(1),interpreter) diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index dc2bc5c3f4..67b4428324 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -92,7 +92,7 @@ class CommonCompilerTest : public CommonRuntimeTest { void UnreserveImageSpace(); - Compiler::Kind compiler_kind_ = kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick; + Compiler::Kind compiler_kind_ = Compiler::kOptimizing; std::unique_ptr<CompilerOptions> compiler_options_; std::unique_ptr<VerificationResults> verification_results_; std::unique_ptr<DexFileToMethodInlinerMap> method_inliner_map_; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 1f114cf336..3c5c2fe010 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -148,7 +148,7 @@ void Mir2Lir::CallRuntimeHelperImmRegLocation(QuickEntrypointEnum trampoline, in if (arg1.wide == 0) { LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1)); } else { - RegStorage r_tmp = TargetReg(cu_->instruction_set == kMips ? kArg2 : kArg1, kWide); + RegStorage r_tmp = TargetReg(kArg2, kWide); LoadValueDirectWideFixed(arg1, r_tmp); } LoadConstant(TargetReg(kArg0, kNotWide), arg0); diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index 939bf40564..6ed666b9f7 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -535,37 +535,76 @@ void MIRGraph::DoDFSPreOrderSSARename(BasicBlock* block) { if (block->visited || block->hidden) { return; } - block->visited = true; - /* Process this block */ - DoSSAConversion(block); + typedef struct { + BasicBlock* bb; + int32_t* ssa_map; + } BasicBlockInfo; + BasicBlockInfo temp; - /* Save SSA map snapshot */ ScopedArenaAllocator allocator(&cu_->arena_stack); + ScopedArenaVector<BasicBlockInfo> bi_stack(allocator.Adapter()); + ScopedArenaVector<BasicBlock*> succ_stack(allocator.Adapter()); + uint32_t num_vregs = GetNumOfCodeAndTempVRs(); - int32_t* saved_ssa_map = allocator.AllocArray<int32_t>(num_vregs, kArenaAllocDalvikToSSAMap); - size_t map_size = sizeof(saved_ssa_map[0]) * num_vregs; - memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size); - - if (block->fall_through != NullBasicBlockId) { - DoDFSPreOrderSSARename(GetBasicBlock(block->fall_through)); - /* Restore SSA map snapshot */ - memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size); - } - if (block->taken != NullBasicBlockId) { - DoDFSPreOrderSSARename(GetBasicBlock(block->taken)); - /* Restore SSA map snapshot */ - memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size); - } - if (block->successor_block_list_type != kNotUsed) { - for (SuccessorBlockInfo* successor_block_info : block->successor_blocks) { - BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block); - DoDFSPreOrderSSARename(succ_bb); - /* Restore SSA map snapshot */ - memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size); + size_t map_size = sizeof(int32_t) * num_vregs; + temp.bb = block; + temp.ssa_map = vreg_to_ssa_map_; + bi_stack.push_back(temp); + + while (!bi_stack.empty()) { + temp = bi_stack.back(); + bi_stack.pop_back(); + BasicBlock* b = temp.bb; + + if (b->visited || b->hidden) { + continue; + } + b->visited = true; + + /* Restore SSA map snapshot, except for the first block */ + if (b != block) { + memcpy(vreg_to_ssa_map_, temp.ssa_map, map_size); + } + + /* Process this block */ + DoSSAConversion(b); + + /* If there are no successor, taken, and fall through blocks, continue */ + if (b->successor_block_list_type == kNotUsed && + b->taken == NullBasicBlockId && + b->fall_through == NullBasicBlockId) { + continue; + } + + /* Save SSA map snapshot */ + int32_t* saved_ssa_map = + allocator.AllocArray<int32_t>(num_vregs, kArenaAllocDalvikToSSAMap); + memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size); + + if (b->successor_block_list_type != kNotUsed) { + for (SuccessorBlockInfo* successor_block_info : b->successor_blocks) { + BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block); + succ_stack.push_back(succ_bb); + } + while (!succ_stack.empty()) { + temp.bb = succ_stack.back(); + succ_stack.pop_back(); + temp.ssa_map = saved_ssa_map; + bi_stack.push_back(temp); + } + } + if (b->taken != NullBasicBlockId) { + temp.bb = GetBasicBlock(b->taken); + temp.ssa_map = saved_ssa_map; + bi_stack.push_back(temp); + } + if (b->fall_through != NullBasicBlockId) { + temp.bb = GetBasicBlock(b->fall_through); + temp.ssa_map = saved_ssa_map; + bi_stack.push_back(temp); } } - return; } } // namespace art diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index cb36f62235..ebbfb14190 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -1186,6 +1186,12 @@ void HGraphBuilder::PotentiallySimplifyFakeString(uint16_t original_dex_register } } +static Primitive::Type GetFieldAccessType(const DexFile& dex_file, uint16_t field_index) { + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); + return Primitive::GetType(type[0]); +} + bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put) { @@ -1205,44 +1211,61 @@ bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction, ArtField* resolved_field = compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa); - if (resolved_field == nullptr) { - MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField); - return false; - } - - Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType(); HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot, dex_pc); - current_block_->AddInstruction(new (arena_) HNullCheck(object, dex_pc)); + HInstruction* null_check = new (arena_) HNullCheck(object, dex_pc); + current_block_->AddInstruction(null_check); + + Primitive::Type field_type = (resolved_field == nullptr) + ? GetFieldAccessType(*dex_file_, field_index) + : resolved_field->GetTypeAsPrimitiveType(); if (is_put) { Temporaries temps(graph_); - HInstruction* null_check = current_block_->GetLastInstruction(); // We need one temporary for the null check. temps.Add(null_check); HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc); - current_block_->AddInstruction(new (arena_) HInstanceFieldSet( - null_check, - value, - field_type, - resolved_field->GetOffset(), - resolved_field->IsVolatile(), - field_index, - *dex_file_, - dex_compilation_unit_->GetDexCache(), - dex_pc)); + HInstruction* field_set = nullptr; + if (resolved_field == nullptr) { + MaybeRecordStat(MethodCompilationStat::kUnresolvedField); + field_set = new (arena_) HUnresolvedInstanceFieldSet(null_check, + value, + field_type, + field_index, + dex_pc); + } else { + field_set = new (arena_) HInstanceFieldSet(null_check, + value, + field_type, + resolved_field->GetOffset(), + resolved_field->IsVolatile(), + field_index, + *dex_file_, + dex_compilation_unit_->GetDexCache(), + dex_pc); + } + current_block_->AddInstruction(field_set); } else { - current_block_->AddInstruction(new (arena_) HInstanceFieldGet( - current_block_->GetLastInstruction(), - field_type, - resolved_field->GetOffset(), - resolved_field->IsVolatile(), - field_index, - *dex_file_, - dex_compilation_unit_->GetDexCache(), - dex_pc)); - - UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc); + HInstruction* field_get = nullptr; + if (resolved_field == nullptr) { + MaybeRecordStat(MethodCompilationStat::kUnresolvedField); + field_get = new (arena_) HUnresolvedInstanceFieldGet(null_check, + field_type, + field_index, + dex_pc); + } else { + field_get = new (arena_) HInstanceFieldGet(null_check, + field_type, + resolved_field->GetOffset(), + resolved_field->IsVolatile(), + field_index, + *dex_file_, + dex_compilation_unit_->GetDexCache(), + dex_pc); + } + current_block_->AddInstruction(field_get); + UpdateLocal(source_or_dest_reg, field_get, dex_pc); } + return true; } @@ -1282,6 +1305,23 @@ bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const { return outer_class.Get() == cls.Get(); } +void HGraphBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction, + uint32_t dex_pc, + bool is_put, + Primitive::Type field_type) { + uint32_t source_or_dest_reg = instruction.VRegA_21c(); + uint16_t field_index = instruction.VRegB_21c(); + + if (is_put) { + HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc); + current_block_->AddInstruction( + new (arena_) HUnresolvedStaticFieldSet(value, field_type, field_index, dex_pc)); + } else { + current_block_->AddInstruction( + new (arena_) HUnresolvedStaticFieldGet(field_type, field_index, dex_pc)); + UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc); + } +} bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put) { @@ -1299,10 +1339,13 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true); if (resolved_field == nullptr) { - MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField); - return false; + MaybeRecordStat(MethodCompilationStat::kUnresolvedField); + Primitive::Type field_type = GetFieldAccessType(*dex_file_, field_index); + BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type); + return true; } + Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType(); const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file))); @@ -1317,6 +1360,7 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, // The compiler driver cannot currently understand multiple dex caches involved. Just bailout. return false; } else { + // TODO: This is rather expensive. Perf it and cache the results if needed. std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField( outer_dex_cache.Get(), GetCompilingClass(), @@ -1325,7 +1369,9 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, &storage_index); bool can_easily_access = is_put ? pair.second : pair.first; if (!can_easily_access) { - return false; + MaybeRecordStat(MethodCompilationStat::kUnresolvedFieldNotAFastAccess); + BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type); + return true; } } @@ -1346,8 +1392,6 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, cls = new (arena_) HClinitCheck(constant, dex_pc); current_block_->AddInstruction(cls); } - - Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType(); if (is_put) { // We need to keep the class alive before loading the value. Temporaries temps(graph_); diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 4c8e3d0442..b2dc24169e 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -187,6 +187,10 @@ class HGraphBuilder : public ValueObject { // Builds an instance field access node and returns whether the instruction is supported. bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put); + void BuildUnresolvedStaticFieldAccess(const Instruction& instruction, + uint32_t dex_pc, + bool is_put, + Primitive::Type field_type); // Builds a static field access node and returns whether the instruction is supported. bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index be05691741..8254277f96 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -413,6 +413,130 @@ void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invok InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); } +void CodeGenerator::CreateUnresolvedFieldLocationSummary( + HInstruction* field_access, + Primitive::Type field_type, + const FieldAccessCallingConvention& calling_convention) { + bool is_instance = field_access->IsUnresolvedInstanceFieldGet() + || field_access->IsUnresolvedInstanceFieldSet(); + bool is_get = field_access->IsUnresolvedInstanceFieldGet() + || field_access->IsUnresolvedStaticFieldGet(); + + ArenaAllocator* allocator = field_access->GetBlock()->GetGraph()->GetArena(); + LocationSummary* locations = + new (allocator) LocationSummary(field_access, LocationSummary::kCall); + + locations->AddTemp(calling_convention.GetFieldIndexLocation()); + + if (is_instance) { + // Add the `this` object for instance field accesses. + locations->SetInAt(0, calling_convention.GetObjectLocation()); + } + + // Note that pSetXXStatic/pGetXXStatic always takes/returns an int or int64 + // regardless of the the type. Because of that we forced to special case + // the access to floating point values. + if (is_get) { + if (Primitive::IsFloatingPointType(field_type)) { + // The return value will be stored in regular registers while register + // allocator expects it in a floating point register. + // Note We don't need to request additional temps because the return + // register(s) are already blocked due the call and they may overlap with + // the input or field index. + // The transfer between the two will be done at codegen level. + locations->SetOut(calling_convention.GetFpuLocation(field_type)); + } else { + locations->SetOut(calling_convention.GetReturnLocation(field_type)); + } + } else { + size_t set_index = is_instance ? 1 : 0; + if (Primitive::IsFloatingPointType(field_type)) { + // The set value comes from a float location while the calling convention + // expects it in a regular register location. Allocate a temp for it and + // make the transfer at codegen. + AddLocationAsTemp(calling_convention.GetSetValueLocation(field_type, is_instance), locations); + locations->SetInAt(set_index, calling_convention.GetFpuLocation(field_type)); + } else { + locations->SetInAt(set_index, + calling_convention.GetSetValueLocation(field_type, is_instance)); + } + } +} + +void CodeGenerator::GenerateUnresolvedFieldAccess( + HInstruction* field_access, + Primitive::Type field_type, + uint32_t field_index, + uint32_t dex_pc, + const FieldAccessCallingConvention& calling_convention) { + LocationSummary* locations = field_access->GetLocations(); + + MoveConstant(locations->GetTemp(0), field_index); + + bool is_instance = field_access->IsUnresolvedInstanceFieldGet() + || field_access->IsUnresolvedInstanceFieldSet(); + bool is_get = field_access->IsUnresolvedInstanceFieldGet() + || field_access->IsUnresolvedStaticFieldGet(); + + if (!is_get && Primitive::IsFloatingPointType(field_type)) { + // Copy the float value to be set into the calling convention register. + // Note that using directly the temp location is problematic as we don't + // support temp register pairs. To avoid boilerplate conversion code, use + // the location from the calling convention. + MoveLocation(calling_convention.GetSetValueLocation(field_type, is_instance), + locations->InAt(is_instance ? 1 : 0), + (Primitive::Is64BitType(field_type) ? Primitive::kPrimLong : Primitive::kPrimInt)); + } + + QuickEntrypointEnum entrypoint = kQuickSet8Static; // Initialize to anything to avoid warnings. + switch (field_type) { + case Primitive::kPrimBoolean: + entrypoint = is_instance + ? (is_get ? kQuickGetBooleanInstance : kQuickSet8Instance) + : (is_get ? kQuickGetBooleanStatic : kQuickSet8Static); + break; + case Primitive::kPrimByte: + entrypoint = is_instance + ? (is_get ? kQuickGetByteInstance : kQuickSet8Instance) + : (is_get ? kQuickGetByteStatic : kQuickSet8Static); + break; + case Primitive::kPrimShort: + entrypoint = is_instance + ? (is_get ? kQuickGetShortInstance : kQuickSet16Instance) + : (is_get ? kQuickGetShortStatic : kQuickSet16Static); + break; + case Primitive::kPrimChar: + entrypoint = is_instance + ? (is_get ? kQuickGetCharInstance : kQuickSet16Instance) + : (is_get ? kQuickGetCharStatic : kQuickSet16Static); + break; + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + entrypoint = is_instance + ? (is_get ? kQuickGet32Instance : kQuickSet32Instance) + : (is_get ? kQuickGet32Static : kQuickSet32Static); + break; + case Primitive::kPrimNot: + entrypoint = is_instance + ? (is_get ? kQuickGetObjInstance : kQuickSetObjInstance) + : (is_get ? kQuickGetObjStatic : kQuickSetObjStatic); + break; + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + entrypoint = is_instance + ? (is_get ? kQuickGet64Instance : kQuickSet64Instance) + : (is_get ? kQuickGet64Static : kQuickSet64Static); + break; + default: + LOG(FATAL) << "Invalid type " << field_type; + } + InvokeRuntime(entrypoint, field_access, dex_pc, nullptr); + + if (is_get && Primitive::IsFloatingPointType(field_type)) { + MoveLocation(locations->Out(), calling_convention.GetReturnLocation(field_type), field_type); + } +} + void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const { // The DCHECKS below check that a register is not specified twice in // the summary. The out location can overlap with an input, so we need diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 5da0e59187..a3ebc43f11 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -143,6 +143,22 @@ class InvokeDexCallingConventionVisitor { DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor); }; +class FieldAccessCallingConvention { + public: + virtual Location GetObjectLocation() const = 0; + virtual Location GetFieldIndexLocation() const = 0; + virtual Location GetReturnLocation(Primitive::Type type) const = 0; + virtual Location GetSetValueLocation(Primitive::Type type, bool is_instance) const = 0; + virtual Location GetFpuLocation(Primitive::Type type) const = 0; + virtual ~FieldAccessCallingConvention() {} + + protected: + FieldAccessCallingConvention() {} + + private: + DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConvention); +}; + class CodeGenerator { public: // Compiles the graph to executable instructions. Returns whether the compilation @@ -177,6 +193,9 @@ class CodeGenerator { virtual void Bind(HBasicBlock* block) = 0; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0; virtual void MoveConstant(Location destination, int32_t value) = 0; + virtual void MoveLocation(Location dst, Location src, Primitive::Type dst_type) = 0; + virtual void AddLocationAsTemp(Location location, LocationSummary* locations) = 0; + virtual Assembler* GetAssembler() = 0; virtual const Assembler& GetAssembler() const = 0; virtual size_t GetWordSize() const = 0; @@ -385,6 +404,18 @@ class CodeGenerator { void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke); + void CreateUnresolvedFieldLocationSummary( + HInstruction* field_access, + Primitive::Type field_type, + const FieldAccessCallingConvention& calling_convention); + + void GenerateUnresolvedFieldAccess( + HInstruction* field_access, + Primitive::Type field_type, + uint32_t field_index, + uint32_t dex_pc, + const FieldAccessCallingConvention& calling_convention); + void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; } DisassemblyInformation* GetDisassemblyInformation() const { return disasm_info_; } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index a7dbb53382..cf7f5f4e08 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -906,6 +906,10 @@ void CodeGeneratorARM::Move64(Location destination, Location source) { Primitive::kPrimInt); } else if (source.IsFpuRegister()) { UNIMPLEMENTED(FATAL); + } else if (source.IsFpuRegisterPair()) { + __ vmovrrd(destination.AsRegisterPairLow<Register>(), + destination.AsRegisterPairHigh<Register>(), + FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); } else { DCHECK(source.IsDoubleStackSlot()); DCHECK(ExpectedPairLayout(destination)); @@ -917,6 +921,10 @@ void CodeGeneratorARM::Move64(Location destination, Location source) { __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), SP, source.GetStackIndex()); + } else if (source.IsRegisterPair()) { + __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), + source.AsRegisterPairLow<Register>(), + source.AsRegisterPairHigh<Register>()); } else { UNIMPLEMENTED(FATAL); } @@ -1038,6 +1046,25 @@ void CodeGeneratorARM::MoveConstant(Location location, int32_t value) { __ LoadImmediate(location.AsRegister<Register>(), value); } +void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) { + if (Primitive::Is64BitType(dst_type)) { + Move64(dst, src); + } else { + Move32(dst, src); + } +} + +void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) { + if (location.IsRegister()) { + locations->AddTemp(location); + } else if (location.IsRegisterPair()) { + locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>())); + locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>())); + } else { + UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; + } +} + void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint, HInstruction* instruction, uint32_t dex_pc, @@ -3605,6 +3632,74 @@ void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instructi HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); } +void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionARM calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionARM calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionARM calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionARM calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderARM::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionARM calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionARM calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderARM::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionARM calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionARM calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() ? LocationSummary::kCallOnSlowPath diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 111112e9b2..16d1d383b4 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -96,6 +96,38 @@ class InvokeDexCallingConventionVisitorARM : public InvokeDexCallingConventionVi DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorARM); }; +class FieldAccessCallingConventionARM : public FieldAccessCallingConvention { + public: + FieldAccessCallingConventionARM() {} + + Location GetObjectLocation() const OVERRIDE { + return Location::RegisterLocation(R1); + } + Location GetFieldIndexLocation() const OVERRIDE { + return Location::RegisterLocation(R0); + } + Location GetReturnLocation(Primitive::Type type) const OVERRIDE { + return Primitive::Is64BitType(type) + ? Location::RegisterPairLocation(R0, R1) + : Location::RegisterLocation(R0); + } + Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE { + return Primitive::Is64BitType(type) + ? Location::RegisterPairLocation(R2, R3) + : (is_instance + ? Location::RegisterLocation(R2) + : Location::RegisterLocation(R1)); + } + Location GetFpuLocation(Primitive::Type type) const OVERRIDE { + return Primitive::Is64BitType(type) + ? Location::FpuRegisterPairLocation(S0, S1) + : Location::FpuRegisterLocation(S0); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionARM); +}; + class ParallelMoveResolverARM : public ParallelMoveResolverWithSwap { public: ParallelMoveResolverARM(ArenaAllocator* allocator, CodeGeneratorARM* codegen) @@ -225,6 +257,9 @@ class CodeGeneratorARM : public CodeGenerator { void Bind(HBasicBlock* block) OVERRIDE; void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; void MoveConstant(Location destination, int32_t value) OVERRIDE; + void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE; + void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE; + size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 78ecfdec10..af5bbaae3d 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -19,7 +19,6 @@ #include "arch/arm64/instruction_set_features_arm64.h" #include "art_method.h" #include "code_generator_utils.h" -#include "common_arm64.h" #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" @@ -666,7 +665,7 @@ void ParallelMoveResolverARM64::FreeScratchLocation(Location loc) { void ParallelMoveResolverARM64::EmitMove(size_t index) { DCHECK_LT(index, moves_.size()); MoveOperands* move = moves_[index]; - codegen_->MoveLocation(move->GetDestination(), move->GetSource()); + codegen_->MoveLocation(move->GetDestination(), move->GetSource(), Primitive::kPrimVoid); } void CodeGeneratorARM64::GenerateFrameEntry() { @@ -750,7 +749,9 @@ void CodeGeneratorARM64::Move(HInstruction* instruction, } if (instruction->IsCurrentMethod()) { - MoveLocation(location, Location::DoubleStackSlot(kCurrentMethodStackOffset)); + MoveLocation(location, + Location::DoubleStackSlot(kCurrentMethodStackOffset), + Primitive::kPrimVoid); } else if (locations != nullptr && locations->Out().Equals(location)) { return; } else if (instruction->IsIntConstant() @@ -793,6 +794,14 @@ void CodeGeneratorARM64::MoveConstant(Location location, int32_t value) { __ Mov(RegisterFrom(location, Primitive::kPrimInt), value); } +void CodeGeneratorARM64::AddLocationAsTemp(Location location, LocationSummary* locations) { + if (location.IsRegister()) { + locations->AddTemp(location); + } else { + UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; + } +} + Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const { Primitive::Type type = load->GetType(); @@ -943,7 +952,9 @@ static bool CoherentConstantAndType(Location constant, Primitive::Type type) { (cst->IsDoubleConstant() && type == Primitive::kPrimDouble); } -void CodeGeneratorARM64::MoveLocation(Location destination, Location source, Primitive::Type type) { +void CodeGeneratorARM64::MoveLocation(Location destination, + Location source, + Primitive::Type dst_type) { if (source.Equals(destination)) { return; } @@ -952,7 +963,7 @@ void CodeGeneratorARM64::MoveLocation(Location destination, Location source, Pri // locations. When moving from and to a register, the argument type can be // used to generate 32bit instead of 64bit moves. In debug mode we also // checks the coherency of the locations and the type. - bool unspecified_type = (type == Primitive::kPrimVoid); + bool unspecified_type = (dst_type == Primitive::kPrimVoid); if (destination.IsRegister() || destination.IsFpuRegister()) { if (unspecified_type) { @@ -962,30 +973,44 @@ void CodeGeneratorARM64::MoveLocation(Location destination, Location source, Pri || src_cst->IsFloatConstant() || src_cst->IsNullConstant()))) { // For stack slots and 32bit constants, a 64bit type is appropriate. - type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat; + dst_type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat; } else { // If the source is a double stack slot or a 64bit constant, a 64bit // type is appropriate. Else the source is a register, and since the // type has not been specified, we chose a 64bit type to force a 64bit // move. - type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble; + dst_type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble; } } - DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(type)) || - (destination.IsRegister() && !Primitive::IsFloatingPointType(type))); - CPURegister dst = CPURegisterFrom(destination, type); + DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(dst_type)) || + (destination.IsRegister() && !Primitive::IsFloatingPointType(dst_type))); + CPURegister dst = CPURegisterFrom(destination, dst_type); if (source.IsStackSlot() || source.IsDoubleStackSlot()) { DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot()); __ Ldr(dst, StackOperandFrom(source)); } else if (source.IsConstant()) { - DCHECK(CoherentConstantAndType(source, type)); + DCHECK(CoherentConstantAndType(source, dst_type)); MoveConstant(dst, source.GetConstant()); + } else if (source.IsRegister()) { + if (destination.IsRegister()) { + __ Mov(Register(dst), RegisterFrom(source, dst_type)); + } else { + DCHECK(destination.IsFpuRegister()); + Primitive::Type source_type = Primitive::Is64BitType(dst_type) + ? Primitive::kPrimLong + : Primitive::kPrimInt; + __ Fmov(FPRegisterFrom(destination, dst_type), RegisterFrom(source, source_type)); + } } else { + DCHECK(source.IsFpuRegister()); if (destination.IsRegister()) { - __ Mov(Register(dst), RegisterFrom(source, type)); + Primitive::Type source_type = Primitive::Is64BitType(dst_type) + ? Primitive::kPrimDouble + : Primitive::kPrimFloat; + __ Fmov(RegisterFrom(destination, dst_type), FPRegisterFrom(source, source_type)); } else { DCHECK(destination.IsFpuRegister()); - __ Fmov(FPRegister(dst), FPRegisterFrom(source, type)); + __ Fmov(FPRegister(dst), FPRegisterFrom(source, dst_type)); } } } else { // The destination is not a register. It must be a stack slot. @@ -993,16 +1018,17 @@ void CodeGeneratorARM64::MoveLocation(Location destination, Location source, Pri if (source.IsRegister() || source.IsFpuRegister()) { if (unspecified_type) { if (source.IsRegister()) { - type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong; + dst_type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong; } else { - type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble; + dst_type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble; } } - DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(type)) && - (source.IsFpuRegister() == Primitive::IsFloatingPointType(type))); - __ Str(CPURegisterFrom(source, type), StackOperandFrom(destination)); + DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(dst_type)) && + (source.IsFpuRegister() == Primitive::IsFloatingPointType(dst_type))); + __ Str(CPURegisterFrom(source, dst_type), StackOperandFrom(destination)); } else if (source.IsConstant()) { - DCHECK(unspecified_type || CoherentConstantAndType(source, type)) << source << " " << type; + DCHECK(unspecified_type || CoherentConstantAndType(source, dst_type)) + << source << " " << dst_type; UseScratchRegisterScope temps(GetVIXLAssembler()); HConstant* src_cst = source.GetConstant(); CPURegister temp; @@ -3508,6 +3534,74 @@ void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruc HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); } +void LocationsBuilderARM64::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionARM64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionARM64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderARM64::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionARM64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionARM64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderARM64::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionARM64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionARM64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderARM64::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionARM64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionARM64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) { new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 7178081bf8..a068b48797 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ #include "code_generator.h" +#include "common_arm64.h" #include "dex/compiler_enums.h" #include "driver/compiler_options.h" #include "nodes.h" @@ -141,6 +142,34 @@ class InvokeDexCallingConventionVisitorARM64 : public InvokeDexCallingConvention DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorARM64); }; +class FieldAccessCallingConventionARM64 : public FieldAccessCallingConvention { + public: + FieldAccessCallingConventionARM64() {} + + Location GetObjectLocation() const OVERRIDE { + return helpers::LocationFrom(vixl::x1); + } + Location GetFieldIndexLocation() const OVERRIDE { + return helpers::LocationFrom(vixl::x0); + } + Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE { + return helpers::LocationFrom(vixl::x0); + } + Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE { + return Primitive::Is64BitType(type) + ? helpers::LocationFrom(vixl::x2) + : (is_instance + ? helpers::LocationFrom(vixl::x2) + : helpers::LocationFrom(vixl::x1)); + } + Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE { + return helpers::LocationFrom(vixl::d0); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionARM64); +}; + class InstructionCodeGeneratorARM64 : public HGraphVisitor { public: InstructionCodeGeneratorARM64(HGraph* graph, CodeGeneratorARM64* codegen); @@ -334,10 +363,9 @@ class CodeGeneratorARM64 : public CodeGenerator { // Code generation helpers. void MoveConstant(vixl::CPURegister destination, HConstant* constant); void MoveConstant(Location destination, int32_t value) OVERRIDE; - // The type is optional. When specified it must be coherent with the - // locations, and is used for optimisation and debugging. - void MoveLocation(Location destination, Location source, - Primitive::Type type = Primitive::kPrimVoid); + void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE; + void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE; + void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src); void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst); void LoadAcquire(HInstruction* instruction, vixl::CPURegister dst, const vixl::MemOperand& src); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index ad0a39c753..e95d283c1a 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -617,7 +617,7 @@ void CodeGeneratorMIPS64::Bind(HBasicBlock* block) { void CodeGeneratorMIPS64::MoveLocation(Location destination, Location source, - Primitive::Type type) { + Primitive::Type dst_type) { if (source.Equals(destination)) { return; } @@ -625,7 +625,7 @@ void CodeGeneratorMIPS64::MoveLocation(Location destination, // A valid move can always be inferred from the destination and source // locations. When moving from and to a register, the argument type can be // used to generate 32bit instead of 64bit moves. - bool unspecified_type = (type == Primitive::kPrimVoid); + bool unspecified_type = (dst_type == Primitive::kPrimVoid); DCHECK_EQ(unspecified_type, false); if (destination.IsRegister() || destination.IsFpuRegister()) { @@ -636,21 +636,21 @@ void CodeGeneratorMIPS64::MoveLocation(Location destination, || src_cst->IsFloatConstant() || src_cst->IsNullConstant()))) { // For stack slots and 32bit constants, a 64bit type is appropriate. - type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat; + dst_type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat; } else { // If the source is a double stack slot or a 64bit constant, a 64bit // type is appropriate. Else the source is a register, and since the // type has not been specified, we chose a 64bit type to force a 64bit // move. - type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble; + dst_type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble; } } - DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(type)) || - (destination.IsRegister() && !Primitive::IsFloatingPointType(type))); + DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(dst_type)) || + (destination.IsRegister() && !Primitive::IsFloatingPointType(dst_type))); if (source.IsStackSlot() || source.IsDoubleStackSlot()) { // Move to GPR/FPR from stack LoadOperandType load_type = source.IsStackSlot() ? kLoadWord : kLoadDoubleword; - if (Primitive::IsFloatingPointType(type)) { + if (Primitive::IsFloatingPointType(dst_type)) { __ LoadFpuFromOffset(load_type, destination.AsFpuRegister<FpuRegister>(), SP, @@ -665,31 +665,47 @@ void CodeGeneratorMIPS64::MoveLocation(Location destination, } else if (source.IsConstant()) { // Move to GPR/FPR from constant GpuRegister gpr = AT; - if (!Primitive::IsFloatingPointType(type)) { + if (!Primitive::IsFloatingPointType(dst_type)) { gpr = destination.AsRegister<GpuRegister>(); } - if (type == Primitive::kPrimInt || type == Primitive::kPrimFloat) { + if (dst_type == Primitive::kPrimInt || dst_type == Primitive::kPrimFloat) { __ LoadConst32(gpr, GetInt32ValueOf(source.GetConstant()->AsConstant())); } else { __ LoadConst64(gpr, GetInt64ValueOf(source.GetConstant()->AsConstant())); } - if (type == Primitive::kPrimFloat) { + if (dst_type == Primitive::kPrimFloat) { __ Mtc1(gpr, destination.AsFpuRegister<FpuRegister>()); - } else if (type == Primitive::kPrimDouble) { + } else if (dst_type == Primitive::kPrimDouble) { __ Dmtc1(gpr, destination.AsFpuRegister<FpuRegister>()); } - } else { + } else if (source.IsRegister()) { if (destination.IsRegister()) { // Move to GPR from GPR __ Move(destination.AsRegister<GpuRegister>(), source.AsRegister<GpuRegister>()); } else { + DCHECK(destination.IsFpuRegister()); + if (Primitive::Is64BitType(dst_type)) { + __ Dmtc1(source.AsRegister<GpuRegister>(), destination.AsFpuRegister<FpuRegister>()); + } else { + __ Mtc1(source.AsRegister<GpuRegister>(), destination.AsFpuRegister<FpuRegister>()); + } + } + } else if (source.IsFpuRegister()) { + if (destination.IsFpuRegister()) { // Move to FPR from FPR - if (type == Primitive::kPrimFloat) { + if (dst_type == Primitive::kPrimFloat) { __ MovS(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>()); } else { - DCHECK_EQ(type, Primitive::kPrimDouble); + DCHECK_EQ(dst_type, Primitive::kPrimDouble); __ MovD(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>()); } + } else { + DCHECK(destination.IsRegister()); + if (Primitive::Is64BitType(dst_type)) { + __ Dmfc1(destination.AsRegister<GpuRegister>(), source.AsFpuRegister<FpuRegister>()); + } else { + __ Mfc1(destination.AsRegister<GpuRegister>(), source.AsFpuRegister<FpuRegister>()); + } } } } else { // The destination is not a register. It must be a stack slot. @@ -697,13 +713,13 @@ void CodeGeneratorMIPS64::MoveLocation(Location destination, if (source.IsRegister() || source.IsFpuRegister()) { if (unspecified_type) { if (source.IsRegister()) { - type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong; + dst_type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong; } else { - type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble; + dst_type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble; } } - DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(type)) && - (source.IsFpuRegister() == Primitive::IsFloatingPointType(type))); + DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(dst_type)) && + (source.IsFpuRegister() == Primitive::IsFloatingPointType(dst_type))); // Move to stack from GPR/FPR StoreOperandType store_type = destination.IsStackSlot() ? kStoreWord : kStoreDoubleword; if (source.IsRegister()) { @@ -861,6 +877,14 @@ void CodeGeneratorMIPS64::MoveConstant(Location location, int32_t value) { __ LoadConst32(location.AsRegister<GpuRegister>(), value); } +void CodeGeneratorMIPS64::AddLocationAsTemp(Location location, LocationSummary* locations) { + if (location.IsRegister()) { + locations->AddTemp(location); + } else { + UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; + } +} + Location CodeGeneratorMIPS64::GetStackLocation(HLoadLocal* load) const { Primitive::Type type = load->GetType(); @@ -3118,6 +3142,74 @@ void InstructionCodeGeneratorMIPS64::VisitStaticFieldSet(HStaticFieldSet* instru HandleFieldSet(instruction, instruction->GetFieldInfo()); } +void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionMIPS64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorMIPS64::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionMIPS64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionMIPS64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorMIPS64::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionMIPS64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderMIPS64::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionMIPS64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorMIPS64::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionMIPS64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderMIPS64::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionMIPS64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorMIPS64::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionMIPS64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + void LocationsBuilderMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) { new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); } diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 16461d6c04..5e8f9e7f30 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -106,6 +106,31 @@ class InvokeRuntimeCallingConvention : public CallingConvention<GpuRegister, Fpu DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); }; +class FieldAccessCallingConventionMIPS64 : public FieldAccessCallingConvention { + public: + FieldAccessCallingConventionMIPS64() {} + + Location GetObjectLocation() const OVERRIDE { + return Location::RegisterLocation(A1); + } + Location GetFieldIndexLocation() const OVERRIDE { + return Location::RegisterLocation(A0); + } + Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE { + return Location::RegisterLocation(A0); + } + Location GetSetValueLocation( + Primitive::Type type ATTRIBUTE_UNUSED, bool is_instance) const OVERRIDE { + return is_instance ? Location::RegisterLocation(A2) : Location::RegisterLocation(A1); + } + Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE { + return Location::FpuRegisterLocation(F0); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionMIPS64); +}; + class ParallelMoveResolverMIPS64 : public ParallelMoveResolverWithSwap { public: ParallelMoveResolverMIPS64(ArenaAllocator* allocator, CodeGeneratorMIPS64* codegen) @@ -280,11 +305,13 @@ class CodeGeneratorMIPS64 : public CodeGenerator { void Finalize(CodeAllocator* allocator) OVERRIDE; // Code generation helpers. - - void MoveLocation(Location destination, Location source, Primitive::Type type); + void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE; void MoveConstant(Location destination, int32_t value) OVERRIDE; + void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE; + + void SwapLocations(Location loc1, Location loc2, Primitive::Type type); // Generate code to invoke a runtime entry point. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 3d97132d9b..5078456eb1 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -827,7 +827,10 @@ void CodeGeneratorX86::Move64(Location destination, Location source) { Location::RegisterLocation(destination.AsRegisterPairLow<Register>()), Primitive::kPrimInt); } else if (source.IsFpuRegister()) { - LOG(FATAL) << "Unimplemented"; + XmmRegister src_reg = source.AsFpuRegister<XmmRegister>(); + __ movd(destination.AsRegisterPairLow<Register>(), src_reg); + __ psrlq(src_reg, Immediate(32)); + __ movd(destination.AsRegisterPairHigh<Register>(), src_reg); } else { // No conflict possible, so just do the moves. DCHECK(source.IsDoubleStackSlot()); @@ -840,6 +843,15 @@ void CodeGeneratorX86::Move64(Location destination, Location source) { __ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>()); } else if (source.IsDoubleStackSlot()) { __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex())); + } else if (source.IsRegisterPair()) { + size_t elem_size = Primitive::ComponentSize(Primitive::kPrimInt); + // Create stack space for 2 elements. + __ subl(ESP, Immediate(2 * elem_size)); + __ movl(Address(ESP, 0), source.AsRegisterPairLow<Register>()); + __ movl(Address(ESP, elem_size), source.AsRegisterPairHigh<Register>()); + __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, 0)); + // And remove the temporary stack space we allocated. + __ addl(ESP, Immediate(2 * elem_size)); } else { LOG(FATAL) << "Unimplemented"; } @@ -966,6 +978,25 @@ void CodeGeneratorX86::MoveConstant(Location location, int32_t value) { __ movl(location.AsRegister<Register>(), Immediate(value)); } +void CodeGeneratorX86::MoveLocation(Location dst, Location src, Primitive::Type dst_type) { + if (Primitive::Is64BitType(dst_type)) { + Move64(dst, src); + } else { + Move32(dst, src); + } +} + +void CodeGeneratorX86::AddLocationAsTemp(Location location, LocationSummary* locations) { + if (location.IsRegister()) { + locations->AddTemp(location); + } else if (location.IsRegisterPair()) { + locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>())); + locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>())); + } else { + UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; + } +} + void InstructionCodeGeneratorX86::HandleGoto(HInstruction* got, HBasicBlock* successor) { DCHECK(!successor->IsExitBlock()); @@ -4085,6 +4116,74 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instr HandleFieldGet(instruction, instruction->GetFieldInfo()); } +void LocationsBuilderX86::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionX86 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorX86::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionX86 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderX86::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionX86 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorX86::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionX86 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderX86::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionX86 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorX86::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionX86 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderX86::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionX86 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorX86::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionX86 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) { LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() ? LocationSummary::kCallOnSlowPath diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 2c2fc65444..ae2d84f945 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -91,6 +91,36 @@ class InvokeDexCallingConventionVisitorX86 : public InvokeDexCallingConventionVi DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorX86); }; +class FieldAccessCallingConventionX86 : public FieldAccessCallingConvention { + public: + FieldAccessCallingConventionX86() {} + + Location GetObjectLocation() const OVERRIDE { + return Location::RegisterLocation(ECX); + } + Location GetFieldIndexLocation() const OVERRIDE { + return Location::RegisterLocation(EAX); + } + Location GetReturnLocation(Primitive::Type type) const OVERRIDE { + return Primitive::Is64BitType(type) + ? Location::RegisterPairLocation(EAX, EDX) + : Location::RegisterLocation(EAX); + } + Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE { + return Primitive::Is64BitType(type) + ? Location::RegisterPairLocation(EDX, EBX) + : (is_instance + ? Location::RegisterLocation(EDX) + : Location::RegisterLocation(ECX)); + } + Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE { + return Location::FpuRegisterLocation(XMM0); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionX86); +}; + class ParallelMoveResolverX86 : public ParallelMoveResolverWithSwap { public: ParallelMoveResolverX86(ArenaAllocator* allocator, CodeGeneratorX86* codegen) @@ -228,6 +258,9 @@ class CodeGeneratorX86 : public CodeGenerator { void Bind(HBasicBlock* block) OVERRIDE; void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; void MoveConstant(Location destination, int32_t value) OVERRIDE; + void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE; + void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE; + size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 6ea6138668..791bb9e6aa 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -990,6 +990,19 @@ void CodeGeneratorX86_64::MoveConstant(Location location, int32_t value) { Load64BitValue(location.AsRegister<CpuRegister>(), static_cast<int64_t>(value)); } +void CodeGeneratorX86_64::MoveLocation( + Location dst, Location src, Primitive::Type dst_type ATTRIBUTE_UNUSED) { + Move(dst, src); +} + +void CodeGeneratorX86_64::AddLocationAsTemp(Location location, LocationSummary* locations) { + if (location.IsRegister()) { + locations->AddTemp(location); + } else { + UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; + } +} + void InstructionCodeGeneratorX86_64::HandleGoto(HInstruction* got, HBasicBlock* successor) { DCHECK(!successor->IsExitBlock()); @@ -3849,6 +3862,74 @@ void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instru HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); } +void LocationsBuilderX86_64::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionX86_64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorX86_64::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instruction) { + FieldAccessCallingConventionX86_64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderX86_64::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionX86_64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorX86_64::VisitUnresolvedInstanceFieldSet( + HUnresolvedInstanceFieldSet* instruction) { + FieldAccessCallingConventionX86_64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderX86_64::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionX86_64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorX86_64::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instruction) { + FieldAccessCallingConventionX86_64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + +void LocationsBuilderX86_64::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionX86_64 calling_convention; + codegen_->CreateUnresolvedFieldLocationSummary( + instruction, instruction->GetFieldType(), calling_convention); +} + +void InstructionCodeGeneratorX86_64::VisitUnresolvedStaticFieldSet( + HUnresolvedStaticFieldSet* instruction) { + FieldAccessCallingConventionX86_64 calling_convention; + codegen_->GenerateUnresolvedFieldAccess(instruction, + instruction->GetFieldType(), + instruction->GetFieldIndex(), + instruction->GetDexPc(), + calling_convention); +} + void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) { LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() ? LocationSummary::kCallOnSlowPath diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 197ce63847..ecc8630e6b 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -70,6 +70,35 @@ class InvokeDexCallingConvention : public CallingConvention<Register, FloatRegis DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention); }; +class FieldAccessCallingConventionX86_64 : public FieldAccessCallingConvention { + public: + FieldAccessCallingConventionX86_64() {} + + Location GetObjectLocation() const OVERRIDE { + return Location::RegisterLocation(RSI); + } + Location GetFieldIndexLocation() const OVERRIDE { + return Location::RegisterLocation(RDI); + } + Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE { + return Location::RegisterLocation(RAX); + } + Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE { + return Primitive::Is64BitType(type) + ? Location::RegisterLocation(RDX) + : (is_instance + ? Location::RegisterLocation(RDX) + : Location::RegisterLocation(RSI)); + } + Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE { + return Location::FpuRegisterLocation(XMM0); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionX86_64); +}; + + class InvokeDexCallingConventionVisitorX86_64 : public InvokeDexCallingConventionVisitor { public: InvokeDexCallingConventionVisitorX86_64() {} @@ -215,6 +244,9 @@ class CodeGeneratorX86_64 : public CodeGenerator { void Bind(HBasicBlock* block) OVERRIDE; void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; void MoveConstant(Location destination, int32_t value) OVERRIDE; + void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE; + void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE; + size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 2c6c3b726a..7a83662696 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -398,6 +398,22 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("intrinsic") << invoke->GetIntrinsic(); } + void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE { + StartAttributeStream("field_type") << field_access->GetFieldType(); + } + + void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* field_access) OVERRIDE { + StartAttributeStream("field_type") << field_access->GetFieldType(); + } + + void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* field_access) OVERRIDE { + StartAttributeStream("field_type") << field_access->GetFieldType(); + } + + void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* field_access) OVERRIDE { + StartAttributeStream("field_type") << field_access->GetFieldType(); + } + void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE { StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit"); } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 039029aa52..0b65c564f7 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -247,12 +247,14 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { return false; } - uint16_t class_def_idx = resolved_method->GetDeclaringClass()->GetDexClassDefIndex(); - if (!compiler_driver_->IsMethodVerifiedWithoutFailures( - resolved_method->GetDexMethodIndex(), class_def_idx, *resolved_method->GetDexFile())) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) - << " couldn't be verified, so it cannot be inlined"; - return false; + if (!resolved_method->GetDeclaringClass()->IsVerified()) { + uint16_t class_def_idx = resolved_method->GetDeclaringClass()->GetDexClassDefIndex(); + if (!compiler_driver_->IsMethodVerifiedWithoutFailures( + resolved_method->GetDexMethodIndex(), class_def_idx, *resolved_method->GetDexFile())) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) + << " couldn't be verified, so it cannot be inlined"; + return false; + } } if (invoke_instruction->IsInvokeStaticOrDirect() && diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index d52f5927de..849f876f36 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1067,6 +1067,10 @@ class HLoopInformationOutwardIterator : public ValueObject { M(Shr, BinaryOperation) \ M(StaticFieldGet, Instruction) \ M(StaticFieldSet, Instruction) \ + M(UnresolvedInstanceFieldGet, Instruction) \ + M(UnresolvedInstanceFieldSet, Instruction) \ + M(UnresolvedStaticFieldGet, Instruction) \ + M(UnresolvedStaticFieldSet, Instruction) \ M(StoreLocal, Instruction) \ M(Sub, BinaryOperation) \ M(SuspendCheck, Instruction) \ @@ -4735,6 +4739,112 @@ class HStaticFieldSet : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); }; +class HUnresolvedInstanceFieldGet : public HExpression<1> { + public: + HUnresolvedInstanceFieldGet(HInstruction* obj, + Primitive::Type field_type, + uint32_t field_index, + uint32_t dex_pc) + : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc), + field_index_(field_index) { + SetRawInputAt(0, obj); + } + + bool NeedsEnvironment() const OVERRIDE { return true; } + bool CanThrow() const OVERRIDE { return true; } + + Primitive::Type GetFieldType() const { return GetType(); } + uint32_t GetFieldIndex() const { return field_index_; } + + DECLARE_INSTRUCTION(UnresolvedInstanceFieldGet); + + private: + const uint32_t field_index_; + + DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldGet); +}; + +class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { + public: + HUnresolvedInstanceFieldSet(HInstruction* obj, + HInstruction* value, + Primitive::Type field_type, + uint32_t field_index, + uint32_t dex_pc) + : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), + field_type_(field_type), + field_index_(field_index) { + DCHECK_EQ(field_type, value->GetType()); + SetRawInputAt(0, obj); + SetRawInputAt(1, value); + } + + bool NeedsEnvironment() const OVERRIDE { return true; } + bool CanThrow() const OVERRIDE { return true; } + + Primitive::Type GetFieldType() const { return field_type_; } + uint32_t GetFieldIndex() const { return field_index_; } + + DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet); + + private: + const Primitive::Type field_type_; + const uint32_t field_index_; + + DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet); +}; + +class HUnresolvedStaticFieldGet : public HExpression<0> { + public: + HUnresolvedStaticFieldGet(Primitive::Type field_type, + uint32_t field_index, + uint32_t dex_pc) + : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc), + field_index_(field_index) { + } + + bool NeedsEnvironment() const OVERRIDE { return true; } + bool CanThrow() const OVERRIDE { return true; } + + Primitive::Type GetFieldType() const { return GetType(); } + uint32_t GetFieldIndex() const { return field_index_; } + + DECLARE_INSTRUCTION(UnresolvedStaticFieldGet); + + private: + const uint32_t field_index_; + + DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldGet); +}; + +class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { + public: + HUnresolvedStaticFieldSet(HInstruction* value, + Primitive::Type field_type, + uint32_t field_index, + uint32_t dex_pc) + : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), + field_type_(field_type), + field_index_(field_index) { + DCHECK_EQ(field_type, value->GetType()); + SetRawInputAt(0, value); + } + + bool NeedsEnvironment() const OVERRIDE { return true; } + bool CanThrow() const OVERRIDE { return true; } + + Primitive::Type GetFieldType() const { return field_type_; } + uint32_t GetFieldIndex() const { return field_index_; } + + DECLARE_INSTRUCTION(UnresolvedStaticFieldSet); + + private: + const Primitive::Type field_type_; + const uint32_t field_index_; + + DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet); +}; + // Implement the move-exception DEX instruction. class HLoadException : public HExpression<0> { public: diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index dbfbd96e39..3e982dca23 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -829,8 +829,12 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite return compiled_method; } -static bool HasOnlyUnresolvedFailures(const VerifiedMethod* verified_method) { - uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS; +static bool CanHandleVerificationFailure(const VerifiedMethod* verified_method) { + // For access errors the compiler will use the unresolved helpers (e.g. HInvokeUnresolved). + uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS + | verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS + | verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD + | verifier::VerifyError::VERIFY_ERROR_ACCESS_METHOD; return (verified_method->GetEncounteredVerificationFailures() & (~unresolved_mask)) == 0; } @@ -847,7 +851,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx); DCHECK(!verified_method->HasRuntimeThrow()); if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) - || HasOnlyUnresolvedFailures(verified_method)) { + || CanHandleVerificationFailure(verified_method)) { method = TryCompile(code_item, access_flags, invoke_type, class_def_idx, method_idx, jclass_loader, dex_file, dex_cache); } else { diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index c7701b70ad..df45c8e890 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -34,6 +34,8 @@ enum MethodCompilationStat { kInstructionSimplifications, kInstructionSimplificationsArch, kUnresolvedMethod, + kUnresolvedField, + kUnresolvedFieldNotAFastAccess, kNotCompiledBranchOutsideMethodCode, kNotCompiledCannotBuildSSA, kNotCompiledCantAccesType, @@ -45,7 +47,6 @@ enum MethodCompilationStat { kNotCompiledPathological, kNotCompiledSpaceFilter, kNotCompiledUnhandledInstruction, - kNotCompiledUnresolvedField, kNotCompiledUnsupportedIsa, kNotCompiledVerifyAtRuntime, kNotOptimizedDisabled, @@ -104,6 +105,8 @@ class OptimizingCompilerStats { case kInstructionSimplifications: return "kInstructionSimplifications"; case kInstructionSimplificationsArch: return "kInstructionSimplificationsArch"; case kUnresolvedMethod : return "kUnresolvedMethod"; + case kUnresolvedField : return "kUnresolvedField"; + case kUnresolvedFieldNotAFastAccess : return "kUnresolvedFieldNotAFastAccess"; case kNotCompiledBranchOutsideMethodCode: return "kNotCompiledBranchOutsideMethodCode"; case kNotCompiledCannotBuildSSA : return "kNotCompiledCannotBuildSSA"; case kNotCompiledCantAccesType : return "kNotCompiledCantAccesType"; @@ -115,7 +118,6 @@ class OptimizingCompilerStats { case kNotCompiledPathological : return "kNotCompiledPathological"; case kNotCompiledSpaceFilter : return "kNotCompiledSpaceFilter"; case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction"; - case kNotCompiledUnresolvedField : return "kNotCompiledUnresolvedField"; case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa"; case kNotCompiledVerifyAtRuntime : return "kNotCompiledVerifyAtRuntime"; case kNotOptimizedDisabled : return "kNotOptimizedDisabled"; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index fe837e4545..d22f2540ad 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -52,6 +52,8 @@ class RTPVisitor : public HGraphDelegateVisitor { void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact); void VisitInstanceFieldGet(HInstanceFieldGet* instr) OVERRIDE; void VisitStaticFieldGet(HStaticFieldGet* instr) OVERRIDE; + void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instr) OVERRIDE; + void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instr) OVERRIDE; void VisitInvoke(HInvoke* instr) OVERRIDE; void VisitArrayGet(HArrayGet* instr) OVERRIDE; void VisitCheckCast(HCheckCast* instr) OVERRIDE; @@ -450,6 +452,22 @@ void RTPVisitor::VisitStaticFieldGet(HStaticFieldGet* instr) { UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo()); } +void RTPVisitor::VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instr) { + // TODO: Use descriptor to get the actual type. + if (instr->GetFieldType() == Primitive::kPrimNot) { + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false)); + } +} + +void RTPVisitor::VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instr) { + // TODO: Use descriptor to get the actual type. + if (instr->GetFieldType() == Primitive::kPrimNot) { + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false)); + } +} + void RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index cc32da1e59..680e2d7b45 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -235,11 +235,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" --compiler-backend=(Quick|Optimizing): select compiler backend"); UsageError(" set."); UsageError(" Example: --compiler-backend=Optimizing"); - if (kUseOptimizingCompiler) { - UsageError(" Default: Optimizing"); - } else { - UsageError(" Default: Quick"); - } + UsageError(" Default: Optimizing"); UsageError(""); UsageError(" --compiler-filter=" "(verify-none" @@ -503,7 +499,7 @@ static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) { class Dex2Oat FINAL { public: explicit Dex2Oat(TimingLogger* timings) : - compiler_kind_(kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick), + compiler_kind_(Compiler::kOptimizing), instruction_set_(kRuntimeISA), // Take the default set of instruction features from the build. verification_results_(nullptr), @@ -752,10 +748,9 @@ class Dex2Oat FINAL { void ProcessOptions(ParserOptions* parser_options) { image_ = (!image_filename_.empty()); - if (!parser_options->requested_specific_compiler && !kUseOptimizingCompiler) { - // If no specific compiler is requested, the current behavior is - // to compile the boot image with Quick, and the rest with Optimizing. - compiler_kind_ = image_ ? Compiler::kQuick : Compiler::kOptimizing; + if (image_) { + // We need the boot image to always be debuggable. + parser_options->debuggable = true; } if (oat_filename_.empty() && oat_fd_ == -1) { diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc index 1ac79304df..82bc8b9521 100644 --- a/imgdiag/imgdiag_test.cc +++ b/imgdiag/imgdiag_test.cc @@ -109,11 +109,12 @@ class ImgDiagTest : public CommonRuntimeTest { std::string boot_image_location_; }; -#if defined (ART_TARGET) +#if defined (ART_TARGET) && !defined(__mips__) TEST_F(ImgDiagTest, ImageDiffPidSelf) { #else // Can't run this test on the host, it will fail when trying to open /proc/kpagestats // because it's root read-only. +// Also test fails on mips. b/24596015. TEST_F(ImgDiagTest, DISABLED_ImageDiffPidSelf) { #endif // Invoke 'img_diag' against the current process. diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index dc1cf8ab51..d09631bc71 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -839,13 +839,12 @@ TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCode, RETURN_IF_R TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER /* * Called by managed code to resolve a static field and store a 64-bit primitive value. - * On entry r0 holds field index, r1:r2 hold new_val + * On entry r0 holds field index, r2:r3 hold new_val */ .extern artSet64StaticFromCode ENTRY art_quick_set64_static - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC - mov r3, r2 @ pass one half of wide argument - mov r2, r1 @ pass other half of wide argument + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r12 @ save callee saves in case of GC + @ r2:r3 contain the wide argument ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer str r9, [sp, #-16]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 16 @@ -870,6 +869,7 @@ THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RE .extern artSet64InstanceFromCode ENTRY art_quick_set64_instance SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12, lr @ save callee saves in case of GC + @ r2:r3 contain the wide argument ldr r12, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer str r9, [sp, #-12]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 12 diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 68121781ca..be5a15ec39 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1421,9 +1421,8 @@ THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RE .extern artSet64StaticFromCode ENTRY art_quick_set64_static SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC - mov x3, x1 // Store value ldr x1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer - mov x2, x3 // Put value param + // x2 contains the parameter mov x3, xSELF // pass Thread::Current bl artSet64StaticFromCode RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index ce1b2f3d24..68156ae7e3 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1244,7 +1244,7 @@ END art_quick_set32_static .extern artSet64StaticFromCode ENTRY art_quick_set64_static SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a2, $a1 # pass new_val + # a2 contains the new val ld $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* jal artSet64StaticFromCode # (field_idx, referrer, new_val, Thread*) move $a3, rSELF # pass Thread::Current diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index f10799cc28..016c664b0c 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -418,6 +418,48 @@ class StubTest : public CommonRuntimeTest { return result; } + // 64bit static field set use a slightly different register order than Invoke3WithReferrer. + // TODO: implement for other architectures + // TODO: try merge with Invoke3WithReferrer + size_t Invoke64StaticSet(size_t arg0, size_t arg1, size_t arg2, uintptr_t code, Thread* self, + ArtMethod* referrer) { + // Push a transition back into managed code onto the linked list in thread. + ManagedStack fragment; + self->PushManagedStackFragment(&fragment); + + size_t result; + size_t fpr_result = 0; +#if defined(__x86_64__) && !defined(__APPLE__) && defined(__clang__) + // Note: Uses the native convention + // TODO: Set the thread? + __asm__ __volatile__( + "pushq %[referrer]\n\t" // Push referrer + "pushq (%%rsp)\n\t" // & 16B alignment padding + ".cfi_adjust_cfa_offset 16\n\t" + "call *%%rax\n\t" // Call the stub + "addq $16, %%rsp\n\t" // Pop null and padding + ".cfi_adjust_cfa_offset -16\n\t" + : "=a" (result) + // Use the result from rax + : "D"(arg0), "d"(arg1), "S"(arg2), "a"(code), [referrer] "c"(referrer) + // This places arg0 into rdi, arg1 into rdx, arg2 into rsi, and code into rax + : "rbx", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "memory"); // clobber all + // TODO: Should we clobber the other registers? +#else + UNUSED(arg0, arg1, arg2, code, referrer); + LOG(WARNING) << "Was asked to invoke for an architecture I do not understand."; + result = 0; +#endif + // Pop transition. + self->PopManagedStackFragment(fragment); + + fp_result = fpr_result; + EXPECT_EQ(0U, fp_result); + + return result; + } + // TODO: Set up a frame according to referrer's specs. size_t Invoke3WithReferrerAndHidden(size_t arg0, size_t arg1, size_t arg2, uintptr_t code, Thread* self, ArtMethod* referrer, size_t hidden) { @@ -774,22 +816,6 @@ class StubTest : public CommonRuntimeTest { return result; } - // Method with 32b arg0, 64b arg1 - size_t Invoke3UWithReferrer(size_t arg0, uint64_t arg1, uintptr_t code, Thread* self, - ArtMethod* referrer) { -#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) || \ - defined(__aarch64__) - // Just pass through. - return Invoke3WithReferrer(arg0, arg1, 0U, code, self, referrer); -#else - // Need to split up arguments. - uint32_t lower = static_cast<uint32_t>(arg1 & 0xFFFFFFFF); - uint32_t upper = static_cast<uint32_t>((arg1 >> 32) & 0xFFFFFFFF); - - return Invoke3WithReferrer(arg0, lower, upper, code, self, referrer); -#endif - } - static uintptr_t GetEntrypoint(Thread* self, QuickEntrypointEnum entrypoint) { int32_t offset; #ifdef __LP64__ @@ -1974,21 +2000,22 @@ static void GetSetObjInstance(Handle<mirror::Object>* obj, ArtField* f, } -// TODO: Complete these tests for 32b architectures. +// TODO: Complete these tests for 32b architectures static void GetSet64Static(ArtField* f, Thread* self, ArtMethod* referrer, StubTest* test) SHARED_REQUIRES(Locks::mutator_lock_) { -#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) || \ - defined(__aarch64__) +// TODO: (defined(__mips__) && defined(__LP64__)) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) uint64_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF }; for (size_t i = 0; i < arraysize(values); ++i) { - test->Invoke3UWithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), - values[i], - StubTest::GetEntrypoint(self, kQuickSet64Static), - self, - referrer); + test->Invoke64StaticSet(static_cast<size_t>(f->GetDexFieldIndex()), + values[i], + 0U, + StubTest::GetEntrypoint(self, kQuickSet64Static), + self, + referrer); size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U, diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index f3b15c9ab2..3afc4d545f 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1434,15 +1434,18 @@ END_FUNCTION art_quick_set64_instance // Call artSet64StaticFromCode with 3 word size arguments plus with the referrer in the 2nd position // so that new_val is aligned on even registers were we passing arguments in registers. DEFINE_FUNCTION art_quick_set64_static + // TODO: Implement SETUP_GOT_NOSAVE for got_reg = ecx to avoid moving around the registers. + movd %ebx, %xmm0 SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC - mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ebx // get referrer + movd %xmm0, %ebx + mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ecx // get referrer subl LITERAL(12), %esp // alignment padding CFI_ADJUST_CFA_OFFSET(12) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - PUSH edx // pass high half of new_val - PUSH ecx // pass low half of new_val - PUSH ebx // pass referrer + PUSH ebx // pass high half of new_val + PUSH edx // pass low half of new_val + PUSH ecx // pass referrer PUSH eax // pass field_idx call SYMBOL(artSet64StaticFromCode) // (field_idx, referrer, new_val, Thread*) addl LITERAL(32), %esp // pop arguments diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 2f438a3c8f..1133203e31 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1383,7 +1383,7 @@ ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_O // This is singled out as the argument order is different. DEFINE_FUNCTION art_quick_set64_static - movq %rsi, %rdx // pass new_val + // new_val is already in %rdx movq 8(%rsp), %rsi // pass referrer SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // field_idx is in rdi diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index d6b2b7e04d..632a50f15c 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -35,6 +35,8 @@ #include "quick/quick_method_frame_info.h" #include "read_barrier-inl.h" #include "runtime-inl.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" #include "utils.h" namespace art { @@ -75,9 +77,28 @@ inline bool ArtMethod::CASDeclaringClass(mirror::Class* expected_class, expected_root, desired_root); } +// AssertSharedHeld doesn't work in GetAccessFlags, so use a NO_THREAD_SAFETY_ANALYSIS helper. +// TODO: Figure out why ASSERT_SHARED_CAPABILITY doesn't work. +ALWAYS_INLINE +static inline void DoGetAccessFlagsHelper(ArtMethod* method) NO_THREAD_SAFETY_ANALYSIS { + CHECK(method->IsRuntimeMethod() || method->GetDeclaringClass()->IsIdxLoaded() || + method->GetDeclaringClass()->IsErroneous()); +} + inline uint32_t ArtMethod::GetAccessFlags() { - DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() || - GetDeclaringClass()->IsErroneous()); + if (kIsDebugBuild) { + Thread* self = Thread::Current(); + if (!Locks::mutator_lock_->IsSharedHeld(self)) { + ScopedObjectAccess soa(self); + CHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() || + GetDeclaringClass()->IsErroneous()); + } else { + // We cannot use SOA in this case. We might be holding the lock, but may not be in the + // runnable state (e.g., during GC). + Locks::mutator_lock_->AssertSharedHeld(self); + DoGetAccessFlagsHelper(this); + } + } return access_flags_; } diff --git a/runtime/art_method.h b/runtime/art_method.h index f78c8274b0..0315c3a953 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -75,7 +75,9 @@ class ArtMethod FINAL { return MemberOffset(OFFSETOF_MEMBER(ArtMethod, declaring_class_)); } - ALWAYS_INLINE uint32_t GetAccessFlags() SHARED_REQUIRES(Locks::mutator_lock_); + // Note: GetAccessFlags acquires the mutator lock in debug mode to check that it is not called for + // a proxy method. + ALWAYS_INLINE uint32_t GetAccessFlags(); void SetAccessFlags(uint32_t new_access_flags) { // Not called within a transaction. @@ -86,77 +88,78 @@ class ArtMethod FINAL { InvokeType GetInvokeType() SHARED_REQUIRES(Locks::mutator_lock_); // Returns true if the method is declared public. - bool IsPublic() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsPublic() { return (GetAccessFlags() & kAccPublic) != 0; } // Returns true if the method is declared private. - bool IsPrivate() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsPrivate() { return (GetAccessFlags() & kAccPrivate) != 0; } // Returns true if the method is declared static. - bool IsStatic() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsStatic() { return (GetAccessFlags() & kAccStatic) != 0; } // Returns true if the method is a constructor. - bool IsConstructor() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsConstructor() { return (GetAccessFlags() & kAccConstructor) != 0; } // Returns true if the method is a class initializer. - bool IsClassInitializer() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsClassInitializer() { return IsConstructor() && IsStatic(); } // Returns true if the method is static, private, or a constructor. - bool IsDirect() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsDirect() { return IsDirect(GetAccessFlags()); } static bool IsDirect(uint32_t access_flags) { - return (access_flags & (kAccStatic | kAccPrivate | kAccConstructor)) != 0; + constexpr uint32_t direct = kAccStatic | kAccPrivate | kAccConstructor; + return (access_flags & direct) != 0; } // Returns true if the method is declared synchronized. - bool IsSynchronized() SHARED_REQUIRES(Locks::mutator_lock_) { - uint32_t synchonized = kAccSynchronized | kAccDeclaredSynchronized; + bool IsSynchronized() { + constexpr uint32_t synchonized = kAccSynchronized | kAccDeclaredSynchronized; return (GetAccessFlags() & synchonized) != 0; } - bool IsFinal() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsFinal() { return (GetAccessFlags() & kAccFinal) != 0; } - bool IsMiranda() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsMiranda() { return (GetAccessFlags() & kAccMiranda) != 0; } - bool IsNative() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsNative() { return (GetAccessFlags() & kAccNative) != 0; } - bool IsFastNative() SHARED_REQUIRES(Locks::mutator_lock_) { - uint32_t mask = kAccFastNative | kAccNative; + bool IsFastNative() { + constexpr uint32_t mask = kAccFastNative | kAccNative; return (GetAccessFlags() & mask) == mask; } - bool IsAbstract() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsAbstract() { return (GetAccessFlags() & kAccAbstract) != 0; } - bool IsSynthetic() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsSynthetic() { return (GetAccessFlags() & kAccSynthetic) != 0; } bool IsProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_); - bool IsPreverified() SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsPreverified() { return (GetAccessFlags() & kAccPreverified) != 0; } - void SetPreverified() SHARED_REQUIRES(Locks::mutator_lock_) { + void SetPreverified() { DCHECK(!IsPreverified()); SetAccessFlags(GetAccessFlags() | kAccPreverified); } @@ -404,7 +407,7 @@ class ArtMethod FINAL { return GetNativePointer<void*>(EntryPointFromJniOffset(pointer_size), pointer_size); } - void SetEntryPointFromJni(const void* entrypoint) SHARED_REQUIRES(Locks::mutator_lock_) { + void SetEntryPointFromJni(const void* entrypoint) { DCHECK(IsNative()); SetEntryPointFromJniPtrSize(entrypoint, sizeof(void*)); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index dbc5ceca0d..b0590e25ff 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2650,10 +2650,8 @@ mirror::DexCache* ClassLinker::FindDexCacheLocked(Thread* self, const DexFile& dex_file, bool allow_failure) { // Search assuming unique-ness of dex file. - JavaVMExt* const vm = self->GetJniEnv()->vm; for (jweak weak_root : dex_caches_) { - mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>( - vm->DecodeWeakGlobal(self, weak_root)); + mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(weak_root)); if (dex_cache != nullptr && dex_cache->GetDexFile() == &dex_file) { return dex_cache; } @@ -6202,10 +6200,9 @@ void ClassLinker::DropFindArrayClassCache() { void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const { Thread* const self = Thread::Current(); - JavaVMExt* const vm = self->GetJniEnv()->vm; for (const ClassLoaderData& data : class_loaders_) { - auto* const class_loader = down_cast<mirror::ClassLoader*>( - vm->DecodeWeakGlobal(self, data.weak_root)); + // Need to use DecodeJObject so that we get null for cleared JNI weak globals. + auto* const class_loader = down_cast<mirror::ClassLoader*>(self->DecodeJObject(data.weak_root)); if (class_loader != nullptr) { visitor->Visit(class_loader); } @@ -6218,8 +6215,8 @@ void ClassLinker::CleanupClassLoaders() { JavaVMExt* const vm = Runtime::Current()->GetJavaVM(); for (auto it = class_loaders_.begin(); it != class_loaders_.end(); ) { const ClassLoaderData& data = *it; - auto* const class_loader = down_cast<mirror::ClassLoader*>( - vm->DecodeWeakGlobal(self, data.weak_root)); + // Need to use DecodeJObject so that we get null for cleared JNI weak globals. + auto* const class_loader = down_cast<mirror::ClassLoader*>(self->DecodeJObject(data.weak_root)); if (class_loader != nullptr) { ++it; } else { diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 450031abd5..d24b4fb542 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -579,15 +579,11 @@ void Dbg::GoActive() { Runtime* runtime = Runtime::Current(); ScopedSuspendAll ssa(__FUNCTION__); - Thread* self = Thread::Current(); - ThreadState old_state = self->SetStateUnsafe(kRunnable); - CHECK_NE(old_state, kRunnable); if (RequiresDeoptimization()) { runtime->GetInstrumentation()->EnableDeoptimization(); } instrumentation_events_ = 0; gDebuggerActive = true; - CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); LOG(INFO) << "Debugger is active"; } diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 468179c9d5..0a7a69f37e 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -620,7 +620,10 @@ void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) { gc_mark_stack_->PushBack(to_ref); } else { CHECK_EQ(static_cast<uint32_t>(mark_stack_mode), - static_cast<uint32_t>(kMarkStackModeGcExclusive)); + static_cast<uint32_t>(kMarkStackModeGcExclusive)) + << "ref=" << to_ref + << " self->gc_marking=" << self->GetIsGcMarking() + << " cc->is_marking=" << is_marking_; CHECK(self == thread_running_gc_) << "Only GC-running thread should access the mark stack " << "in the GC exclusive mark stack mode"; diff --git a/runtime/globals.h b/runtime/globals.h index d70f3ab19b..987a94ea4b 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -58,12 +58,6 @@ static constexpr bool kIsTargetBuild = true; static constexpr bool kIsTargetBuild = false; #endif -#if defined(ART_USE_OPTIMIZING_COMPILER) -static constexpr bool kUseOptimizingCompiler = true; -#else -static constexpr bool kUseOptimizingCompiler = false; -#endif - // Garbage collector constants. static constexpr bool kMovingCollector = true; static constexpr bool kMarkCompactSupport = false && kMovingCollector; diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index e9c16c1aa4..9b9c5d2760 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -111,6 +111,9 @@ void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread, DCHECK(this_object != nullptr); ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*)); if (info != nullptr) { + // Since the instrumentation is marked from the declaring class we need to mark the card so + // that mod-union tables and card rescanning know about the update. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass()); info->AddInvokeInfo(thread, dex_pc, this_object->GetClass()); } } diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h index daae401b46..85ac4aab96 100644 --- a/runtime/read_barrier-inl.h +++ b/runtime/read_barrier-inl.h @@ -62,8 +62,10 @@ inline MirrorType* ReadBarrier::Barrier( if (heap != nullptr && heap->GetReadBarrierTable()->IsSet(old_ref)) { ref = reinterpret_cast<MirrorType*>(Mark(old_ref)); // Update the field atomically. This may fail if mutator updates before us, but it's ok. - obj->CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<false, false>( - offset, old_ref, ref); + if (ref != old_ref) { + obj->CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<false, false>( + offset, old_ref, ref); + } } AssertToSpaceInvariant(obj, offset, ref); return ref; @@ -90,17 +92,17 @@ inline MirrorType* ReadBarrier::BarrierForRoot(MirrorType** root, // To be implemented. return ref; } else if (with_read_barrier && kUseTableLookupReadBarrier) { - if (kMaybeDuringStartup && IsDuringStartup()) { - // During startup, the heap may not be initialized yet. Just - // return the given ref. - return ref; - } - if (Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) { + Thread* self = Thread::Current(); + if (self != nullptr && + self->GetIsGcMarking() && + Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) { MirrorType* old_ref = ref; ref = reinterpret_cast<MirrorType*>(Mark(old_ref)); // Update the field atomically. This may fail if mutator updates before us, but it's ok. - Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root); - atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, ref); + if (ref != old_ref) { + Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root); + atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, ref); + } } AssertToSpaceInvariant(gc_root_source, ref); return ref; @@ -127,19 +129,19 @@ inline MirrorType* ReadBarrier::BarrierForRoot(mirror::CompressedReference<Mirro // To be implemented. return ref; } else if (with_read_barrier && kUseTableLookupReadBarrier) { - if (kMaybeDuringStartup && IsDuringStartup()) { - // During startup, the heap may not be initialized yet. Just - // return the given ref. - return ref; - } - if (Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) { + Thread* self = Thread::Current(); + if (self != nullptr && + self->GetIsGcMarking() && + Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) { auto old_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref); ref = reinterpret_cast<MirrorType*>(Mark(ref)); auto new_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref); // Update the field atomically. This may fail if mutator updates before us, but it's ok. - auto* atomic_root = - reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root); - atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref); + if (new_ref.AsMirrorPtr() != old_ref.AsMirrorPtr()) { + auto* atomic_root = + reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root); + atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref); + } } AssertToSpaceInvariant(gc_root_source, ref); return ref; diff --git a/runtime/stack.cc b/runtime/stack.cc index 7f72f8ab61..1d21a6494a 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -110,7 +110,7 @@ StackVisitor::StackVisitor(Thread* thread, } InlineInfo StackVisitor::GetCurrentInlineInfo() const { - ArtMethod* outer_method = *GetCurrentQuickFrame(); + ArtMethod* outer_method = GetOuterMethod(); uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_); CodeInfo code_info = outer_method->GetOptimizedCodeInfo(); StackMapEncoding encoding = code_info.ExtractEncoding(); @@ -194,11 +194,12 @@ size_t StackVisitor::GetNativePcOffset() const { } bool StackVisitor::IsReferenceVReg(ArtMethod* m, uint16_t vreg) { + DCHECK_EQ(m, GetMethod()); // Process register map (which native and runtime methods don't have) if (m->IsNative() || m->IsRuntimeMethod() || m->IsProxyMethod()) { return false; } - if (m->IsOptimized(sizeof(void*))) { + if (GetOuterMethod()->IsOptimized(sizeof(void*))) { return true; // TODO: Implement. } const uint8_t* native_gc_map = m->GetNativeGcMap(sizeof(void*)); @@ -251,7 +252,7 @@ bool StackVisitor::GetVReg(ArtMethod* m, uint16_t vreg, VRegKind kind, uint32_t* if (GetVRegFromDebuggerShadowFrame(vreg, kind, val)) { return true; } - if (m->IsOptimized(sizeof(void*))) { + if (GetOuterMethod()->IsOptimized(sizeof(void*))) { return GetVRegFromOptimizedCode(m, vreg, kind, val); } else { return GetVRegFromQuickCode(m, vreg, kind, val); @@ -288,15 +289,15 @@ bool StackVisitor::GetVRegFromQuickCode(ArtMethod* m, uint16_t vreg, VRegKind ki bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKind kind, uint32_t* val) const { + ArtMethod* outer_method = GetOuterMethod(); + const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*)); + DCHECK(code_pointer != nullptr); DCHECK_EQ(m, GetMethod()); const DexFile::CodeItem* code_item = m->GetCodeItem(); DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be null or how would we compile // its instructions? uint16_t number_of_dex_registers = code_item->registers_size_; DCHECK_LT(vreg, code_item->registers_size_); - ArtMethod* outer_method = *GetCurrentQuickFrame(); - const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*)); - DCHECK(code_pointer != nullptr); CodeInfo code_info = outer_method->GetOptimizedCodeInfo(); StackMapEncoding encoding = code_info.ExtractEncoding(); @@ -405,7 +406,7 @@ bool StackVisitor::GetVRegPair(ArtMethod* m, uint16_t vreg, VRegKind kind_lo, if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably read registers without a context. DCHECK(m == GetMethod()); - if (m->IsOptimized(sizeof(void*))) { + if (GetOuterMethod()->IsOptimized(sizeof(void*))) { return GetVRegPairFromOptimizedCode(m, vreg, kind_lo, kind_hi, val); } else { return GetVRegPairFromQuickCode(m, vreg, kind_lo, kind_hi, val); @@ -481,7 +482,7 @@ bool StackVisitor::SetVReg(ArtMethod* m, uint16_t vreg, uint32_t new_value, if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably write registers without a context. DCHECK(m == GetMethod()); - if (m->IsOptimized(sizeof(void*))) { + if (GetOuterMethod()->IsOptimized(sizeof(void*))) { return false; } else { return SetVRegFromQuickCode(m, vreg, new_value, kind); @@ -590,7 +591,7 @@ bool StackVisitor::SetVRegPair(ArtMethod* m, uint16_t vreg, uint64_t new_value, if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably write registers without a context. DCHECK(m == GetMethod()); - if (m->IsOptimized(sizeof(void*))) { + if (GetOuterMethod()->IsOptimized(sizeof(void*))) { return false; } else { return SetVRegPairFromQuickCode(m, vreg, new_value, kind_lo, kind_hi); @@ -724,14 +725,14 @@ void StackVisitor::SetFPR(uint32_t reg, uintptr_t value) { uintptr_t StackVisitor::GetReturnPc() const { uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame()); DCHECK(sp != nullptr); - uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue(); + uint8_t* pc_addr = sp + GetOuterMethod()->GetReturnPcOffset().SizeValue(); return *reinterpret_cast<uintptr_t*>(pc_addr); } void StackVisitor::SetReturnPc(uintptr_t new_ret_pc) { uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame()); CHECK(sp != nullptr); - uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue(); + uint8_t* pc_addr = sp + GetOuterMethod()->GetReturnPcOffset().SizeValue(); *reinterpret_cast<uintptr_t*>(pc_addr) = new_ret_pc; } diff --git a/runtime/stack.h b/runtime/stack.h index 292c745090..31acf0eb64 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -473,6 +473,10 @@ class StackVisitor { ArtMethod* GetMethod() const SHARED_REQUIRES(Locks::mutator_lock_); + ArtMethod* GetOuterMethod() const { + return *GetCurrentQuickFrame(); + } + bool IsShadowFrame() const { return cur_shadow_frame_ != nullptr; } diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 8bf241b66d..f5d20bd608 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -118,11 +118,8 @@ inline void Thread::AssertThreadSuspensionIsAllowable(bool check_locks) const { } } -inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { - AssertThreadSuspensionIsAllowable(); +inline void Thread::TransitionToSuspendedAndRunCheckpoints(ThreadState new_state) { DCHECK_NE(new_state, kRunnable); - DCHECK_EQ(this, Thread::Current()); - // Change to non-runnable state, thereby appearing suspended to the system. DCHECK_EQ(GetState(), kRunnable); union StateAndFlags old_state_and_flags; union StateAndFlags new_state_and_flags; @@ -145,12 +142,9 @@ inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { break; } } +} - // Change to non-runnable state, thereby appearing suspended to the system. - // Mark the release of the share of the mutator_lock_. - Locks::mutator_lock_->TransitionFromRunnableToSuspended(this); - - // Once suspended - check the active suspend barrier flag +inline void Thread::PassActiveSuspendBarriers() { while (true) { uint16_t current_flags = tls32_.state_and_flags.as_struct.flags; if (LIKELY((current_flags & (kCheckpointRequest | kActiveSuspendBarrier)) == 0)) { @@ -159,11 +153,22 @@ inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { PassActiveSuspendBarriers(this); } else { // Impossible - LOG(FATAL) << "Fatal, thread transited into suspended without running the checkpoint"; + LOG(FATAL) << "Fatal, thread transitioned into suspended without running the checkpoint"; } } } +inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { + AssertThreadSuspensionIsAllowable(); + DCHECK_EQ(this, Thread::Current()); + // Change to non-runnable state, thereby appearing suspended to the system. + TransitionToSuspendedAndRunCheckpoints(new_state); + // Mark the release of the share of the mutator_lock_. + Locks::mutator_lock_->TransitionFromRunnableToSuspended(this); + // Once suspended - check the active suspend barrier flag + PassActiveSuspendBarriers(); +} + inline ThreadState Thread::TransitionFromSuspendedToRunnable() { union StateAndFlags old_state_and_flags; old_state_and_flags.as_int = tls32_.state_and_flags.as_int; @@ -191,7 +196,9 @@ inline ThreadState Thread::TransitionFromSuspendedToRunnable() { PassActiveSuspendBarriers(this); } else if ((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0) { // Impossible - LOG(FATAL) << "Fatal, wrong checkpoint flag"; + LOG(FATAL) << "Transitioning to runnable with checkpoint flag, " + << " flags=" << old_state_and_flags.as_struct.flags + << " state=" << old_state_and_flags.as_struct.state; } else if ((old_state_and_flags.as_struct.flags & kSuspendRequest) != 0) { // Wait while our suspend count is non-zero. MutexLock mu(this, *Locks::thread_suspend_count_lock_); diff --git a/runtime/thread.h b/runtime/thread.h index d21644d179..d262c62224 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -247,17 +247,15 @@ class Thread { SHARED_REQUIRES(Locks::mutator_lock_); // Transition from non-runnable to runnable state acquiring share on mutator_lock_. - ThreadState TransitionFromSuspendedToRunnable() + ALWAYS_INLINE ThreadState TransitionFromSuspendedToRunnable() REQUIRES(!Locks::thread_suspend_count_lock_) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) - ALWAYS_INLINE; + SHARED_LOCK_FUNCTION(Locks::mutator_lock_); // Transition from runnable into a state where mutator privileges are denied. Releases share of // mutator lock. - void TransitionFromRunnableToSuspended(ThreadState new_state) + ALWAYS_INLINE void TransitionFromRunnableToSuspended(ThreadState new_state) REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_) - UNLOCK_FUNCTION(Locks::mutator_lock_) - ALWAYS_INLINE; + UNLOCK_FUNCTION(Locks::mutator_lock_); // Once called thread suspension will cause an assertion failure. const char* StartAssertNoThreadSuspension(const char* cause) ACQUIRE(Roles::uninterruptible_) { @@ -1017,11 +1015,15 @@ class Thread { // Dbg::Disconnected. ThreadState SetStateUnsafe(ThreadState new_state) { ThreadState old_state = GetState(); - tls32_.state_and_flags.as_struct.state = new_state; - // if transit to a suspended state, check the pass barrier request. - if (UNLIKELY((new_state != kRunnable) && - (tls32_.state_and_flags.as_struct.flags & kActiveSuspendBarrier))) { - PassActiveSuspendBarriers(this); + if (old_state == kRunnable && new_state != kRunnable) { + // Need to run pending checkpoint and suspend barriers. Run checkpoints in runnable state in + // case they need to use a ScopedObjectAccess. If we are holding the mutator lock and a SOA + // attempts to TransitionFromSuspendedToRunnable, it results in a deadlock. + TransitionToSuspendedAndRunCheckpoints(new_state); + // Since we transitioned to a suspended state, check the pass barrier requests. + PassActiveSuspendBarriers(); + } else { + tls32_.state_and_flags.as_struct.state = new_state; } return old_state; } @@ -1064,6 +1066,12 @@ class Thread { void SetUpAlternateSignalStack(); void TearDownAlternateSignalStack(); + ALWAYS_INLINE void TransitionToSuspendedAndRunCheckpoints(ThreadState new_state) + REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_); + + ALWAYS_INLINE void PassActiveSuspendBarriers() + REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_); + // 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to // change from being Suspended to Runnable without a suspend request occurring. union PACKED(4) StateAndFlags { diff --git a/test/024-illegal-access/expected.txt b/test/024-illegal-access/expected.txt index 5f951f4939..0ae4a774f3 100644 --- a/test/024-illegal-access/expected.txt +++ b/test/024-illegal-access/expected.txt @@ -1,2 +1,5 @@ Got expected failure 1 Got expected failure 2 +Got expected failure 3 +Got expected failure 4 +Got expected failure 5 diff --git a/test/024-illegal-access/src/Main.java b/test/024-illegal-access/src/Main.java index bde73e9452..84c7114cb4 100644 --- a/test/024-illegal-access/src/Main.java +++ b/test/024-illegal-access/src/Main.java @@ -17,7 +17,7 @@ public class Main { static public void main(String[] args) { try { - PublicAccess.main(); + PublicAccess.accessStaticField(); System.err.println("ERROR: call 1 not expected to succeed"); } catch (VerifyError ve) { // dalvik @@ -28,14 +28,41 @@ public class Main { } try { - CheckInstanceof.main(new Object()); + PublicAccess.accessStaticMethod(); System.err.println("ERROR: call 2 not expected to succeed"); + } catch (IllegalAccessError iae) { + // reference + System.out.println("Got expected failure 2"); + } + + try { + PublicAccess.accessInstanceField(); + System.err.println("ERROR: call 3 not expected to succeed"); } catch (VerifyError ve) { // dalvik - System.out.println("Got expected failure 2"); + System.out.println("Got expected failure 3"); } catch (IllegalAccessError iae) { // reference - System.out.println("Got expected failure 2"); + System.out.println("Got expected failure 3"); + } + + try { + PublicAccess.accessInstanceMethod(); + System.err.println("ERROR: call 4 not expected to succeed"); + } catch (IllegalAccessError iae) { + // reference + System.out.println("Got expected failure 4"); + } + + try { + CheckInstanceof.main(new Object()); + System.err.println("ERROR: call 5 not expected to succeed"); + } catch (VerifyError ve) { + // dalvik + System.out.println("Got expected failure 5"); + } catch (IllegalAccessError iae) { + // reference + System.out.println("Got expected failure 5"); } } } diff --git a/test/024-illegal-access/src/PublicAccess.java b/test/024-illegal-access/src/PublicAccess.java index 4e72cd4dce..e3fef855e2 100644 --- a/test/024-illegal-access/src/PublicAccess.java +++ b/test/024-illegal-access/src/PublicAccess.java @@ -18,8 +18,20 @@ * Some stuff for access checks. */ public class PublicAccess { - public static void main() { - String shouldFail = SemiPrivate.mPrivvy; + public static void accessStaticField() { + String shouldFail = SemiPrivate.mStaticPrivvy; + System.out.println("Got " + shouldFail); + } + public static void accessStaticMethod() { + String shouldFail = SemiPrivate.privvyStaticMethod(); + System.out.println("Got " + shouldFail); + } + public static void accessInstanceField() { + String shouldFail = new SemiPrivate().mInstancePrivvy; + System.out.println("Got " + shouldFail); + } + public static void accessInstanceMethod() { + String shouldFail = new SemiPrivate().privvyInstanceMethod(); System.out.println("Got " + shouldFail); } } diff --git a/test/024-illegal-access/src/SemiPrivate.java b/test/024-illegal-access/src/SemiPrivate.java index 06b16c40b9..62e0d05213 100644 --- a/test/024-illegal-access/src/SemiPrivate.java +++ b/test/024-illegal-access/src/SemiPrivate.java @@ -18,5 +18,15 @@ * Version with package scope access. */ public class SemiPrivate { - /* not private */ static String mPrivvy = "stuff"; + /* not private */ static String mStaticPrivvy = "stuff"; + + /* not private */ static String privvyStaticMethod() { + return "stuff"; + } + + /* not private */ String mInstancePrivvy = "stuff"; + + /* not private */ String privvyInstanceMethod() { + return "stuff"; + } } diff --git a/test/024-illegal-access/src2/SemiPrivate.java b/test/024-illegal-access/src2/SemiPrivate.java index 064265ab37..4f36a07418 100644 --- a/test/024-illegal-access/src2/SemiPrivate.java +++ b/test/024-illegal-access/src2/SemiPrivate.java @@ -18,5 +18,15 @@ * Version with private access. */ public class SemiPrivate { - private static String mPrivvy = "stuff"; + private static String mStaticPrivvy = "stuff"; + + private static String privvyStaticMethod() { + return "stuff"; + } + + private String mInstancePrivvy = "stuff"; + + private String privvyInstanceMethod() { + return "stuff"; + } } diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java index 6f047974b3..adb5adae82 100644 --- a/test/529-checker-unresolved/src/Main.java +++ b/test/529-checker-unresolved/src/Main.java @@ -44,6 +44,76 @@ public class Main extends UnresolvedSuperClass { super.superMethod(); } + /// CHECK-START: void Main.callUnresolvedStaticFieldAccess() register (before) + /// CHECK: UnresolvedStaticFieldSet field_type:PrimByte + /// CHECK: UnresolvedStaticFieldSet field_type:PrimChar + /// CHECK: UnresolvedStaticFieldSet field_type:PrimInt + /// CHECK: UnresolvedStaticFieldSet field_type:PrimLong + /// CHECK: UnresolvedStaticFieldSet field_type:PrimFloat + /// CHECK: UnresolvedStaticFieldSet field_type:PrimDouble + /// CHECK: UnresolvedStaticFieldSet field_type:PrimNot + + /// CHECK: UnresolvedStaticFieldGet field_type:PrimByte + /// CHECK: UnresolvedStaticFieldGet field_type:PrimChar + /// CHECK: UnresolvedStaticFieldGet field_type:PrimInt + /// CHECK: UnresolvedStaticFieldGet field_type:PrimLong + /// CHECK: UnresolvedStaticFieldGet field_type:PrimFloat + /// CHECK: UnresolvedStaticFieldGet field_type:PrimDouble + /// CHECK: UnresolvedStaticFieldGet field_type:PrimNot + static public void callUnresolvedStaticFieldAccess() { + Object o = new Object(); + UnresolvedClass.staticByte = (byte)1; + UnresolvedClass.staticChar = '1'; + UnresolvedClass.staticInt = 123456789; + UnresolvedClass.staticLong = 123456789123456789l; + UnresolvedClass.staticFloat = 123456789123456789f; + UnresolvedClass.staticDouble = 123456789123456789d; + UnresolvedClass.staticObject = o; + + expectEquals((byte)1, UnresolvedClass.staticByte); + expectEquals('1', UnresolvedClass.staticChar); + expectEquals(123456789, UnresolvedClass.staticInt); + expectEquals(123456789123456789l, UnresolvedClass.staticLong); + expectEquals(123456789123456789f, UnresolvedClass.staticFloat); + expectEquals(123456789123456789d, UnresolvedClass.staticDouble); + expectEquals(o, UnresolvedClass.staticObject); + } + + /// CHECK-START: void Main.callUnresolvedInstanceFieldAccess(UnresolvedClass) register (before) + /// CHECK: UnresolvedInstanceFieldSet field_type:PrimByte + /// CHECK: UnresolvedInstanceFieldSet field_type:PrimChar + /// CHECK: UnresolvedInstanceFieldSet field_type:PrimInt + /// CHECK: UnresolvedInstanceFieldSet field_type:PrimLong + /// CHECK: UnresolvedInstanceFieldSet field_type:PrimFloat + /// CHECK: UnresolvedInstanceFieldSet field_type:PrimDouble + /// CHECK: UnresolvedInstanceFieldSet field_type:PrimNot + + /// CHECK: UnresolvedInstanceFieldGet field_type:PrimByte + /// CHECK: UnresolvedInstanceFieldGet field_type:PrimChar + /// CHECK: UnresolvedInstanceFieldGet field_type:PrimInt + /// CHECK: UnresolvedInstanceFieldGet field_type:PrimLong + /// CHECK: UnresolvedInstanceFieldGet field_type:PrimFloat + /// CHECK: UnresolvedInstanceFieldGet field_type:PrimDouble + /// CHECK: UnresolvedInstanceFieldGet field_type:PrimNot + static public void callUnresolvedInstanceFieldAccess(UnresolvedClass c) { + Object o = new Object(); + c.instanceByte = (byte)1; + c.instanceChar = '1'; + c.instanceInt = 123456789; + c.instanceLong = 123456789123456789l; + c.instanceFloat = 123456789123456789f; + c.instanceDouble = 123456789123456789d; + c.instanceObject = o; + + expectEquals((byte)1, c.instanceByte); + expectEquals('1', c.instanceChar); + expectEquals(123456789, c.instanceInt); + expectEquals(123456789123456789l, c.instanceLong); + expectEquals(123456789123456789f, c.instanceFloat); + expectEquals(123456789123456789d, c.instanceDouble); + expectEquals(o, c.instanceObject); + } + /// CHECK-START: void Main.main(java.lang.String[]) register (before) /// CHECK: InvokeUnresolved invoke_type:direct static public void main(String[] args) { @@ -52,5 +122,49 @@ public class Main extends UnresolvedSuperClass { callInvokeUnresolvedVirtual(c); callInvokeUnresolvedInterface(c); callInvokeUnresolvedSuper(new Main()); + callUnresolvedStaticFieldAccess(); + callUnresolvedInstanceFieldAccess(c); + } + + public static void expectEquals(byte expected, byte result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(char expected, char result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(float expected, float result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(double expected, double result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(Object expected, Object result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } } } diff --git a/test/529-checker-unresolved/src/Unresolved.java b/test/529-checker-unresolved/src/Unresolved.java index 5bf92dd331..03ceb6857b 100644 --- a/test/529-checker-unresolved/src/Unresolved.java +++ b/test/529-checker-unresolved/src/Unresolved.java @@ -40,6 +40,22 @@ class UnresolvedClass extends UnresolvedSuperClass implements UnresolvedInterfac public void interfaceMethod() { System.out.println("UnresolvedClass.interfaceMethod()"); } + + public static byte staticByte; + public static char staticChar; + public static int staticInt; + public static long staticLong; + public static float staticFloat; + public static double staticDouble; + public static Object staticObject; + + public byte instanceByte; + public char instanceChar; + public int instanceInt; + public long instanceLong; + public float instanceFloat; + public double instanceDouble; + public Object instanceObject; } final class UnresolvedFinalClass { diff --git a/test/535-deopt-and-inlining/expected.txt b/test/535-deopt-and-inlining/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/535-deopt-and-inlining/expected.txt diff --git a/test/535-deopt-and-inlining/info.txt b/test/535-deopt-and-inlining/info.txt new file mode 100644 index 0000000000..717612a1ad --- /dev/null +++ b/test/535-deopt-and-inlining/info.txt @@ -0,0 +1,2 @@ +Stress test for deoptimization and JIT, to ensure the +stack visitor uses the right ArtMethod when deopting. diff --git a/test/535-deopt-and-inlining/src/Main.java b/test/535-deopt-and-inlining/src/Main.java new file mode 100644 index 0000000000..c231bf0e87 --- /dev/null +++ b/test/535-deopt-and-inlining/src/Main.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + public static void run() { + // Loop enough to get JIT compilation. + for (int i = 0; i < 10000; ++i) { + doCall(new int[0]); + } + } + + public static void main(String[] args) throws Exception { + run(); + } + + public static void doCall(int[] array) { + try { + deopt(array); + } catch (IndexOutOfBoundsException ioobe) { + // Expected + } + } + + public static void deopt(int[] array) { + // Invoke `deopt` much more than `$inline$deopt` so that only `deopt` gets + // initially JITted. + if (call == 100) { + call = 0; + $inline$deopt(array); + } else { + call++; + } + } + + public static void $inline$deopt(int[] array) { + array[0] = 1; + array[1] = 1; + } + + static int call = 0; +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 49778cb3a6..a103eacf6d 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -490,14 +490,6 @@ ifneq (,$(filter optimizing,$(COMPILER_TYPES))) $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES)) endif -# If ART_USE_OPTIMIZING_COMPILER is set to true, then the default core.art has been -# compiled with the optimizing compiler. -ifeq ($(ART_USE_OPTIMIZING_COMPILER),true) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS := # Tests that should fail when the optimizing compiler compiles them non-debuggable. diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk index 3c1522c3d8..71366c1313 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -16,6 +16,8 @@ LOCAL_PATH := $(call my-dir) +include art/build/Android.common_test.mk + # --- ahat.jar ---------------- include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, src) @@ -44,7 +46,7 @@ $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/ahat $(ACP) ahat: $(LOCAL_BUILT_MODULE) -# --- ahat-test.jar -------------- +# --- ahat-tests.jar -------------- include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, test) LOCAL_JAR_MANIFEST := test/manifest.txt @@ -53,6 +55,42 @@ LOCAL_IS_HOST_MODULE := true LOCAL_MODULE_TAGS := tests LOCAL_MODULE := ahat-tests include $(BUILD_HOST_JAVA_LIBRARY) +AHAT_TEST_JAR := $(LOCAL_BUILT_MODULE) + +# --- ahat-test-dump.jar -------------- +include $(CLEAR_VARS) +LOCAL_MODULE := ahat-test-dump +LOCAL_MODULE_TAGS := tests +LOCAL_SRC_FILES := $(call all-java-files-under, test-dump) +include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) + +# Determine the location of the test-dump.jar and test-dump.hprof files. +# These use variables set implicitly by the include of +# BUILD_HOST_DALVIK_JAVA_LIBRARY above. +AHAT_TEST_DUMP_JAR := $(LOCAL_BUILT_MODULE) +AHAT_TEST_DUMP_HPROF := $(intermediates.COMMON)/test-dump.hprof + +# Run ahat-test-dump.jar to generate test-dump.hprof +AHAT_TEST_DUMP_DEPENDENCIES := \ + $(ART_HOST_EXECUTABLES) \ + $(HOST_OUT_EXECUTABLES)/art \ + $(HOST_CORE_IMG_OUT_BASE)$(CORE_IMG_SUFFIX) + +$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_ART := $(HOST_OUT_EXECUTABLES)/art +$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_JAR := $(AHAT_TEST_DUMP_JAR) +$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_DEPENDENCIES := $(AHAT_TEST_DUMP_DEPENDENCIES) +$(AHAT_TEST_DUMP_HPROF): $(AHAT_TEST_DUMP_JAR) $(AHAT_TEST_DUMP_DEPENDENCIES) + $(PRIVATE_AHAT_TEST_ART) -cp $(PRIVATE_AHAT_TEST_DUMP_JAR) Main $@ + +.PHONY: ahat-test +ahat-test: PRIVATE_AHAT_TEST_DUMP_HPROF := $(AHAT_TEST_DUMP_HPROF) +ahat-test: PRIVATE_AHAT_TEST_JAR := $(AHAT_TEST_JAR) +ahat-test: $(AHAT_TEST_JAR) $(AHAT_TEST_DUMP_HPROF) + java -Dahat.test.dump.hprof=$(PRIVATE_AHAT_TEST_DUMP_HPROF) -jar $(PRIVATE_AHAT_TEST_JAR) + +# Clean up local variables. +AHAT_TEST_DUMP_DEPENDENCIES := +AHAT_TEST_DUMP_HPROF := +AHAT_TEST_DUMP_JAR := +AHAT_TEST_JAR := -ahat-test: $(LOCAL_BUILT_MODULE) - java -jar $< diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java index 2437d0388c..3035ef75c9 100644 --- a/tools/ahat/src/AhatSnapshot.java +++ b/tools/ahat/src/AhatSnapshot.java @@ -18,13 +18,18 @@ package com.android.ahat; import com.android.tools.perflib.heap.ClassObj; import com.android.tools.perflib.heap.Heap; +import com.android.tools.perflib.heap.HprofParser; import com.android.tools.perflib.heap.Instance; import com.android.tools.perflib.heap.RootObj; import com.android.tools.perflib.heap.Snapshot; import com.android.tools.perflib.heap.StackFrame; import com.android.tools.perflib.heap.StackTrace; +import com.android.tools.perflib.heap.io.HprofBuffer; +import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -47,7 +52,22 @@ class AhatSnapshot { private Site mRootSite; private Map<Heap, Long> mHeapSizes; - public AhatSnapshot(Snapshot snapshot) { + /** + * Create an AhatSnapshot from an hprof file. + */ + public static AhatSnapshot fromHprof(File hprof) throws IOException { + HprofBuffer buffer = new MemoryMappedFileBuffer(hprof); + Snapshot snapshot = (new HprofParser(buffer)).parse(); + snapshot.computeDominators(); + return new AhatSnapshot(snapshot); + } + + /** + * Construct an AhatSnapshot for the given perflib snapshot. + * Ther user is responsible for calling snapshot.computeDominators before + * calling this AhatSnapshot constructor. + */ + private AhatSnapshot(Snapshot snapshot) { mSnapshot = snapshot; mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps()); mDominated = new HashMap<Instance, List<Instance>>(); @@ -92,6 +112,11 @@ class AhatSnapshot { } } + // Note: This method is exposed for testing purposes. + public ClassObj findClass(String name) { + return mSnapshot.findClass(name); + } + public Instance findInstance(long id) { return mSnapshot.findInstance(id); } diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java index 7ee3ff24ed..a6ac3b8765 100644 --- a/tools/ahat/src/InstanceUtils.java +++ b/tools/ahat/src/InstanceUtils.java @@ -32,7 +32,7 @@ class InstanceUtils { * given name. */ public static boolean isInstanceOfClass(Instance inst, String className) { - ClassObj cls = inst.getClassObj(); + ClassObj cls = (inst == null) ? null : inst.getClassObj(); return (cls != null && className.equals(cls.getClassName())); } @@ -132,7 +132,7 @@ class InstanceUtils { * Read a field of an instance. * Returns null if the field value is null or if the field couldn't be read. */ - private static Object getField(Instance inst, String fieldName) { + public static Object getField(Instance inst, String fieldName) { if (!(inst instanceof ClassInstance)) { return null; } diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java index 2e2ddd299f..1563aa0262 100644 --- a/tools/ahat/src/Main.java +++ b/tools/ahat/src/Main.java @@ -16,10 +16,6 @@ package com.android.ahat; -import com.android.tools.perflib.heap.HprofParser; -import com.android.tools.perflib.heap.Snapshot; -import com.android.tools.perflib.heap.io.HprofBuffer; -import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer; import com.sun.net.httpserver.HttpServer; import java.io.File; import java.io.IOException; @@ -71,15 +67,8 @@ public class Main { return; } - System.out.println("Reading hprof file..."); - HprofBuffer buffer = new MemoryMappedFileBuffer(hprof); - Snapshot snapshot = (new HprofParser(buffer)).parse(); - - System.out.println("Computing Dominators..."); - snapshot.computeDominators(); - - System.out.println("Processing snapshot for ahat..."); - AhatSnapshot ahat = new AhatSnapshot(snapshot); + System.out.println("Processing hprof file..."); + AhatSnapshot ahat = AhatSnapshot.fromHprof(hprof); InetAddress loopback = InetAddress.getLoopbackAddress(); InetSocketAddress addr = new InetSocketAddress(loopback, port); diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java new file mode 100644 index 0000000000..cea1dc179e --- /dev/null +++ b/tools/ahat/test-dump/Main.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import dalvik.system.VMDebug; +import java.io.IOException; + +/** + * Program used to create a heap dump for test purposes. + */ +public class Main { + // Keep a reference to the DumpedStuff instance so that it is not garbage + // collected before we take the heap dump. + public static DumpedStuff stuff; + + // We will take a heap dump that includes a single instance of this + // DumpedStuff class. Objects stored as fields in this class can be easily + // found in the hprof dump by searching for the instance of the DumpedStuff + // class and reading the desired field. + public static class DumpedStuff { + public String basicString = "hello, world"; + public String nullString = null; + public Object anObject = new Object(); + } + + public static void main(String[] args) throws IOException { + if (args.length < 1) { + System.err.println("no output file specified"); + return; + } + String file = args[0]; + + // Allocate the instance of DumpedStuff. + stuff = new DumpedStuff(); + + // Take a heap dump that will include that instance of DumpedStuff. + System.err.println("Dumping hprof data to " + file); + VMDebug.dumpHprofData(file); + } +} diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java new file mode 100644 index 0000000000..7613df4994 --- /dev/null +++ b/tools/ahat/test/InstanceUtilsTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat; + +import com.android.tools.perflib.heap.Instance; +import java.io.IOException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import org.junit.Test; + +public class InstanceUtilsTest { + @Test + public void basicString() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("basicString"); + assertEquals("hello, world", InstanceUtils.asString(str)); + } + + @Test + public void nullString() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance obj = (Instance)dump.getDumpedThing("nullString"); + assertNull(InstanceUtils.asString(obj)); + } + + @Test + public void notString() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance obj = (Instance)dump.getDumpedThing("anObject"); + assertNotNull(obj); + assertNull(InstanceUtils.asString(obj)); + } +} diff --git a/tools/ahat/test/TestDump.java b/tools/ahat/test/TestDump.java new file mode 100644 index 0000000000..c3a76e4f18 --- /dev/null +++ b/tools/ahat/test/TestDump.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat; + +import com.android.tools.perflib.heap.ClassObj; +import com.android.tools.perflib.heap.Field; +import com.android.tools.perflib.heap.Instance; +import java.io.File; +import java.io.IOException; +import java.util.Map; + +/** + * The TestDump class is used to get an AhatSnapshot for the test-dump + * program. + */ +public class TestDump { + // It can take on the order of a second to parse and process the test-dump + // hprof. To avoid repeating this overhead for each test case, we cache the + // loaded instance of TestDump and reuse it when possible. In theory the + // test cases should not be able to modify the cached snapshot in a way that + // is visible to other test cases. + private static TestDump mCachedTestDump = null; + + private AhatSnapshot mSnapshot = null; + + /** + * Load the test-dump.hprof file. + * The location of the file is read from the system property + * "ahat.test.dump.hprof", which is expected to be set on the command line. + * For example: + * java -Dahat.test.dump.hprof=test-dump.hprof -jar ahat-tests.jar + * + * An IOException is thrown if there is a failure reading the hprof file. + */ + private TestDump() throws IOException { + String hprof = System.getProperty("ahat.test.dump.hprof"); + mSnapshot = AhatSnapshot.fromHprof(new File(hprof)); + } + + /** + * Get the AhatSnapshot for the test dump program. + */ + public AhatSnapshot getAhatSnapshot() { + return mSnapshot; + } + + /** + * Return the value of a field in the DumpedStuff instance in the + * snapshot for the test-dump program. + */ + public Object getDumpedThing(String name) { + ClassObj main = mSnapshot.findClass("Main"); + Instance stuff = null; + for (Map.Entry<Field, Object> fields : main.getStaticFieldValues().entrySet()) { + if ("stuff".equals(fields.getKey().getName())) { + stuff = (Instance) fields.getValue(); + } + } + return InstanceUtils.getField(stuff, name); + } + + /** + * Get the test dump. + * An IOException is thrown if there is an error reading the test dump hprof + * file. + * To improve performance, this returns a cached instance of the TestDump + * when possible. + */ + public static synchronized TestDump getTestDump() throws IOException { + if (mCachedTestDump == null) { + mCachedTestDump = new TestDump(); + } + return mCachedTestDump; + } +} diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java index fb53d90801..bab712199c 100644 --- a/tools/ahat/test/Tests.java +++ b/tools/ahat/test/Tests.java @@ -22,6 +22,7 @@ public class Tests { public static void main(String[] args) { if (args.length == 0) { args = new String[]{ + "com.android.ahat.InstanceUtilsTest", "com.android.ahat.QueryTest", "com.android.ahat.SortTest" }; |