diff options
32 files changed, 497 insertions, 225 deletions
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index eb4915b821..6f9dd6d268 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -1679,9 +1679,7 @@ void MIRGraph::StringChange() { if (opcode == Instruction::NEW_INSTANCE) { uint32_t type_idx = mir->dalvikInsn.vB; if (cu_->compiler_driver->IsStringTypeIndex(type_idx, cu_->dex_file)) { - // Change NEW_INSTANCE into CONST_4 of 0 - mir->dalvikInsn.opcode = Instruction::CONST_4; - mir->dalvikInsn.vB = 0; + LOG(FATAL) << "Quick cannot compile String allocations"; } } else if ((opcode == Instruction::INVOKE_DIRECT) || (opcode == Instruction::INVOKE_DIRECT_RANGE)) { @@ -1689,52 +1687,13 @@ void MIRGraph::StringChange() { DexFileMethodInliner* inliner = cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file); if (inliner->IsStringInitMethodIndex(method_idx)) { - bool is_range = (opcode == Instruction::INVOKE_DIRECT_RANGE); - uint32_t orig_this_reg = is_range ? mir->dalvikInsn.vC : mir->dalvikInsn.arg[0]; - // Remove this pointer from string init and change to static call. - mir->dalvikInsn.vA--; - if (!is_range) { - mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC; - for (uint32_t i = 0; i < mir->dalvikInsn.vA; i++) { - mir->dalvikInsn.arg[i] = mir->dalvikInsn.arg[i + 1]; - } - } else { - mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC_RANGE; - mir->dalvikInsn.vC++; - } - // Insert a move-result instruction to the original this pointer reg. - MIR* move_result_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR)); - move_result_mir->dalvikInsn.opcode = Instruction::MOVE_RESULT_OBJECT; - move_result_mir->dalvikInsn.vA = orig_this_reg; - move_result_mir->offset = mir->offset; - move_result_mir->m_unit_index = mir->m_unit_index; - bb->InsertMIRAfter(mir, move_result_mir); - // Add additional moves if this pointer was copied to other registers. - const VerifiedMethod* verified_method = - cu_->compiler_driver->GetVerifiedMethod(cu_->dex_file, cu_->method_idx); - DCHECK(verified_method != nullptr); - const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map = - verified_method->GetStringInitPcRegMap(); - auto map_it = string_init_map.find(mir->offset); - if (map_it != string_init_map.end()) { - const std::set<uint32_t>& reg_set = map_it->second; - for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { - MIR* move_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR)); - move_mir->dalvikInsn.opcode = Instruction::MOVE_OBJECT; - move_mir->dalvikInsn.vA = *set_it; - move_mir->dalvikInsn.vB = orig_this_reg; - move_mir->offset = mir->offset; - move_mir->m_unit_index = mir->m_unit_index; - bb->InsertMIRAfter(move_result_mir, move_mir); - } - } + LOG(FATAL) << "Quick cannot compile String allocations"; } } } } } - bool MIRGraph::EliminateSuspendChecksGate() { if (kLeafOptimization || // Incompatible (could create loops without suspend checks). (cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled. diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc index 027290f9b1..49768ded46 100644 --- a/compiler/dex/quick/quick_compiler.cc +++ b/compiler/dex/quick/quick_compiler.cc @@ -509,7 +509,8 @@ static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set) } bool QuickCompiler::CanCompileInstruction(const MIR* mir, - const DexFile& dex_file) const { + const DexFile& dex_file, + CompilationUnit* cu) const { switch (mir->dalvikInsn.opcode) { // Quick compiler won't support new instruction semantics to invoke-super into an interface // method @@ -522,6 +523,13 @@ bool QuickCompiler::CanCompileInstruction(const MIR* mir, // False if we are an interface i.e. !(java_access_flags & kAccInterface) return class_def != nullptr && ((class_def->GetJavaAccessFlags() & kAccInterface) == 0); } + case Instruction::NEW_INSTANCE: { + uint32_t type_idx = mir->dalvikInsn.vB; + if (cu->compiler_driver->IsStringTypeIndex(type_idx, cu->dex_file)) { + return false; + } + return true; + } default: return true; } @@ -567,7 +575,7 @@ bool QuickCompiler::CanCompileMethod(uint32_t method_idx, << MIRGraph::extended_mir_op_names_[opcode - kMirOpFirst]; } return false; - } else if (!CanCompileInstruction(mir, dex_file)) { + } else if (!CanCompileInstruction(mir, dex_file, cu)) { VLOG(compiler) << "Cannot compile dalvik opcode : " << mir->dalvikInsn.opcode; return false; } diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h index 55f45f1ab0..f32cf866ca 100644 --- a/compiler/dex/quick/quick_compiler.h +++ b/compiler/dex/quick/quick_compiler.h @@ -75,7 +75,7 @@ class QuickCompiler : public Compiler { explicit QuickCompiler(CompilerDriver* driver); private: - bool CanCompileInstruction(const MIR* mir, const DexFile& dex_file) const; + bool CanCompileInstruction(const MIR* mir, const DexFile& dex_file, CompilationUnit* cu) const; std::unique_ptr<PassManager> pre_opt_pass_manager_; std::unique_ptr<PassManager> post_opt_pass_manager_; diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc index 0355f116f1..9ae21648bf 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -37,20 +37,16 @@ namespace art { -VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types, - bool has_runtime_throw, - const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map) +VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw) : encountered_error_types_(encountered_error_types), - has_runtime_throw_(has_runtime_throw), - string_init_pc_reg_map_(string_init_pc_reg_map) { + has_runtime_throw_(has_runtime_throw) { } const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier, bool compile) { std::unique_ptr<VerifiedMethod> verified_method( new VerifiedMethod(method_verifier->GetEncounteredFailureTypes(), - method_verifier->HasInstructionThatWillThrow(), - method_verifier->GetStringInitPcRegMap())); + method_verifier->HasInstructionThatWillThrow())); if (compile) { /* Generate a register map. */ diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h index 74fcb07d27..12d0219058 100644 --- a/compiler/dex/verified_method.h +++ b/compiler/dex/verified_method.h @@ -83,14 +83,8 @@ class VerifiedMethod { return has_runtime_throw_; } - const SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() const { - return string_init_pc_reg_map_; - } - private: - VerifiedMethod(uint32_t encountered_error_types, - bool has_runtime_throw, - const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map); + VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw); /* * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of @@ -129,10 +123,6 @@ class VerifiedMethod { const uint32_t encountered_error_types_; const bool has_runtime_throw_; - - // Copy of mapping generated by verifier of dex PCs of string init invocations - // to the set of other registers that the receiver has been copied into. - const SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_; }; } // namespace art diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index a48d06f3d0..13d3f752c3 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -92,6 +92,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifySystemArrayCopy(HInvoke* invoke); void SimplifyStringEquals(HInvoke* invoke); void SimplifyCompare(HInvoke* invoke, bool has_zero_op); + void SimplifyIsNaN(HInvoke* invoke); OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; @@ -1551,6 +1552,16 @@ void InstructionSimplifierVisitor::SimplifyCompare(HInvoke* invoke, bool is_sign invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, compare); } +void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + uint32_t dex_pc = invoke->GetDexPc(); + // IsNaN(x) is the same as x != x. + HInstruction* x = invoke->InputAt(0); + HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc); + condition->SetBias(ComparisonBias::kLtBias); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition); +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) { SimplifyStringEquals(instruction); @@ -1568,6 +1579,9 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum || instruction->GetIntrinsic() == Intrinsics::kLongSignum) { SimplifyCompare(instruction, /* is_signum */ true); + } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN || + instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) { + SimplifyIsNaN(instruction); } } diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 00a158b10a..ea8669fa18 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1858,8 +1858,6 @@ UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) @@ -1867,6 +1865,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) UNIMPLEMENTED_INTRINSIC(LongRotateLeft) UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 4140d94e17..8741fd284f 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1618,8 +1618,6 @@ UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) @@ -1627,6 +1625,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) UNIMPLEMENTED_INTRINSIC(LongRotateLeft) UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 5d6e8c280f..c8629644b6 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1220,8 +1220,6 @@ UNIMPLEMENTED_INTRINSIC(MathTanh) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) @@ -1229,6 +1227,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) UNIMPLEMENTED_INTRINSIC(LongCompare) UNIMPLEMENTED_INTRINSIC(IntegerSignum) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index ac2850342d..cf3a3657de 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1764,8 +1764,6 @@ UNIMPLEMENTED_INTRINSIC(MathTanh) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) @@ -1773,6 +1771,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) UNIMPLEMENTED_INTRINSIC(LongCompare) UNIMPLEMENTED_INTRINSIC(IntegerSignum) diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 22cefe8aa5..260a8773fb 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2635,8 +2635,6 @@ UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) @@ -2644,6 +2642,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) UNIMPLEMENTED_INTRINSIC(LongRotateLeft) UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index c9a43442b3..93e8c00e5a 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2717,10 +2717,10 @@ UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) UNIMPLEMENTED_INTRINSIC(LongRotateLeft) UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 43f2499b24..09ca8b7b44 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -422,6 +422,34 @@ bool SsaBuilder::FixAmbiguousArrayOps() { return true; } +static bool HasAliasInEnvironments(HInstruction* instruction) { + for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses()); + !use_it.Done(); + use_it.Advance()) { + HEnvironment* use = use_it.Current()->GetUser(); + HUseListNode<HEnvironment*>* next = use_it.Current()->GetNext(); + if (next != nullptr && next->GetUser() == use) { + return true; + } + } + + if (kIsDebugBuild) { + // Do a quadratic search to ensure same environment uses are next + // to each other. + for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses()); + !use_it.Done(); + use_it.Advance()) { + HUseListNode<HEnvironment*>* current = use_it.Current(); + HUseListNode<HEnvironment*>* next = current->GetNext(); + while (next != nullptr) { + DCHECK(next->GetUser() != current->GetUser()); + next = next->GetNext(); + } + } + } + return false; +} + void SsaBuilder::RemoveRedundantUninitializedStrings() { if (GetGraph()->IsDebuggable()) { // Do not perform the optimization for consistency with the interpreter @@ -433,7 +461,7 @@ void SsaBuilder::RemoveRedundantUninitializedStrings() { // Replace NewInstance of String with NullConstant if not used prior to // calling StringFactory. In case of deoptimization, the interpreter is // expected to skip null check on the `this` argument of the StringFactory call. - if (!new_instance->HasNonEnvironmentUses()) { + if (!new_instance->HasNonEnvironmentUses() && !HasAliasInEnvironments(new_instance)) { new_instance->ReplaceWith(GetGraph()->GetNullConstant()); new_instance->GetBlock()->RemoveInstruction(new_instance); diff --git a/runtime/Android.mk b/runtime/Android.mk index e9f7add1af..947de8a79e 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -238,6 +238,7 @@ LIBART_HOST_LDFLAGS := # (empty) body is called. JIT_DEBUG_REGISTER_CODE_LDFLAGS := -Wl,--keep-unique,__jit_debug_register_code LIBART_TARGET_LDFLAGS_arm := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS) +LIBART_TARGET_LDFLAGS_arm64 := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS) LIBART_TARGET_LDFLAGS_x86 := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS) LIBART_TARGET_LDFLAGS_x86_64 := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS) JIT_DEBUG_REGISTER_CODE_LDFLAGS := diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 82a5f9611c..6972b3ef3f 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -1009,10 +1009,6 @@ void Locks::Init() { DCHECK(alloc_tracker_lock_ == nullptr); alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kInterpreterStringInitMapLock); - DCHECK(interpreter_string_init_map_lock_ == nullptr); - interpreter_string_init_map_lock_ = new Mutex("Interpreter String initializer reference map lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock); DCHECK(thread_list_lock_ == nullptr); thread_list_lock_ = new Mutex("thread list lock", current_lock_level); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index f674a6f3c8..e72f2a2e7b 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -102,7 +102,6 @@ enum LockLevel { kMonitorListLock, kJniLoadLibraryLock, kThreadListLock, - kInterpreterStringInitMapLock, kAllocTrackerLock, kDeoptimizationLock, kProfilerLock, diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h index 9b56856f11..bd19d00544 100644 --- a/runtime/base/scoped_arena_containers.h +++ b/runtime/base/scoped_arena_containers.h @@ -201,11 +201,12 @@ inline ScopedArenaAllocatorAdapter<void> ScopedArenaAllocator::Adapter(ArenaAllo template <typename T> class ArenaDelete { static constexpr uint8_t kMagicFill = 0xCE; + protected: // Used for variable sized objects such as RegisterLine. ALWAYS_INLINE void ProtectMemory(T* ptr, size_t size) const { if (RUNNING_ON_MEMORY_TOOL > 0) { - // Writing to the memory will fail if it we already destroyed the pointer with + // Writing to the memory will fail ift we already destroyed the pointer with // DestroyOnlyDelete since we make it no access. memset(ptr, kMagicFill, size); MEMORY_TOOL_MAKE_NOACCESS(ptr, size); @@ -220,8 +221,10 @@ class ArenaDelete { public: void operator()(T* ptr) const { - ptr->~T(); - ProtectMemory(ptr, sizeof(T)); + if (ptr != nullptr) { + ptr->~T(); + ProtectMemory(ptr, sizeof(T)); + } } }; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 0c06c386b5..894ce9af72 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -676,13 +676,17 @@ class RelocationRange { dest_(dest), length_(length) {} - bool ContainsSource(uintptr_t address) const { + bool InSource(uintptr_t address) const { return address - source_ < length_; } + bool InDest(uintptr_t address) const { + return address - dest_ < length_; + } + // Translate a source address to the destination space. uintptr_t ToDest(uintptr_t address) const { - DCHECK(ContainsSource(address)); + DCHECK(InSource(address)); return address + Delta(); } @@ -724,24 +728,28 @@ class FixupVisitor : public ValueObject { template <typename T> ALWAYS_INLINE T* ForwardObject(T* src) const { const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src); - if (boot_image_.ContainsSource(uint_src)) { + if (boot_image_.InSource(uint_src)) { return reinterpret_cast<T*>(boot_image_.ToDest(uint_src)); } - if (app_image_.ContainsSource(uint_src)) { + if (app_image_.InSource(uint_src)) { return reinterpret_cast<T*>(app_image_.ToDest(uint_src)); } + // Since we are fixing up the app image, there should only be pointers to the app image and + // boot image. + DCHECK(src == nullptr) << reinterpret_cast<const void*>(src); return src; } // Return the relocated address of a code pointer (contained by an oat file). ALWAYS_INLINE const void* ForwardCode(const void* src) const { const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src); - if (boot_oat_.ContainsSource(uint_src)) { + if (boot_oat_.InSource(uint_src)) { return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src)); } - if (app_oat_.ContainsSource(uint_src)) { + if (app_oat_.InSource(uint_src)) { return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src)); } + DCHECK(src == nullptr) << src; return src; } @@ -766,6 +774,11 @@ class FixupObjectAdapter : public FixupVisitor { template<typename... Args> explicit FixupObjectAdapter(Args... args) : FixupVisitor(args...) {} + // Must be called on pointers that already have been relocated to the destination relocation. + ALWAYS_INLINE bool IsInAppImage(mirror::Object* object) const { + return app_image_.InDest(reinterpret_cast<uintptr_t>(object)); + } + template <typename T> T* operator()(T* obj) const { return ForwardObject(obj); @@ -816,7 +829,10 @@ class FixupRootVisitor : public FixupVisitor { class FixupObjectVisitor : public FixupVisitor { public: template<typename... Args> - explicit FixupObjectVisitor(Args... args) : FixupVisitor(args...) {} + explicit FixupObjectVisitor(gc::accounting::ContinuousSpaceBitmap* pointer_array_visited, + Args... args) + : FixupVisitor(args...), + pointer_array_visited_(pointer_array_visited) {} // Fix up separately since we also need to fix up method entrypoints. ALWAYS_INLINE void VisitRootIfNonNull( @@ -841,6 +857,19 @@ class FixupObjectVisitor : public FixupVisitor { } } + // Visit a pointer array and forward corresponding native data. Ignores pointer arrays in the + // boot image. Uses the bitmap to ensure the same array is not visited multiple times. + template <typename Visitor> + void VisitPointerArray(mirror::PointerArray* array, const Visitor& visitor) const + NO_THREAD_SAFETY_ANALYSIS { + if (array != nullptr && + visitor.IsInAppImage(array) && + !pointer_array_visited_->Test(array)) { + array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, sizeof(void*), visitor); + pointer_array_visited_->Set(array); + } + } + // java.lang.ref.Reference visitor. void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref) const SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { @@ -859,11 +888,9 @@ class FixupObjectVisitor : public FixupVisitor { mirror::Class* klass = obj->AsClass<kVerifyNone, kWithoutReadBarrier>(); FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_); klass->FixupNativePointers<kVerifyNone, kWithoutReadBarrier>(klass, sizeof(void*), visitor); - // Deal with the arrays. - mirror::PointerArray* vtable = klass->GetVTable<kVerifyNone, kWithoutReadBarrier>(); - if (vtable != nullptr) { - vtable->Fixup<kVerifyNone, kWithoutReadBarrier>(vtable, sizeof(void*), visitor); - } + // Deal with the pointer arrays. Use the helper function since multiple classes can reference + // the same arrays. + VisitPointerArray(klass->GetVTable<kVerifyNone, kWithoutReadBarrier>(), visitor); mirror::IfTable* iftable = klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>(); if (iftable != nullptr) { for (int32_t i = 0, count = iftable->Count(); i < count; ++i) { @@ -871,12 +898,15 @@ class FixupObjectVisitor : public FixupVisitor { mirror::PointerArray* methods = iftable->GetMethodArray<kVerifyNone, kWithoutReadBarrier>(i); DCHECK(methods != nullptr); - methods->Fixup<kVerifyNone, kWithoutReadBarrier>(methods, sizeof(void*), visitor); + VisitPointerArray(methods, visitor); } } } } } + + private: + gc::accounting::ContinuousSpaceBitmap* const pointer_array_visited_; }; class ForwardObjectAdapter { @@ -1010,9 +1040,18 @@ static bool RelocateInPlace(ImageHeader& image_header, const ImageSection& objects_section = image_header.GetImageSection(ImageHeader::kSectionObjects); uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset()); uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End()); - // Two pass approach, fix up all classes first, then fix up non class-objects. - FixupObjectVisitor fixup_object_visitor(boot_image, boot_oat, app_image, app_oat); if (fixup_image) { + // Two pass approach, fix up all classes first, then fix up non class-objects. + // The visited bitmap is used to ensure that pointer arrays are not forwarded twice. + std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> visited_bitmap( + gc::accounting::ContinuousSpaceBitmap::Create("Pointer array bitmap", + target_base, + image_header.GetImageSize())); + FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(), + boot_image, + boot_oat, + app_image, + app_oat); TimingLogger::ScopedTiming timing("Fixup classes", &logger); // Fixup class only touches app image classes, don't need the mutator lock since the space is // not yet visible to the GC. @@ -1025,7 +1064,7 @@ static bool RelocateInPlace(ImageHeader& image_header, bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor); FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat); // Fixup image roots. - CHECK(app_image.ContainsSource(reinterpret_cast<uintptr_t>( + CHECK(app_image.InSource(reinterpret_cast<uintptr_t>( image_header.GetImageRoots<kWithoutReadBarrier>()))); image_header.RelocateImageObjects(app_image.Delta()); CHECK_EQ(image_header.GetImageBegin(), target_base); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index cbaa8173d2..3453abcd64 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -733,39 +733,21 @@ static inline bool DoCallCommon(ArtMethod* called_method, } if (string_init && !self->IsExceptionPending()) { - // Set the new string result of the StringFactory. - shadow_frame.SetVRegReference(string_init_vreg_this, result->GetL()); - // Overwrite all potential copies of the original result of the new-instance of string with the - // new result of the StringFactory. Use the verifier to find this set of registers. - ArtMethod* method = shadow_frame.GetMethod(); - MethodReference method_ref = method->ToMethodReference(); - SafeMap<uint32_t, std::set<uint32_t>>* string_init_map_ptr = nullptr; - MethodRefToStringInitRegMap& method_to_string_init_map = Runtime::Current()->GetStringInitMap(); - { - MutexLock mu(self, *Locks::interpreter_string_init_map_lock_); - auto it = method_to_string_init_map.find(method_ref); - if (it != method_to_string_init_map.end()) { - string_init_map_ptr = &it->second; - } - } - if (string_init_map_ptr == nullptr) { - SafeMap<uint32_t, std::set<uint32_t>> string_init_map = - verifier::MethodVerifier::FindStringInitMap(method); - MutexLock mu(self, *Locks::interpreter_string_init_map_lock_); - auto it = method_to_string_init_map.lower_bound(method_ref); - if (it == method_to_string_init_map.end() || - method_to_string_init_map.key_comp()(method_ref, it->first)) { - it = method_to_string_init_map.PutBefore(it, method_ref, std::move(string_init_map)); - } - string_init_map_ptr = &it->second; - } - if (string_init_map_ptr->size() != 0) { - uint32_t dex_pc = shadow_frame.GetDexPC(); - auto map_it = string_init_map_ptr->find(dex_pc); - if (map_it != string_init_map_ptr->end()) { - const std::set<uint32_t>& reg_set = map_it->second; - for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { - shadow_frame.SetVRegReference(*set_it, result->GetL()); + mirror::Object* existing = shadow_frame.GetVRegReference(string_init_vreg_this); + if (existing == nullptr) { + // If it's null, we come from compiled code that was deoptimized. Nothing to do, + // as the compiler verified there was no alias. + // Set the new string result of the StringFactory. + shadow_frame.SetVRegReference(string_init_vreg_this, result->GetL()); + } else { + // Replace the fake string that was allocated with the StringFactory result. + for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { + if (shadow_frame.GetVRegReference(i) == existing) { + DCHECK_EQ(shadow_frame.GetVRegReference(i), + reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i))); + shadow_frame.SetVRegReference(i, result->GetL()); + DCHECK_EQ(shadow_frame.GetVRegReference(i), + reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i))); } } } diff --git a/runtime/runtime.h b/runtime/runtime.h index 1956bae52a..cbb3e89444 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -94,8 +94,6 @@ struct TraceConfig; class Transaction; typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions; -typedef SafeMap<MethodReference, SafeMap<uint32_t, std::set<uint32_t>>, - MethodReferenceComparator> MethodRefToStringInitRegMap; // Not all combinations of flags are valid. You may not visit all roots as well as the new roots // (no logical reason to do this). You also may not start logging new roots and stop logging new @@ -574,10 +572,6 @@ class Runtime { return jit_options_.get(); } - MethodRefToStringInitRegMap& GetStringInitMap() { - return method_ref_string_init_reg_map_; - } - bool IsDebuggable() const; // Returns the build fingerprint, if set. Otherwise an empty string is returned. @@ -803,8 +797,6 @@ class Runtime { // Experimental opcodes should not be used by other production code. ExperimentalFlags experimental_flags_; - MethodRefToStringInitRegMap method_ref_string_init_reg_map_; - // Contains the build fingerprint, if given as a parameter. std::string fingerprint_; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index a6cf9eaf86..0c6060e4e8 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -617,23 +617,6 @@ ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { return GetQuickInvokedMethod(inst, register_line, is_range, false); } -SafeMap<uint32_t, std::set<uint32_t>> MethodVerifier::FindStringInitMap(ArtMethod* m) { - Thread* self = Thread::Current(); - StackHandleScope<2> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader())); - MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(), - m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), - true, true, false, true); - // Avoid copying: The map is moved out of the verifier before the verifier is destroyed. - return std::move(verifier.FindStringInitMap()); -} - -SafeMap<uint32_t, std::set<uint32_t>>& MethodVerifier::FindStringInitMap() { - Verify(); - return GetStringInitPcRegMap(); -} - bool MethodVerifier::Verify() { // Some older code doesn't correctly mark constructors as such. Test for this case by looking at // the name. @@ -2865,8 +2848,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * Replace the uninitialized reference with an initialized one. We need to do this for all * registers that have the same object instance in them, not just the "this" register. */ - const uint32_t this_reg = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - work_line_->MarkRefsAsInitialized(this, this_type, this_reg, work_insn_idx_); + work_line_->MarkRefsAsInitialized(this, this_type); } if (return_type == nullptr) { return_type = ®_types_.FromDescriptor(GetClassLoader(), return_type_descriptor, false); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index b53a45cf41..6d8e1ab6ee 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -213,9 +213,6 @@ class MethodVerifier { static ArtMethod* FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_pc) SHARED_REQUIRES(Locks::mutator_lock_); - static SafeMap<uint32_t, std::set<uint32_t>> FindStringInitMap(ArtMethod* m) - SHARED_REQUIRES(Locks::mutator_lock_); - static void Init() SHARED_REQUIRES(Locks::mutator_lock_); static void Shutdown(); @@ -294,10 +291,6 @@ class MethodVerifier { ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) SHARED_REQUIRES(Locks::mutator_lock_); - SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() { - return string_init_pc_reg_map_; - } - uint32_t GetEncounteredFailureTypes() { return encountered_failure_types_; } @@ -875,11 +868,6 @@ class MethodVerifier { friend class art::Thread; - // Map of dex pcs of invocations of java.lang.String.<init> to the set of other registers that - // contain the uninitialized this pointer to that invoke. Will contain no entry if there are - // no other registers. - SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_; - DISALLOW_COPY_AND_ASSIGN(MethodVerifier); }; std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs); diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h index bfbb78f58a..29d87c4433 100644 --- a/runtime/verifier/register_line-inl.h +++ b/runtime/verifier/register_line-inl.h @@ -204,9 +204,10 @@ inline RegisterLine::RegisterLine(size_t num_regs, MethodVerifier* verifier) } inline void RegisterLineArenaDelete::operator()(RegisterLine* ptr) const { - const size_t size = ptr != nullptr ? RegisterLine::ComputeSize(ptr->NumRegs()) : 0u; - ptr->~RegisterLine(); - ProtectMemory(ptr, size); + if (ptr != nullptr) { + ptr->~RegisterLine(); + ProtectMemory(ptr, RegisterLine::ComputeSize(ptr->NumRegs())); + } } } // namespace verifier diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index b7cde995c7..82c371dec5 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -91,25 +91,14 @@ bool RegisterLine::VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsr return true; } -void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type, - uint32_t this_reg, uint32_t dex_pc) { +void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) { DCHECK(uninit_type.IsUninitializedTypes()); - bool is_string = !uninit_type.IsUnresolvedTypes() && uninit_type.GetClass()->IsStringClass(); const RegType& init_type = verifier->GetRegTypeCache()->FromUninitialized(uninit_type); size_t changed = 0; for (uint32_t i = 0; i < num_regs_; i++) { if (GetRegisterType(verifier, i).Equals(uninit_type)) { line_[i] = init_type.GetId(); changed++; - if (is_string && i != this_reg) { - auto it = verifier->GetStringInitPcRegMap().find(dex_pc); - if (it != verifier->GetStringInitPcRegMap().end()) { - it->second.insert(i); - } else { - std::set<uint32_t> reg_set = { i }; - verifier->GetStringInitPcRegMap().Put(dex_pc, reg_set); - } - } } } // Is this initializing "this"? diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index d50845421f..15ae202301 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -99,11 +99,14 @@ class RegisterLine { // available now. An example is sharpening types after a check-cast. Note that when given kKeep, // the new_type is dchecked to be a reference type. template <LockOp kLockOp> - ALWAYS_INLINE bool SetRegisterType(MethodVerifier* verifier, uint32_t vdst, + ALWAYS_INLINE bool SetRegisterType(MethodVerifier* verifier, + uint32_t vdst, const RegType& new_type) SHARED_REQUIRES(Locks::mutator_lock_); - bool SetRegisterTypeWide(MethodVerifier* verifier, uint32_t vdst, const RegType& new_type1, + bool SetRegisterTypeWide(MethodVerifier* verifier, + uint32_t vdst, + const RegType& new_type1, const RegType& new_type2) SHARED_REQUIRES(Locks::mutator_lock_); @@ -117,11 +120,14 @@ class RegisterLine { // Get the type of register vsrc. const RegType& GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const; - ALWAYS_INLINE bool VerifyRegisterType(MethodVerifier* verifier, uint32_t vsrc, + ALWAYS_INLINE bool VerifyRegisterType(MethodVerifier* verifier, + uint32_t vsrc, const RegType& check_type) SHARED_REQUIRES(Locks::mutator_lock_); - bool VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsrc, const RegType& check_type1, + bool VerifyRegisterTypeWide(MethodVerifier* verifier, + uint32_t vsrc, + const RegType& check_type1, const RegType& check_type2) SHARED_REQUIRES(Locks::mutator_lock_); @@ -155,8 +161,7 @@ class RegisterLine { * reference type. This is called when an appropriate constructor is invoked -- all copies of * the reference must be marked as initialized. */ - void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type, - uint32_t this_reg, uint32_t dex_pc) + void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) SHARED_REQUIRES(Locks::mutator_lock_); /* @@ -210,31 +215,42 @@ class RegisterLine { * allow_failure will return Conflict() instead of causing a verification failure if there is an * error. */ - const RegType& GetInvocationThis(MethodVerifier* verifier, const Instruction* inst, - bool is_range, bool allow_failure = false) + const RegType& GetInvocationThis(MethodVerifier* verifier, + const Instruction* inst, + bool is_range, + bool allow_failure = false) SHARED_REQUIRES(Locks::mutator_lock_); /* * Verify types for a simple two-register instruction (e.g. "neg-int"). * "dst_type" is stored into vA, and "src_type" is verified against vB. */ - void CheckUnaryOp(MethodVerifier* verifier, const Instruction* inst, const RegType& dst_type, + void CheckUnaryOp(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type, const RegType& src_type) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckUnaryOpWide(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type1, const RegType& dst_type2, - const RegType& src_type1, const RegType& src_type2) + void CheckUnaryOpWide(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type1, + const RegType& dst_type2, + const RegType& src_type1, + const RegType& src_type2) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckUnaryOpToWide(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type1, const RegType& dst_type2, + void CheckUnaryOpToWide(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type1, + const RegType& dst_type2, const RegType& src_type) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckUnaryOpFromWide(MethodVerifier* verifier, const Instruction* inst, + void CheckUnaryOpFromWide(MethodVerifier* verifier, + const Instruction* inst, const RegType& dst_type, - const RegType& src_type1, const RegType& src_type2) + const RegType& src_type1, + const RegType& src_type2) SHARED_REQUIRES(Locks::mutator_lock_); /* @@ -242,19 +258,28 @@ class RegisterLine { * "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified * against vB/vC. */ - void CheckBinaryOp(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type, const RegType& src_type1, const RegType& src_type2, + void CheckBinaryOp(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type, + const RegType& src_type1, + const RegType& src_type2, bool check_boolean_op) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckBinaryOpWide(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type1, const RegType& dst_type2, - const RegType& src_type1_1, const RegType& src_type1_2, - const RegType& src_type2_1, const RegType& src_type2_2) + void CheckBinaryOpWide(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type1, + const RegType& dst_type2, + const RegType& src_type1_1, + const RegType& src_type1_2, + const RegType& src_type2_1, + const RegType& src_type2_2) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckBinaryOpWideShift(MethodVerifier* verifier, const Instruction* inst, - const RegType& long_lo_type, const RegType& long_hi_type, + void CheckBinaryOpWideShift(MethodVerifier* verifier, + const Instruction* inst, + const RegType& long_lo_type, + const RegType& long_hi_type, const RegType& int_type) SHARED_REQUIRES(Locks::mutator_lock_); @@ -262,20 +287,28 @@ class RegisterLine { * Verify types for a binary "2addr" operation. "src_type1"/"src_type2" * are verified against vA/vB, then "dst_type" is stored into vA. */ - void CheckBinaryOp2addr(MethodVerifier* verifier, const Instruction* inst, + void CheckBinaryOp2addr(MethodVerifier* verifier, + const Instruction* inst, const RegType& dst_type, - const RegType& src_type1, const RegType& src_type2, + const RegType& src_type1, + const RegType& src_type2, bool check_boolean_op) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckBinaryOp2addrWide(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type1, const RegType& dst_type2, - const RegType& src_type1_1, const RegType& src_type1_2, - const RegType& src_type2_1, const RegType& src_type2_2) + void CheckBinaryOp2addrWide(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type1, + const RegType& dst_type2, + const RegType& src_type1_1, + const RegType& src_type1_2, + const RegType& src_type2_1, + const RegType& src_type2_2) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckBinaryOp2addrWideShift(MethodVerifier* verifier, const Instruction* inst, - const RegType& long_lo_type, const RegType& long_hi_type, + void CheckBinaryOp2addrWideShift(MethodVerifier* verifier, + const Instruction* inst, + const RegType& long_lo_type, + const RegType& long_hi_type, const RegType& int_type) SHARED_REQUIRES(Locks::mutator_lock_); @@ -285,9 +318,12 @@ class RegisterLine { * * If "check_boolean_op" is set, we use the constant value in vC. */ - void CheckLiteralOp(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type, const RegType& src_type, - bool check_boolean_op, bool is_lit16) + void CheckLiteralOp(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type, + const RegType& src_type, + bool check_boolean_op, + bool is_lit16) SHARED_REQUIRES(Locks::mutator_lock_); // Verify/push monitor onto the monitor stack, locking the value in reg_idx at location insn_idx. @@ -409,8 +445,6 @@ class RegisterLineArenaDelete : public ArenaDelete<RegisterLine> { void operator()(RegisterLine* ptr) const; }; - - } // namespace verifier } // namespace art diff --git a/test/575-checker-isnan/expected.txt b/test/575-checker-isnan/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/575-checker-isnan/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/575-checker-isnan/info.txt b/test/575-checker-isnan/info.txt new file mode 100644 index 0000000000..5c48a6a877 --- /dev/null +++ b/test/575-checker-isnan/info.txt @@ -0,0 +1 @@ +Unit test for float/double isNaN() operation. diff --git a/test/575-checker-isnan/src/Main.java b/test/575-checker-isnan/src/Main.java new file mode 100644 index 0000000000..cc71e5e27d --- /dev/null +++ b/test/575-checker-isnan/src/Main.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: boolean Main.isNaN32(float) instruction_simplifier (before) + /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: boolean Main.isNaN32(float) instruction_simplifier (after) + /// CHECK-DAG: <<Result:z\d+>> NotEqual + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: boolean Main.isNaN32(float) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + private static boolean isNaN32(float x) { + return Float.isNaN(x); + } + + /// CHECK-START: boolean Main.isNaN64(double) instruction_simplifier (before) + /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: boolean Main.isNaN64(double) instruction_simplifier (after) + /// CHECK-DAG: <<Result:z\d+>> NotEqual + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: boolean Main.isNaN64(double) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + private static boolean isNaN64(double x) { + return Double.isNaN(x); + } + + public static void main(String args[]) { + // A few distinct numbers. + expectFalse(isNaN32(Float.NEGATIVE_INFINITY)); + expectFalse(isNaN32(-1.0f)); + expectFalse(isNaN32(-0.0f)); + expectFalse(isNaN32(0.0f)); + expectFalse(isNaN32(1.0f)); + expectFalse(isNaN32(Float.POSITIVE_INFINITY)); + + // A few distinct subnormal numbers. + expectFalse(isNaN32(Float.intBitsToFloat(0x00400000))); + expectFalse(isNaN32(Float.intBitsToFloat(0x80400000))); + expectFalse(isNaN32(Float.intBitsToFloat(0x00000001))); + expectFalse(isNaN32(Float.intBitsToFloat(0x80000001))); + + // A few NaN numbers. + expectTrue(isNaN32(Float.NaN)); + expectTrue(isNaN32(0.0f / 0.0f)); + expectTrue(isNaN32((float)Math.sqrt(-1.0f))); + float[] fvals = { + Float.intBitsToFloat(0x7f800001), + Float.intBitsToFloat(0x7fa00000), + Float.intBitsToFloat(0x7fc00000), + Float.intBitsToFloat(0x7fffffff), + Float.intBitsToFloat(0xff800001), + Float.intBitsToFloat(0xffa00000), + Float.intBitsToFloat(0xffc00000), + Float.intBitsToFloat(0xffffffff) + }; + for (int i = 0; i < fvals.length; i++) { + expectTrue(isNaN32(fvals[i])); + } + + // A few distinct numbers. + expectFalse(isNaN64(Double.NEGATIVE_INFINITY)); + expectFalse(isNaN32(-1.0f)); + expectFalse(isNaN64(-0.0d)); + expectFalse(isNaN64(0.0d)); + expectFalse(isNaN64(1.0d)); + expectFalse(isNaN64(Double.POSITIVE_INFINITY)); + + // A few distinct subnormal numbers. + expectFalse(isNaN64(Double.longBitsToDouble(0x0008000000000000l))); + expectFalse(isNaN64(Double.longBitsToDouble(0x8008000000000000l))); + expectFalse(isNaN64(Double.longBitsToDouble(0x0000000000000001l))); + expectFalse(isNaN64(Double.longBitsToDouble(0x8000000000000001l))); + + // A few NaN numbers. + expectTrue(isNaN64(Double.NaN)); + expectTrue(isNaN64(0.0d / 0.0d)); + expectTrue(isNaN64(Math.sqrt(-1.0d))); + double[] dvals = { + Double.longBitsToDouble(0x7ff0000000000001L), + Double.longBitsToDouble(0x7ff4000000000000L), + Double.longBitsToDouble(0x7ff8000000000000L), + Double.longBitsToDouble(0x7fffffffffffffffL), + Double.longBitsToDouble(0xfff0000000000001L), + Double.longBitsToDouble(0xfff4000000000000L), + Double.longBitsToDouble(0xfff8000000000000L), + Double.longBitsToDouble(0xffffffffffffffffL) + }; + for (int i = 0; i < dvals.length; i++) { + expectTrue(isNaN64(dvals[i])); + } + + System.out.println("passed"); + } + + private static void expectTrue(boolean value) { + if (!value) { + throw new Error("Expected True"); + } + } + + private static void expectFalse(boolean value) { + if (value) { + throw new Error("Expected False"); + } + } +} diff --git a/test/575-checker-string-init-alias/expected.txt b/test/575-checker-string-init-alias/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/575-checker-string-init-alias/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/575-checker-string-init-alias/info.txt b/test/575-checker-string-init-alias/info.txt new file mode 100644 index 0000000000..a91ea645e7 --- /dev/null +++ b/test/575-checker-string-init-alias/info.txt @@ -0,0 +1,2 @@ +Test for the String.<init> change and deoptimization: make +sure the compiler knows how to handle dex aliases. diff --git a/test/575-checker-string-init-alias/smali/TestCase.smali b/test/575-checker-string-init-alias/smali/TestCase.smali new file mode 100644 index 0000000000..ff04b278a4 --- /dev/null +++ b/test/575-checker-string-init-alias/smali/TestCase.smali @@ -0,0 +1,72 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LTestCase; + +.super Ljava/lang/Object; + +.field public static staticField:Ljava/lang/String; + +## CHECK-START: void TestCase.testNoAlias(int[], java.lang.String) register (after) +## CHECK: <<Null:l\d+>> NullConstant +## CHECK: Deoptimize env:[[<<Null>>,{{.*]]}} +## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init> +.method public static testNoAlias([ILjava/lang/String;)V + .registers 6 + const v1, 0 + const v2, 1 + new-instance v0, Ljava/lang/String; + + # Will deoptimize. + aget v3, p0, v1 + + # Check that we're being executed by the interpreter. + invoke-static {}, LMain;->assertIsInterpreted()V + + invoke-direct {v0, p1}, Ljava/lang/String;-><init>(Ljava/lang/String;)V + + sput-object v0, LTestCase;->staticField:Ljava/lang/String; + + # Will throw AIOOBE. + aget v3, p0, v2 + + return-void +.end method + +## CHECK-START: void TestCase.testAlias(int[], java.lang.String) register (after) +## CHECK: <<New:l\d+>> NewInstance +## CHECK: Deoptimize env:[[<<New>>,<<New>>,{{.*]]}} +## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init> +.method public static testAlias([ILjava/lang/String;)V + .registers 7 + const v2, 0 + const v3, 1 + new-instance v0, Ljava/lang/String; + move-object v1, v0 + + # Will deoptimize. + aget v4, p0, v2 + + # Check that we're being executed by the interpreter. + invoke-static {}, LMain;->assertIsInterpreted()V + + invoke-direct {v1, p1}, Ljava/lang/String;-><init>(Ljava/lang/String;)V + + sput-object v1, LTestCase;->staticField:Ljava/lang/String; + + # Will throw AIOOBE. + aget v4, p0, v3 + + return-void +.end method diff --git a/test/575-checker-string-init-alias/src/Main.java b/test/575-checker-string-init-alias/src/Main.java new file mode 100644 index 0000000000..1ab320748a --- /dev/null +++ b/test/575-checker-string-init-alias/src/Main.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +public class Main { + // Workaround for b/18051191. + class Inner {} + + public static native void assertIsInterpreted(); + + private static void assertEqual(String expected, String actual) { + if (!expected.equals(actual)) { + throw new Error("Assertion failed: " + expected + " != " + actual); + } + } + + public static void main(String[] args) throws Throwable { + System.loadLibrary(args[0]); + Class<?> c = Class.forName("TestCase"); + int[] array = new int[1]; + + { + Method m = c.getMethod("testNoAlias", int[].class, String.class); + try { + m.invoke(null, new Object[] { array , "foo" }); + throw new Error("Expected AIOOBE"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof ArrayIndexOutOfBoundsException)) { + throw new Error("Expected AIOOBE"); + } + // Ignore + } + Field field = c.getField("staticField"); + assertEqual("foo", (String)field.get(null)); + } + + { + Method m = c.getMethod("testAlias", int[].class, String.class); + try { + m.invoke(null, new Object[] { array, "bar" }); + throw new Error("Expected AIOOBE"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof ArrayIndexOutOfBoundsException)) { + throw new Error("Expected AIOOBE"); + } + // Ignore + } + Field field = c.getField("staticField"); + assertEqual("bar", (String)field.get(null)); + } + } +} |