Merge "Implement Optimizing's constant folding as a visitor."
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index eb4915b..6f9dd6d 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -1679,9 +1679,7 @@
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 @@
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 027290f..49768de 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -509,7 +509,8 @@
}
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 @@
// 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 @@
<< 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 55f45f1..f32cf86 100644
--- a/compiler/dex/quick/quick_compiler.h
+++ b/compiler/dex/quick/quick_compiler.h
@@ -75,7 +75,7 @@
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 0355f11..9ae2164 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 74fcb07..12d0219 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -83,14 +83,8 @@
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 @@
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 a48d06f..13d3f75 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -92,6 +92,7 @@
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 @@
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 @@
} 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 00a158b..ea8669f 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1858,8 +1858,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1867,6 +1865,8 @@
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 4140d94..8741fd2 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1618,8 +1618,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1627,6 +1625,8 @@
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 5d6e8c2..c862964 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1220,8 +1220,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1229,6 +1227,8 @@
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 ac28503..cf3a365 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1764,8 +1764,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1773,6 +1771,8 @@
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 22cefe8..260a877 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2635,8 +2635,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -2644,6 +2642,8 @@
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 c9a4344..93e8c00 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2717,10 +2717,10 @@
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 43f2499..09ca8b7 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -422,6 +422,34 @@
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 @@
// 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 e9f7add..947de8a 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -238,6 +238,7 @@
# (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 82a5f96..6972b3e 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -1009,10 +1009,6 @@
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 f674a6f..e72f2a2 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -102,7 +102,6 @@
kMonitorListLock,
kJniLoadLibraryLock,
kThreadListLock,
- kInterpreterStringInitMapLock,
kAllocTrackerLock,
kDeoptimizationLock,
kProfilerLock,
diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h
index 9b56856..bd19d00 100644
--- a/runtime/base/scoped_arena_containers.h
+++ b/runtime/base/scoped_arena_containers.h
@@ -201,11 +201,12 @@
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 @@
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 0c06c38..894ce9a 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -676,13 +676,17 @@
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 @@
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 @@
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 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 @@
}
}
+ // 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 @@
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 @@
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 @@
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 @@
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 cbaa817..3453abc 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -733,39 +733,21 @@
}
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 1956bae..cbb3e89 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -94,8 +94,6 @@
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 @@
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 @@
// 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 a6cf9ea..0c6060e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -617,23 +617,6 @@
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 @@
* 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 b53a45c..6d8e1ab 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -213,9 +213,6 @@
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 @@
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 @@
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 bfbb78f..29d87c4 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -204,9 +204,10 @@
}
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 b7cde99..82c371d 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -91,25 +91,14 @@
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 d508454..15ae202 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -99,11 +99,14 @@
// 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 @@
// 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 @@
* 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 @@
* 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 @@
* "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 @@
* 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 @@
*
* 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 @@
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 0000000..b0aad4d
--- /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 0000000..5c48a6a
--- /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 0000000..cc71e5e
--- /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 0000000..6a5618e
--- /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 0000000..a91ea64
--- /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 0000000..ff04b27
--- /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 0000000..1ab3207
--- /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));
+ }
+ }
+}