Merge "Revert "Change RequiresConstructorBarrier default to yes""
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 98577d6..f45aa66 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -6440,8 +6440,9 @@
reg,
method_reg,
ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value());
- // temp = temp[index_in_cache]
- uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
+ // temp = temp[index_in_cache];
+ // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
+ uint32_t index_in_cache = invoke->GetDexMethodIndex();
__ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
break;
}
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 491014d..f401bda 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3705,7 +3705,8 @@
MemOperand(method_reg.X(),
ArtMethod::DexCacheResolvedMethodsOffset(kArm64WordSize).Int32Value()));
// temp = temp[index_in_cache];
- uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
+ // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
+ uint32_t index_in_cache = invoke->GetDexMethodIndex();
__ Ldr(reg.X(), MemOperand(reg.X(), GetCachePointerOffset(index_in_cache)));
break;
}
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 8b19f84..bbfdb30 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -3896,8 +3896,9 @@
reg,
method_reg,
ArtMethod::DexCacheResolvedMethodsOffset(kMipsPointerSize).Int32Value());
- // temp = temp[index_in_cache]
- uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
+ // temp = temp[index_in_cache];
+ // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
+ uint32_t index_in_cache = invoke->GetDexMethodIndex();
__ LoadFromOffset(kLoadWord,
reg,
reg,
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 2f9eca6..08e18e6 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3110,8 +3110,9 @@
reg,
method_reg,
ArtMethod::DexCacheResolvedMethodsOffset(kMips64PointerSize).Int32Value());
- // temp = temp[index_in_cache]
- uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
+ // temp = temp[index_in_cache];
+ // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
+ uint32_t index_in_cache = invoke->GetDexMethodIndex();
__ LoadFromOffset(kLoadDoubleword,
reg,
reg,
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 715b5be..b6b2ce3 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4433,8 +4433,9 @@
// /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
__ movl(reg, Address(method_reg,
ArtMethod::DexCacheResolvedMethodsOffset(kX86PointerSize).Int32Value()));
- // temp = temp[index_in_cache]
- uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
+ // temp = temp[index_in_cache];
+ // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
+ uint32_t index_in_cache = invoke->GetDexMethodIndex();
__ movl(reg, Address(reg, CodeGenerator::GetCachePointerOffset(index_in_cache)));
break;
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index cc46a07..31a8d14 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -808,8 +808,9 @@
__ movq(reg,
Address(CpuRegister(method_reg),
ArtMethod::DexCacheResolvedMethodsOffset(kX86_64PointerSize).SizeValue()));
- // temp = temp[index_in_cache]
- uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
+ // temp = temp[index_in_cache];
+ // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
+ uint32_t index_in_cache = invoke->GetDexMethodIndex();
__ movq(reg, Address(reg, CodeGenerator::GetCachePointerOffset(index_in_cache)));
break;
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index d936a8c..ae292dc 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1116,41 +1116,10 @@
}
}
- // Run simple optimizations on the graph.
- HDeadCodeElimination dce(callee_graph, stats_);
- HConstantFolding fold(callee_graph);
- HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
- InstructionSimplifier simplify(callee_graph, stats_);
- IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_, stats_);
-
- HOptimization* optimizations[] = {
- &intrinsics,
- &sharpening,
- &simplify,
- &fold,
- &dce,
- };
-
- for (size_t i = 0; i < arraysize(optimizations); ++i) {
- HOptimization* optimization = optimizations[i];
- optimization->Run();
- }
-
size_t number_of_instructions_budget = kMaximumNumberOfHInstructions;
- if (depth_ + 1 < compiler_driver_->GetCompilerOptions().GetInlineDepthLimit()) {
- HInliner inliner(callee_graph,
- outermost_graph_,
- codegen_,
- outer_compilation_unit_,
- dex_compilation_unit,
- compiler_driver_,
- handles_,
- stats_,
- total_number_of_dex_registers_ + code_item->registers_size_,
- depth_ + 1);
- inliner.Run();
- number_of_instructions_budget += inliner.number_of_inlined_instructions_;
- }
+ size_t number_of_inlined_instructions =
+ RunOptimizations(callee_graph, code_item, dex_compilation_unit);
+ number_of_instructions_budget += number_of_inlined_instructions;
// TODO: We should abort only if all predecessors throw. However,
// HGraph::InlineInto currently does not handle an exit block with
@@ -1196,7 +1165,7 @@
for (HInstructionIterator instr_it(block->GetInstructions());
!instr_it.Done();
instr_it.Advance()) {
- if (number_of_instructions++ == number_of_instructions_budget) {
+ if (number_of_instructions++ == number_of_instructions_budget) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " is not inlined because its caller has reached"
<< " its instruction budget limit.";
@@ -1277,6 +1246,47 @@
return true;
}
+size_t HInliner::RunOptimizations(HGraph* callee_graph,
+ const DexFile::CodeItem* code_item,
+ const DexCompilationUnit& dex_compilation_unit) {
+ HDeadCodeElimination dce(callee_graph, stats_);
+ HConstantFolding fold(callee_graph);
+ HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
+ InstructionSimplifier simplify(callee_graph, stats_);
+ IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_, stats_);
+
+ HOptimization* optimizations[] = {
+ &intrinsics,
+ &sharpening,
+ &simplify,
+ &fold,
+ &dce,
+ };
+
+ for (size_t i = 0; i < arraysize(optimizations); ++i) {
+ HOptimization* optimization = optimizations[i];
+ optimization->Run();
+ }
+
+ size_t number_of_inlined_instructions = 0u;
+ if (depth_ + 1 < compiler_driver_->GetCompilerOptions().GetInlineDepthLimit()) {
+ HInliner inliner(callee_graph,
+ outermost_graph_,
+ codegen_,
+ outer_compilation_unit_,
+ dex_compilation_unit,
+ compiler_driver_,
+ handles_,
+ stats_,
+ total_number_of_dex_registers_ + code_item->registers_size_,
+ depth_ + 1);
+ inliner.Run();
+ number_of_inlined_instructions += inliner.number_of_inlined_instructions_;
+ }
+
+ return number_of_inlined_instructions;
+}
+
void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
HInstruction* return_replacement,
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index cdb2167..7cf1424 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -76,6 +76,12 @@
bool same_dex_file,
HInstruction** return_replacement);
+ // Run simple optimizations on `callee_graph`.
+ // Returns the number of inlined instructions.
+ size_t RunOptimizations(HGraph* callee_graph,
+ const DexFile::CodeItem* code_item,
+ const DexCompilationUnit& dex_compilation_unit);
+
// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
bool TryPatternSubstitution(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 0e709eb..77efb6b 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -776,7 +776,7 @@
if (imm5 == 0) {
args << "rrx";
} else {
- args << "ror";
+ args << "ror #" << imm5;
}
break;
}
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 293451c..17e0339 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -88,6 +88,8 @@
kOatFileManagerLock,
kTracingUniqueMethodsLock,
kTracingStreamingLock,
+ kDeoptimizedMethodsLock,
+ kClassLoaderClassesLock,
kDefaultMutexLevel,
kMarkSweepLargeObjectLock,
kPinTableLock,
@@ -96,7 +98,7 @@
kAllocatedThreadIdsLock,
kMonitorPoolLock,
kMethodVerifiersLock,
- kClassLinkerClassesLock,
+ kClassLinkerClassesLock, // TODO rename.
kBreakpointLock,
kMonitorLock,
kMonitorListLock,
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
index e512906..42e320a 100644
--- a/runtime/class_table-inl.h
+++ b/runtime/class_table-inl.h
@@ -23,6 +23,7 @@
template<class Visitor>
void ClassTable::VisitRoots(Visitor& visitor) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
for (ClassSet& class_set : classes_) {
for (GcRoot<mirror::Class>& root : class_set) {
visitor.VisitRoot(root.AddressWithoutBarrier());
@@ -35,6 +36,7 @@
template<class Visitor>
void ClassTable::VisitRoots(const Visitor& visitor) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
for (ClassSet& class_set : classes_) {
for (GcRoot<mirror::Class>& root : class_set) {
visitor.VisitRoot(root.AddressWithoutBarrier());
@@ -47,6 +49,7 @@
template <typename Visitor>
bool ClassTable::Visit(Visitor& visitor) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
for (ClassSet& class_set : classes_) {
for (GcRoot<mirror::Class>& root : class_set) {
if (!visitor(root.Read())) {
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index d815b1a..8267c68 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -20,17 +20,19 @@
namespace art {
-ClassTable::ClassTable() {
+ClassTable::ClassTable() : lock_("Class loader classes", kClassLoaderClassesLock) {
Runtime* const runtime = Runtime::Current();
classes_.push_back(ClassSet(runtime->GetHashTableMinLoadFactor(),
runtime->GetHashTableMaxLoadFactor()));
}
void ClassTable::FreezeSnapshot() {
+ WriterMutexLock mu(Thread::Current(), lock_);
classes_.push_back(ClassSet());
}
bool ClassTable::Contains(mirror::Class* klass) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
for (ClassSet& class_set : classes_) {
auto it = class_set.Find(GcRoot<mirror::Class>(klass));
if (it != class_set.end()) {
@@ -41,6 +43,7 @@
}
mirror::Class* ClassTable::LookupByDescriptor(mirror::Class* klass) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
for (ClassSet& class_set : classes_) {
auto it = class_set.Find(GcRoot<mirror::Class>(klass));
if (it != class_set.end()) {
@@ -51,6 +54,7 @@
}
mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash) {
+ WriterMutexLock mu(Thread::Current(), lock_);
// Should only be updating latest table.
auto existing_it = classes_.back().FindWithHash(descriptor, hash);
if (kIsDebugBuild && existing_it == classes_.back().end()) {
@@ -74,6 +78,7 @@
}
size_t ClassTable::NumZygoteClasses() const {
+ ReaderMutexLock mu(Thread::Current(), lock_);
size_t sum = 0;
for (size_t i = 0; i < classes_.size() - 1; ++i) {
sum += classes_[i].Size();
@@ -82,10 +87,12 @@
}
size_t ClassTable::NumNonZygoteClasses() const {
+ ReaderMutexLock mu(Thread::Current(), lock_);
return classes_.back().Size();
}
mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
for (ClassSet& class_set : classes_) {
auto it = class_set.FindWithHash(descriptor, hash);
if (it != class_set.end()) {
@@ -96,14 +103,17 @@
}
void ClassTable::Insert(mirror::Class* klass) {
+ WriterMutexLock mu(Thread::Current(), lock_);
classes_.back().Insert(GcRoot<mirror::Class>(klass));
}
void ClassTable::InsertWithHash(mirror::Class* klass, size_t hash) {
+ WriterMutexLock mu(Thread::Current(), lock_);
classes_.back().InsertWithHash(GcRoot<mirror::Class>(klass), hash);
}
bool ClassTable::Remove(const char* descriptor) {
+ WriterMutexLock mu(Thread::Current(), lock_);
for (ClassSet& class_set : classes_) {
auto it = class_set.Find(descriptor);
if (it != class_set.end()) {
@@ -137,6 +147,7 @@
}
bool ClassTable::InsertDexFile(mirror::Object* dex_file) {
+ WriterMutexLock mu(Thread::Current(), lock_);
DCHECK(dex_file != nullptr);
for (GcRoot<mirror::Object>& root : dex_files_) {
if (root.Read() == dex_file) {
@@ -148,6 +159,7 @@
}
size_t ClassTable::WriteToMemory(uint8_t* ptr) const {
+ ReaderMutexLock mu(Thread::Current(), lock_);
ClassSet combined;
// Combine all the class sets in case there are multiple, also adjusts load factor back to
// default in case classes were pruned.
@@ -173,6 +185,7 @@
}
void ClassTable::AddClassSet(ClassSet&& set) {
+ WriterMutexLock mu(Thread::Current(), lock_);
classes_.insert(classes_.begin(), std::move(set));
}
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 0e0e860..eb784b5 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -71,87 +71,96 @@
// Used by image writer for checking.
bool Contains(mirror::Class* klass)
- REQUIRES(Locks::classlinker_classes_lock_)
+ REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
// Freeze the current class tables by allocating a new table and never updating or modifying the
// existing table. This helps prevents dirty pages after caused by inserting after zygote fork.
void FreezeSnapshot()
- REQUIRES(Locks::classlinker_classes_lock_)
+ REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
// Returns the number of classes in previous snapshots.
- size_t NumZygoteClasses() const SHARED_REQUIRES(Locks::classlinker_classes_lock_);
+ size_t NumZygoteClasses() const REQUIRES(!lock_);
// Returns all off the classes in the lastest snapshot.
- size_t NumNonZygoteClasses() const SHARED_REQUIRES(Locks::classlinker_classes_lock_);
+ size_t NumNonZygoteClasses() const REQUIRES(!lock_);
// Update a class in the table with the new class. Returns the existing class which was replaced.
mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash)
- REQUIRES(Locks::classlinker_classes_lock_)
+ REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
// NO_THREAD_SAFETY_ANALYSIS for object marking requiring heap bitmap lock.
template<class Visitor>
void VisitRoots(Visitor& visitor)
NO_THREAD_SAFETY_ANALYSIS
- SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
template<class Visitor>
void VisitRoots(const Visitor& visitor)
NO_THREAD_SAFETY_ANALYSIS
- SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Stops visit if the visitor returns false.
template <typename Visitor>
bool Visit(Visitor& visitor)
- SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Return the first class that matches the descriptor. Returns null if there are none.
mirror::Class* Lookup(const char* descriptor, size_t hash)
- SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Return the first class that matches the descriptor of klass. Returns null if there are none.
mirror::Class* LookupByDescriptor(mirror::Class* klass)
- SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void Insert(mirror::Class* klass)
- REQUIRES(Locks::classlinker_classes_lock_)
+ REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+
void InsertWithHash(mirror::Class* klass, size_t hash)
- REQUIRES(Locks::classlinker_classes_lock_)
+ REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
// Returns true if the class was found and removed, false otherwise.
bool Remove(const char* descriptor)
- REQUIRES(Locks::classlinker_classes_lock_)
+ REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
// Return true if we inserted the dex file, false if it already exists.
bool InsertDexFile(mirror::Object* dex_file)
- REQUIRES(Locks::classlinker_classes_lock_)
+ REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
// Combines all of the tables into one class set.
size_t WriteToMemory(uint8_t* ptr) const
- SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Read a table from ptr and put it at the front of the class set.
size_t ReadFromMemory(uint8_t* ptr)
- REQUIRES(Locks::classlinker_classes_lock_)
+ REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
// Add a class set to the front of classes.
void AddClassSet(ClassSet&& set)
- REQUIRES(Locks::classlinker_classes_lock_)
+ REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
private:
- // TODO: shard lock to have one per class loader.
+ // Lock to guard inserting and removing.
+ mutable ReaderWriterMutex lock_;
// We have a vector to help prevent dirty pages after the zygote forks by calling FreezeSnapshot.
- std::vector<ClassSet> classes_ GUARDED_BY(Locks::classlinker_classes_lock_);
+ std::vector<ClassSet> classes_ GUARDED_BY(lock_);
// Dex files used by the class loader which may not be owned by the class loader. We keep these
// live so that we do not have issues closing any of the dex files.
- std::vector<GcRoot<mirror::Object>> dex_files_ GUARDED_BY(Locks::classlinker_classes_lock_);
+ std::vector<GcRoot<mirror::Object>> dex_files_ GUARDED_BY(lock_);
};
} // namespace art
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index d16afd9..4e40aea 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -210,7 +210,11 @@
if (mod_union_table_->ShouldAddReference(root->AsMirrorPtr())) {
*has_target_reference_ = true;
// TODO: Add MarkCompressedReference callback here.
- root->Assign(visitor_->MarkObject(root->AsMirrorPtr()));
+ mirror::Object* old_ref = root->AsMirrorPtr();
+ mirror::Object* new_ref = visitor_->MarkObject(old_ref);
+ if (old_ref != new_ref) {
+ root->Assign(new_ref);
+ }
}
}
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index b61bef7..c19107a 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -231,7 +231,7 @@
protected:
// Returns object if the object is marked in the heap bitmap, otherwise null.
virtual mirror::Object* IsMarked(mirror::Object* object) OVERRIDE
- SHARED_REQUIRES(Locks::heap_bitmap_lock_);
+ SHARED_REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
void MarkObjectNonNull(mirror::Object* obj,
mirror::Object* holder = nullptr,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index a0c6bfb..34bc458 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -80,7 +80,7 @@
have_exception_caught_listeners_(false),
have_branch_listeners_(false),
have_invoke_virtual_or_interface_listeners_(false),
- deoptimized_methods_lock_("deoptimized methods lock"),
+ deoptimized_methods_lock_("deoptimized methods lock", kDeoptimizedMethodsLock),
deoptimization_enabled_(false),
interpreter_handler_table_(kMainHandlerTable),
quick_alloc_entry_points_instrumentation_counter_(0),
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 2f22ac4..4615ec9 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -16,7 +16,11 @@
#include "unstarted_runtime.h"
+#include <errno.h>
+#include <stdlib.h>
+
#include <cmath>
+#include <limits>
#include <unordered_map>
#include "ScopedLocalRef.h"
@@ -282,6 +286,23 @@
}
}
+// Special managed code cut-out to allow constructor lookup in a un-started runtime.
+void UnstartedRuntime::UnstartedClassGetDeclaredConstructor(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ mirror::Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
+ if (klass == nullptr) {
+ ThrowNullPointerExceptionForMethodAccess(shadow_frame->GetMethod(), InvokeType::kVirtual);
+ return;
+ }
+ mirror::ObjectArray<mirror::Class>* args =
+ shadow_frame->GetVRegReference(arg_offset + 1)->AsObjectArray<mirror::Class>();
+ if (Runtime::Current()->IsActiveTransaction()) {
+ result->SetL(mirror::Class::GetDeclaredConstructorInternal<true>(self, klass, args));
+ } else {
+ result->SetL(mirror::Class::GetDeclaredConstructorInternal<false>(self, klass, args));
+ }
+}
+
void UnstartedRuntime::UnstartedClassGetEnclosingClass(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
StackHandleScope<1> hs(self);
@@ -1033,6 +1054,24 @@
result->SetL(value);
}
+void UnstartedRuntime::UnstartedUnsafePutObjectVolatile(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ // Argument 0 is the Unsafe instance, skip.
+ mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset + 1);
+ if (obj == nullptr) {
+ AbortTransactionOrFail(self, "Cannot access null object, retry at runtime.");
+ return;
+ }
+ int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2);
+ mirror::Object* value = shadow_frame->GetVRegReference(arg_offset + 4);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->SetFieldObjectVolatile<true>(MemberOffset(offset), value);
+ } else {
+ obj->SetFieldObjectVolatile<false>(MemberOffset(offset), value);
+ }
+}
+
void UnstartedRuntime::UnstartedUnsafePutOrderedObject(
Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -1052,6 +1091,93 @@
}
}
+// A cutout for Integer.parseInt(String). Note: this code is conservative and will bail instead
+// of correctly handling the corner cases.
+void UnstartedRuntime::UnstartedIntegerParseInt(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
+ if (obj == nullptr) {
+ AbortTransactionOrFail(self, "Cannot parse null string, retry at runtime.");
+ return;
+ }
+
+ std::string string_value = obj->AsString()->ToModifiedUtf8();
+ if (string_value.empty()) {
+ AbortTransactionOrFail(self, "Cannot parse empty string, retry at runtime.");
+ return;
+ }
+
+ const char* c_str = string_value.c_str();
+ char *end;
+ // Can we set errno to 0? Is this always a variable, and not a macro?
+ // Worst case, we'll incorrectly fail a transaction. Seems OK.
+ int64_t l = strtol(c_str, &end, 10);
+
+ if ((errno == ERANGE && l == LONG_MAX) || l > std::numeric_limits<int32_t>::max() ||
+ (errno == ERANGE && l == LONG_MIN) || l < std::numeric_limits<int32_t>::min()) {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ if (l == 0) {
+ // Check whether the string wasn't exactly zero.
+ if (string_value != "0") {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ } else if (*end != '\0') {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+
+ result->SetI(static_cast<int32_t>(l));
+}
+
+// A cutout for Long.parseLong.
+//
+// Note: for now use code equivalent to Integer.parseInt, as the full range may not be supported
+// well.
+void UnstartedRuntime::UnstartedLongParseLong(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
+ if (obj == nullptr) {
+ AbortTransactionOrFail(self, "Cannot parse null string, retry at runtime.");
+ return;
+ }
+
+ std::string string_value = obj->AsString()->ToModifiedUtf8();
+ if (string_value.empty()) {
+ AbortTransactionOrFail(self, "Cannot parse empty string, retry at runtime.");
+ return;
+ }
+
+ const char* c_str = string_value.c_str();
+ char *end;
+ // Can we set errno to 0? Is this always a variable, and not a macro?
+ // Worst case, we'll incorrectly fail a transaction. Seems OK.
+ int64_t l = strtol(c_str, &end, 10);
+
+ // Note: comparing against int32_t min/max is intentional here.
+ if ((errno == ERANGE && l == LONG_MAX) || l > std::numeric_limits<int32_t>::max() ||
+ (errno == ERANGE && l == LONG_MIN) || l < std::numeric_limits<int32_t>::min()) {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ if (l == 0) {
+ // Check whether the string wasn't exactly zero.
+ if (string_value != "0") {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ } else if (*end != '\0') {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+
+ result->SetJ(l);
+}
+
void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray(
Thread* self, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED,
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index cb614a1..a3ed558 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -25,6 +25,7 @@
V(ClassNewInstance, "java.lang.Object java.lang.Class.newInstance()") \
V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \
V(ClassGetDeclaredMethod, "java.lang.reflect.Method java.lang.Class.getDeclaredMethodInternal(java.lang.String, java.lang.Class[])") \
+ V(ClassGetDeclaredConstructor, "java.lang.reflect.Constructor java.lang.Class.getDeclaredConstructorInternal(java.lang.Class[])") \
V(ClassGetEnclosingClass, "java.lang.Class java.lang.Class.getEnclosingClass()") \
V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \
V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \
@@ -56,7 +57,10 @@
V(UnsafeCompareAndSwapLong, "boolean sun.misc.Unsafe.compareAndSwapLong(java.lang.Object, long, long, long)") \
V(UnsafeCompareAndSwapObject, "boolean sun.misc.Unsafe.compareAndSwapObject(java.lang.Object, long, java.lang.Object, java.lang.Object)") \
V(UnsafeGetObjectVolatile, "java.lang.Object sun.misc.Unsafe.getObjectVolatile(java.lang.Object, long)") \
- V(UnsafePutOrderedObject, "void sun.misc.Unsafe.putOrderedObject(java.lang.Object, long, java.lang.Object)")
+ V(UnsafePutObjectVolatile, "void sun.misc.Unsafe.putObjectVolatile(java.lang.Object, long, java.lang.Object)") \
+ V(UnsafePutOrderedObject, "void sun.misc.Unsafe.putOrderedObject(java.lang.Object, long, java.lang.Object)") \
+ V(IntegerParseInt, "int java.lang.Integer.parseInt(java.lang.String)") \
+ V(LongParseLong, "long java.lang.Long.parseLong(java.lang.String)")
// Methods that are native.
#define UNSTARTED_RUNTIME_JNI_LIST(V) \
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index fb53b1d..f40e4e3 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -508,5 +508,100 @@
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
+TEST_F(UnstartedRuntimeTest, IntegerParseIntTest) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ // Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
+ // suffixes).
+ constexpr const char* test_string = "-2147483646";
+ constexpr int32_t test_values[] = {
+ 6,
+ 46,
+ 646,
+ 3646,
+ 83646,
+ 483646,
+ 7483646,
+ 47483646,
+ 147483646,
+ 2147483646,
+ -2147483646
+ };
+
+ static_assert(arraysize(test_values) == 11U, "test_values");
+ CHECK_EQ(strlen(test_string), 11U);
+
+ for (size_t i = 0; i <= 10; ++i) {
+ const char* test_value = &test_string[10 - i];
+
+ StackHandleScope<1> hs_str(self);
+ Handle<mirror::String> h_str(
+ hs_str.NewHandle(mirror::String::AllocFromModifiedUtf8(self, test_value)));
+ ASSERT_NE(h_str.Get(), nullptr);
+ ASSERT_FALSE(self->IsExceptionPending());
+
+ tmp->SetVRegReference(0, h_str.Get());
+
+ JValue result;
+ UnstartedIntegerParseInt(self, tmp, &result, 0);
+
+ ASSERT_FALSE(self->IsExceptionPending());
+ EXPECT_EQ(result.GetI(), test_values[i]);
+ }
+
+ ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+// Right now the same as Integer.Parse
+TEST_F(UnstartedRuntimeTest, LongParseLongTest) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ // Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
+ // suffixes).
+ constexpr const char* test_string = "-2147483646";
+ constexpr int64_t test_values[] = {
+ 6,
+ 46,
+ 646,
+ 3646,
+ 83646,
+ 483646,
+ 7483646,
+ 47483646,
+ 147483646,
+ 2147483646,
+ -2147483646
+ };
+
+ static_assert(arraysize(test_values) == 11U, "test_values");
+ CHECK_EQ(strlen(test_string), 11U);
+
+ for (size_t i = 0; i <= 10; ++i) {
+ const char* test_value = &test_string[10 - i];
+
+ StackHandleScope<1> hs_str(self);
+ Handle<mirror::String> h_str(
+ hs_str.NewHandle(mirror::String::AllocFromModifiedUtf8(self, test_value)));
+ ASSERT_NE(h_str.Get(), nullptr);
+ ASSERT_FALSE(self->IsExceptionPending());
+
+ tmp->SetVRegReference(0, h_str.Get());
+
+ JValue result;
+ UnstartedLongParseLong(self, tmp, &result, 0);
+
+ ASSERT_FALSE(self->IsExceptionPending());
+ EXPECT_EQ(result.GetJ(), test_values[i]);
+ }
+
+ ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
} // namespace interpreter
} // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index a36091f..42f003d 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -1024,8 +1024,8 @@
// TODO: Move this to java_lang_Class.cc?
ArtMethod* Class::GetDeclaredConstructor(
- Thread* self, Handle<mirror::ObjectArray<mirror::Class>> args) {
- for (auto& m : GetDirectMethods(sizeof(void*))) {
+ Thread* self, Handle<mirror::ObjectArray<mirror::Class>> args, size_t pointer_size) {
+ for (auto& m : GetDirectMethods(pointer_size)) {
// Skip <clinit> which is a static constructor, as well as non constructors.
if (m.IsStatic() || !m.IsConstructor()) {
continue;
@@ -1139,5 +1139,31 @@
mirror::String* name,
mirror::ObjectArray<mirror::Class>* args);
+template <bool kTransactionActive>
+mirror::Constructor* Class::GetDeclaredConstructorInternal(
+ Thread* self,
+ mirror::Class* klass,
+ mirror::ObjectArray<mirror::Class>* args) {
+ StackHandleScope<1> hs(self);
+ const size_t pointer_size = kTransactionActive
+ ? Runtime::Current()->GetClassLinker()->GetImagePointerSize()
+ : sizeof(void*);
+ ArtMethod* result = klass->GetDeclaredConstructor(self, hs.NewHandle(args), pointer_size);
+ return result != nullptr
+ ? mirror::Constructor::CreateFromArtMethod<kTransactionActive>(self, result)
+ : nullptr;
+}
+
+// mirror::Constructor::CreateFromArtMethod<kTransactionActive>(self, result)
+
+template mirror::Constructor* Class::GetDeclaredConstructorInternal<false>(
+ Thread* self,
+ mirror::Class* klass,
+ mirror::ObjectArray<mirror::Class>* args);
+template mirror::Constructor* Class::GetDeclaredConstructorInternal<true>(
+ Thread* self,
+ mirror::Class* klass,
+ mirror::ObjectArray<mirror::Class>* args);
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 9808c3e..57c3590 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -768,6 +768,11 @@
mirror::String* name,
mirror::ObjectArray<mirror::Class>* args)
SHARED_REQUIRES(Locks::mutator_lock_);
+ template <bool kTransactionActive = false>
+ static Constructor* GetDeclaredConstructorInternal(Thread* self,
+ mirror::Class* klass,
+ mirror::ObjectArray<mirror::Class>* args)
+ SHARED_REQUIRES(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ALWAYS_INLINE ArraySlice<ArtMethod> GetDeclaredVirtualMethodsSlice(size_t pointer_size)
@@ -1215,7 +1220,7 @@
// May cause thread suspension due to EqualParameters.
ArtMethod* GetDeclaredConstructor(
- Thread* self, Handle<mirror::ObjectArray<mirror::Class>> args)
+ Thread* self, Handle<mirror::ObjectArray<mirror::Class>> args, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
// Used to initialize a class in the allocation code path to ensure it is guarded by a StoreStore
diff --git a/runtime/mirror/class_loader-inl.h b/runtime/mirror/class_loader-inl.h
index 84fa80f..cc910b0 100644
--- a/runtime/mirror/class_loader-inl.h
+++ b/runtime/mirror/class_loader-inl.h
@@ -34,7 +34,6 @@
VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
if (kVisitClasses) {
// Visit classes loaded after.
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
ClassTable* const class_table = GetClassTable();
if (class_table != nullptr) {
class_table->VisitRoots(visitor);
diff --git a/runtime/mirror/method.cc b/runtime/mirror/method.cc
index 97973e6..9838b71 100644
--- a/runtime/mirror/method.cc
+++ b/runtime/mirror/method.cc
@@ -96,14 +96,18 @@
array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
+template <bool kTransactionActive>
Constructor* Constructor::CreateFromArtMethod(Thread* self, ArtMethod* method) {
DCHECK(method->IsConstructor()) << PrettyMethod(method);
auto* ret = down_cast<Constructor*>(StaticClass()->AllocObject(self));
if (LIKELY(ret != nullptr)) {
- static_cast<AbstractMethod*>(ret)->CreateFromArtMethod(method);
+ static_cast<AbstractMethod*>(ret)->CreateFromArtMethod<kTransactionActive>(method);
}
return ret;
}
+template Constructor* Constructor::CreateFromArtMethod<false>(Thread* self, ArtMethod* method);
+template Constructor* Constructor::CreateFromArtMethod<true>(Thread* self, ArtMethod* method);
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/method.h b/runtime/mirror/method.h
index 12a72fe..0b56964 100644
--- a/runtime/mirror/method.h
+++ b/runtime/mirror/method.h
@@ -60,6 +60,7 @@
// C++ mirror of java.lang.reflect.Constructor.
class MANAGED Constructor: public AbstractMethod {
public:
+ template <bool kTransactionActive = false>
static Constructor* CreateFromArtMethod(Thread* self, ArtMethod* method)
SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index bf24de5..54040cb 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -322,15 +322,11 @@
static jobject Class_getDeclaredConstructorInternal(
JNIEnv* env, jobject javaThis, jobjectArray args) {
ScopedFastNativeObjectAccess soa(env);
- auto* klass = DecodeClass(soa, javaThis);
- auto* params = soa.Decode<mirror::ObjectArray<mirror::Class>*>(args);
- StackHandleScope<1> hs(soa.Self());
- auto* declared_constructor = klass->GetDeclaredConstructor(soa.Self(), hs.NewHandle(params));
- if (declared_constructor != nullptr) {
- return soa.AddLocalReference<jobject>(
- mirror::Constructor::CreateFromArtMethod(soa.Self(), declared_constructor));
- }
- return nullptr;
+ mirror::Constructor* result = mirror::Class::GetDeclaredConstructorInternal(
+ soa.Self(),
+ DecodeClass(soa, javaThis),
+ soa.Decode<mirror::ObjectArray<mirror::Class>*>(args));
+ return soa.AddLocalReference<jobject>(result);
}
static ALWAYS_INLINE inline bool MethodMatchesConstructor(ArtMethod* m, bool public_only)
@@ -608,7 +604,8 @@
}
auto* constructor = klass->GetDeclaredConstructor(
soa.Self(),
- ScopedNullHandle<mirror::ObjectArray<mirror::Class>>());
+ ScopedNullHandle<mirror::ObjectArray<mirror::Class>>(),
+ sizeof(void*));
if (UNLIKELY(constructor == nullptr)) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
"%s has no zero argument constructor",
diff --git a/test/570-checker-osr/src/Main.java b/test/570-checker-osr/src/Main.java
index 1142d49..6514334 100644
--- a/test/570-checker-osr/src/Main.java
+++ b/test/570-checker-osr/src/Main.java
@@ -16,8 +16,28 @@
public class Main {
public static void main(String[] args) {
- new SubMain();
System.loadLibrary(args[0]);
+ Thread testThread = new Thread() {
+ public void run() {
+ performTest();
+ }
+ };
+ testThread.start();
+ try {
+ testThread.join(20 * 1000); // 20s timeout.
+ } catch (InterruptedException ie) {
+ System.out.println("Interrupted.");
+ System.exit(1);
+ }
+ Thread.State state = testThread.getState();
+ if (state != Thread.State.TERMINATED) {
+ System.out.println("Test timed out, current state: " + state);
+ System.exit(1);
+ }
+ }
+
+ public static void performTest() {
+ new SubMain();
if ($noinline$returnInt() != 53) {
throw new Error("Unexpected return value");
}