diff options
author | Brian Carlstrom <bdc@google.com> | 2013-07-10 08:46:00 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2013-07-10 08:46:28 -0700 |
commit | 2c3caadb48b066a0355cd3d2dcd5d5e9b48fc19c (patch) | |
tree | fc53640b73fb2f648e018a15bed8585818f3784f /src | |
parent | 75fe90cdb6e358a09047468b750648c8a3bfac9f (diff) | |
parent | 0b4e3ef67508a0b8c121b6b26ab5ea0a1d8e7141 (diff) | |
download | android_art-2c3caadb48b066a0355cd3d2dcd5d5e9b48fc19c.tar.gz android_art-2c3caadb48b066a0355cd3d2dcd5d5e9b48fc19c.tar.bz2 android_art-2c3caadb48b066a0355cd3d2dcd5d5e9b48fc19c.zip |
Merge remote-tracking branch 'goog/dalvik-dev' into merge-art-to-master
Change-Id: Id6bec909d7137192be0acc7bd8f736d1d8027745
Diffstat (limited to 'src')
226 files changed, 9968 insertions, 5514 deletions
diff --git a/src/atomic.cc b/src/atomic.cc index 4efb06187b..f2a998289c 100644 --- a/src/atomic.cc +++ b/src/atomic.cc @@ -130,20 +130,9 @@ bool QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* } while (__builtin_expect(status != 0, 0)); return prev == old_value; #elif defined(__i386__) - // cmpxchg8b implicitly uses %ebx which is also the PIC register. - int8_t status; - __asm__ __volatile__ ( - "pushl %%ebx\n" - "movl (%3), %%ebx\n" - "movl 4(%3), %%ecx\n" - "lock cmpxchg8b %1\n" - "sete %0\n" - "popl %%ebx" - : "=R" (status), "+m" (*addr) - : "A"(old_value), "D" (&new_value) - : "%ecx" - ); - return status != 0; + // The compiler does the right job and works better than inline assembly, especially with -O0 + // compilation. + return __sync_bool_compare_and_swap(addr, old_value, new_value); #else #error Unexpected architecture #endif diff --git a/src/atomic_integer.h b/src/atomic_integer.h index 188f4c28b0..c4a8de9817 100644 --- a/src/atomic_integer.h +++ b/src/atomic_integer.h @@ -71,7 +71,7 @@ class AtomicInteger { return success; } private: - int32_t value_; + volatile int32_t value_; }; } diff --git a/src/barrier_test.cc b/src/barrier_test.cc index 093ba3535a..55d2d3d715 100644 --- a/src/barrier_test.cc +++ b/src/barrier_test.cc @@ -88,7 +88,7 @@ TEST_F(BarrierTest, CheckWait) { // at this point. EXPECT_EQ(num_threads, count2); // Wait for all the threads to finish. - thread_pool.Wait(self); + thread_pool.Wait(self, true, false); // All three counts should be equal to num_threads now. EXPECT_EQ(count1, count2); EXPECT_EQ(count2, count3); diff --git a/src/base/macros.h b/src/base/macros.h index 8579872d58..847105d20c 100644 --- a/src/base/macros.h +++ b/src/base/macros.h @@ -136,6 +136,12 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #define ALWAYS_INLINE __attribute__ ((always_inline)) #endif +#if defined (__APPLE__) +#define HOT_ATTR +#else +#define HOT_ATTR __attribute__ ((hot)) +#endif + // bionic and glibc both have TEMP_FAILURE_RETRY, but Mac OS' libc doesn't. #ifndef TEMP_FAILURE_RETRY #define TEMP_FAILURE_RETRY(exp) ({ \ diff --git a/src/base/mutex.cc b/src/base/mutex.cc index a2851e5b3c..fbec826af2 100644 --- a/src/base/mutex.cc +++ b/src/base/mutex.cc @@ -777,6 +777,11 @@ void ConditionVariable::Signal(Thread* self) { } void ConditionVariable::Wait(Thread* self) { + guard_.CheckSafeToWait(self); + WaitHoldingLocks(self); +} + +void ConditionVariable::WaitHoldingLocks(Thread* self) { DCHECK(self == NULL || self == Thread::Current()); guard_.AssertExclusiveHeld(self); unsigned int old_recursion_count = guard_.recursion_count_; @@ -811,6 +816,7 @@ void ConditionVariable::Wait(Thread* self) { void ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { DCHECK(self == NULL || self == Thread::Current()); guard_.AssertExclusiveHeld(self); + guard_.CheckSafeToWait(self); unsigned int old_recursion_count = guard_.recursion_count_; #if ART_USE_FUTEXES timespec rel_ts; diff --git a/src/base/mutex.h b/src/base/mutex.h index a3efd5c6f4..24df572e97 100644 --- a/src/base/mutex.h +++ b/src/base/mutex.h @@ -53,7 +53,7 @@ namespace art { class ScopedContentionRecorder; class Thread; -const bool kDebugLocking = kIsDebugBuild; +const bool kDebugLocking = true || kIsDebugBuild; // Base class for all Mutex implementations class BaseMutex { @@ -141,7 +141,7 @@ class LOCKABLE Mutex : public BaseMutex { // Assert that the Mutex is exclusively held by the current thread. void AssertExclusiveHeld(const Thread* self) { - if (kDebugLocking && !gAborting) { + if (kDebugLocking && (gAborting == 0)) { CHECK(IsExclusiveHeld(self)) << *this; } } @@ -149,7 +149,7 @@ class LOCKABLE Mutex : public BaseMutex { // Assert that the Mutex is not held by the current thread. void AssertNotHeldExclusive(const Thread* self) { - if (kDebugLocking) { + if (kDebugLocking && (gAborting == 0)) { CHECK(!IsExclusiveHeld(self)) << *this; } } @@ -238,7 +238,7 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { // Assert the current thread has exclusive access to the ReaderWriterMutex. void AssertExclusiveHeld(const Thread* self) { - if (kDebugLocking) { + if (kDebugLocking & (gAborting == 0)) { CHECK(IsExclusiveHeld(self)) << *this; } } @@ -246,8 +246,8 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { // Assert the current thread doesn't have exclusive access to the ReaderWriterMutex. void AssertNotExclusiveHeld(const Thread* self) { - if (kDebugLocking) { - CHECK(!IsExclusiveHeld(self)); + if (kDebugLocking & (gAborting == 0)) { + CHECK(!IsExclusiveHeld(self)) << *this; } } void AssertNotWriterHeld(const Thread* self) { AssertNotExclusiveHeld(self); } @@ -257,7 +257,7 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { // Assert the current thread has shared access to the ReaderWriterMutex. void AssertSharedHeld(const Thread* self) { - if (kDebugLocking) { + if (kDebugLocking & (gAborting == 0)) { // TODO: we can only assert this well when self != NULL. CHECK(IsSharedHeld(self) || self == NULL) << *this; } @@ -267,7 +267,7 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { // Assert the current thread doesn't hold this ReaderWriterMutex either in shared or exclusive // mode. void AssertNotHeld(const Thread* self) { - if (kDebugLocking) { + if (kDebugLocking && (gAborting == 0)) { CHECK(!IsSharedHeld(self)) << *this; } } @@ -307,6 +307,10 @@ class ConditionVariable { // pointer copy, thereby defeating annotalysis. void Wait(Thread* self) NO_THREAD_SAFETY_ANALYSIS; void TimedWait(Thread* self, int64_t ms, int32_t ns) NO_THREAD_SAFETY_ANALYSIS; + // Variant of Wait that should be used with caution. Doesn't validate that no mutexes are held + // when waiting. + // TODO: remove this. + void WaitHoldingLocks(Thread* self) NO_THREAD_SAFETY_ANALYSIS; private: const char* const name_; diff --git a/src/base/stringpiece.cc b/src/base/stringpiece.cc index 715d964a12..47140e3247 100644 --- a/src/base/stringpiece.cc +++ b/src/base/stringpiece.cc @@ -21,12 +21,6 @@ namespace art { -bool operator<(const StringPiece& x, const StringPiece& y) { - const int r = memcmp(x.data(), y.data(), - std::min(x.size(), y.size())); - return ((r < 0) || ((r == 0) && (x.size() < y.size()))); -} - void StringPiece::CopyToString(std::string* target) const { target->assign(ptr_, length_); } diff --git a/src/base/stringpiece.h b/src/base/stringpiece.h index 193f5f7e7b..3664218860 100644 --- a/src/base/stringpiece.h +++ b/src/base/stringpiece.h @@ -188,7 +188,11 @@ inline bool operator!=(const StringPiece& x, const StringPiece& y) { return !(x == y); } -bool operator<(const StringPiece& x, const StringPiece& y); +inline bool operator<(const StringPiece& x, const StringPiece& y) { + const int r = memcmp(x.data(), y.data(), + std::min(x.size(), y.size())); + return ((r < 0) || ((r == 0) && (x.size() < y.size()))); +} inline bool operator>(const StringPiece& x, const StringPiece& y) { return y < x; diff --git a/src/base/timing_logger.cc b/src/base/timing_logger.cc index 6d5586c08f..c7cbbe504f 100644 --- a/src/base/timing_logger.cc +++ b/src/base/timing_logger.cc @@ -82,7 +82,7 @@ CumulativeLogger::~CumulativeLogger() { } void CumulativeLogger::SetName(const std::string& name) { - name_ = name; + name_.assign(name); } void CumulativeLogger::Start() { @@ -123,13 +123,40 @@ void CumulativeLogger::AddLogger(const TimingLogger &logger) { } } +void CumulativeLogger::AddNewLogger(const base::NewTimingLogger &logger) { + MutexLock mu(Thread::Current(), lock_); + const std::vector<std::pair<uint64_t, const char*> >& splits = logger.GetSplits(); + typedef std::vector<std::pair<uint64_t, const char*> >::const_iterator It; + if (kIsDebugBuild && splits.size() != histograms_.size()) { + LOG(ERROR) << "Mismatch in splits."; + typedef std::vector<Histogram<uint64_t> *>::const_iterator It2; + It it = splits.begin(); + It2 it2 = histograms_.begin(); + while ((it != splits.end()) && (it2 != histograms_.end())) { + if (it != splits.end()) { + LOG(ERROR) << "\tsplit: " << it->second; + ++it; + } + if (it2 != histograms_.end()) { + LOG(ERROR) << "\tpreviously record: " << (*it2)->Name(); + ++it2; + } + } + } + for (It it = splits.begin(), end = splits.end(); it != end; ++it) { + std::pair<uint64_t, const char*> split = *it; + uint64_t split_time = split.first; + const char* split_name = split.second; + AddPair(split_name, split_time); + } +} + void CumulativeLogger::Dump(std::ostream &os) { MutexLock mu(Thread::Current(), lock_); DumpHistogram(os); } void CumulativeLogger::AddPair(const std::string &label, uint64_t delta_time) { - // Convert delta time to microseconds so that we don't overflow our counters. delta_time /= kAdjust; if (index_ >= histograms_.size()) { @@ -154,4 +181,89 @@ void CumulativeLogger::DumpHistogram(std::ostream &os) { os << "Done Dumping histograms \n"; } + +namespace base { + +NewTimingLogger::NewTimingLogger(const char* name, bool precise, bool verbose) + : name_(name), precise_(precise), verbose_(verbose), + current_split_(NULL), current_split_start_ns_(0) { +} + +void NewTimingLogger::Reset() { + current_split_ = NULL; + current_split_start_ns_ = 0; + splits_.clear(); +} + +void NewTimingLogger::StartSplit(const char* new_split_label) { + DCHECK(current_split_ == NULL); + if (verbose_) { + LOG(INFO) << "Begin: " << new_split_label; + } + current_split_ = new_split_label; + current_split_start_ns_ = NanoTime(); +} + +// Ends the current split and starts the one given by the label. +void NewTimingLogger::NewSplit(const char* new_split_label) { + DCHECK(current_split_ != NULL); + uint64_t current_time = NanoTime(); + uint64_t split_time = current_time - current_split_start_ns_; + splits_.push_back(std::pair<uint64_t, const char*>(split_time, current_split_)); + if (verbose_) { + LOG(INFO) << "End: " << current_split_ << " " << PrettyDuration(split_time) << "\n" + << "Begin: " << new_split_label; + } + current_split_ = new_split_label; + current_split_start_ns_ = current_time; +} + +void NewTimingLogger::EndSplit() { + DCHECK(current_split_ != NULL); + uint64_t current_time = NanoTime(); + uint64_t split_time = current_time - current_split_start_ns_; + if (verbose_) { + LOG(INFO) << "End: " << current_split_ << " " << PrettyDuration(split_time); + } + splits_.push_back(std::pair<uint64_t, const char*>(split_time, current_split_)); +} + +uint64_t NewTimingLogger::GetTotalNs() const { + uint64_t total_ns = 0; + typedef std::vector<std::pair<uint64_t, const char*> >::const_iterator It; + for (It it = splits_.begin(), end = splits_.end(); it != end; ++it) { + std::pair<uint64_t, const char*> split = *it; + total_ns += split.first; + } + return total_ns; +} + +void NewTimingLogger::Dump(std::ostream &os) const { + uint64_t longest_split = 0; + uint64_t total_ns = 0; + typedef std::vector<std::pair<uint64_t, const char*> >::const_iterator It; + for (It it = splits_.begin(), end = splits_.end(); it != end; ++it) { + std::pair<uint64_t, const char*> split = *it; + uint64_t split_time = split.first; + longest_split = std::max(longest_split, split_time); + total_ns += split_time; + } + // Compute which type of unit we will use for printing the timings. + TimeUnit tu = GetAppropriateTimeUnit(longest_split); + uint64_t divisor = GetNsToTimeUnitDivisor(tu); + // Print formatted splits. + for (It it = splits_.begin(), end = splits_.end(); it != end; ++it) { + std::pair<uint64_t, const char*> split = *it; + uint64_t split_time = split.first; + if (!precise_ && divisor >= 1000) { + // Make the fractional part 0. + split_time -= split_time % (divisor / 1000); + } + os << name_ << ": " << std::setw(8) << FormatDuration(split_time, tu) << " " + << split.second << "\n"; + } + os << name_ << ": end, " << NsToMs(total_ns) << " ms\n"; +} + +} // namespace base } // namespace art diff --git a/src/base/timing_logger.h b/src/base/timing_logger.h index bbcc286ba4..65732b170d 100644 --- a/src/base/timing_logger.h +++ b/src/base/timing_logger.h @@ -45,6 +45,10 @@ class TimingLogger { friend class CumulativeLogger; }; +namespace base { + class NewTimingLogger; +} // namespace base + class CumulativeLogger { public: @@ -61,6 +65,7 @@ class CumulativeLogger { // parent class that is unable to determine the "name" of a sub-class. void SetName(const std::string& name); void AddLogger(const TimingLogger& logger) LOCKS_EXCLUDED(lock_); + void AddNewLogger(const base::NewTimingLogger& logger) LOCKS_EXCLUDED(lock_); private: @@ -79,6 +84,59 @@ class CumulativeLogger { DISALLOW_COPY_AND_ASSIGN(CumulativeLogger); }; +namespace base { + +// A replacement to timing logger that know when a split starts for the purposes of logging. +// TODO: replace uses of TimingLogger with base::NewTimingLogger. +class NewTimingLogger { + public: + explicit NewTimingLogger(const char* name, bool precise, bool verbose); + + // Clears current splits and labels. + void Reset(); + + // Starts a split, a split shouldn't be in progress. + void StartSplit(const char* new_split_label); + + // Ends the current split and starts the one given by the label. + void NewSplit(const char* new_split_label); + + // Ends the current split and records the end time. + void EndSplit(); + + uint64_t GetTotalNs() const; + + void Dump(std::ostream& os) const; + + const std::vector<std::pair<uint64_t, const char*> >& GetSplits() const { + return splits_; + } + + protected: + // The name of the timing logger. + const std::string name_; + + // Do we want to print the exactly recorded split (true) or round down to the time unit being + // used (false). + const bool precise_; + + // Verbose logging. + const bool verbose_; + + // The name of the current split. + const char* current_split_; + + // The nanosecond time the current split started on. + uint64_t current_split_start_ns_; + + // Splits are nanosecond times and split names. + std::vector<std::pair<uint64_t, const char*> > splits_; + + private: + DISALLOW_COPY_AND_ASSIGN(NewTimingLogger); +}; + +} // namespace base } // namespace art #endif // ART_SRC_TIMING_LOGGER_H_ diff --git a/src/check_jni.cc b/src/check_jni.cc index 19f8abfee0..403a2eb348 100644 --- a/src/check_jni.cc +++ b/src/check_jni.cc @@ -23,7 +23,7 @@ #include "class_linker.h" #include "class_linker-inl.h" #include "dex_file-inl.h" -#include "gc/space.h" +#include "gc/space/space.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" #include "mirror/abstract_method-inl.h" @@ -36,7 +36,7 @@ #include "thread.h" #define LIBCORE_CPP_JNI_HELPERS -#include <JNIHelp.h> // from libcore +#include <JNIHelp.h> // from libcore #undef LIBCORE_CPP_JNI_HELPERS namespace art { @@ -1215,7 +1215,10 @@ class CheckJNI { } static void FatalError(JNIEnv* env, const char* msg) { - CHECK_JNI_ENTRY(kFlag_NullableUtf, "Eu", env, msg); + // The JNI specification doesn't say it's okay to call FatalError with a pending exception, + // but you're about to abort anyway, and it's quite likely that you have a pending exception, + // and it's not unimaginable that you don't know that you do. So we allow it. + CHECK_JNI_ENTRY(kFlag_ExcepOkay | kFlag_NullableUtf, "Eu", env, msg); baseEnv(env)->FatalError(env, msg); CHECK_JNI_EXIT_VOID(); } diff --git a/src/class_linker.cc b/src/class_linker.cc index 70c7ff30b2..68d0fbbc55 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -34,8 +34,10 @@ #include "class_linker-inl.h" #include "debugger.h" #include "dex_file-inl.h" -#include "gc/card_table-inl.h" -#include "heap.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/heap.h" +#include "gc/space/image_space.h" #include "intern_table.h" #include "interpreter/interpreter.h" #include "leb128.h" @@ -44,7 +46,7 @@ #include "mirror/class.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" -#include "mirror/dex_cache.h" +#include "mirror/dex_cache-inl.h" #include "mirror/field-inl.h" #include "mirror/iftable-inl.h" #include "mirror/abstract_method.h" @@ -63,8 +65,6 @@ #include "ScopedLocalRef.h" #include "scoped_thread_state_change.h" #include "sirt_ref.h" -#include "gc/space.h" -#include "gc/space_bitmap.h" #include "stack_indirect_reference_table.h" #include "thread.h" #include "UniquePtr.h" @@ -74,7 +74,9 @@ namespace art { -void artInterpreterToQuickEntry(Thread* self, ShadowFrame* shadow_frame, JValue* result); +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); static void ThrowNoClassDefFoundError(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2))) @@ -195,12 +197,14 @@ ClassLinker* ClassLinker::CreateFromImage(InternTable* intern_table) { ClassLinker::ClassLinker(InternTable* intern_table) // dex_lock_ is recursive as it may be used in stack dumping. - : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel, true), + : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel), class_roots_(NULL), array_iftable_(NULL), init_done_(false), is_dirty_(false), - intern_table_(intern_table) { + intern_table_(intern_table), + portable_resolution_trampoline_(NULL), + quick_resolution_trampoline_(NULL) { CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax)); } @@ -212,7 +216,7 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class // java_lang_Class comes first, it's needed for AllocClass Thread* self = Thread::Current(); - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); SirtRef<mirror::Class> java_lang_Class(self, down_cast<mirror::Class*>(heap->AllocObject(self, NULL, @@ -545,7 +549,7 @@ void ClassLinker::FinishInit() { CHECK_EQ(java_lang_dex.GetFieldId(zombie->GetDexFieldIndex()).type_idx_, GetClassRoot(kJavaLangObject)->GetDexTypeIndex()); - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); heap->SetReferenceOffsets(referent->GetOffset(), queue->GetOffset(), queueNext->GetOffset(), @@ -591,7 +595,7 @@ bool ClassLinker::GenerateOatFile(const std::string& dex_filename, const char* class_path = Runtime::Current()->GetClassPathString().c_str(); - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); std::string boot_image_option_string("--boot-image="); boot_image_option_string += heap->GetImageSpace()->GetImageFilename(); const char* boot_image_option = boot_image_option_string.c_str(); @@ -662,22 +666,22 @@ bool ClassLinker::GenerateOatFile(const std::string& dex_filename, } void ClassLinker::RegisterOatFile(const OatFile& oat_file) { - MutexLock mu(Thread::Current(), dex_lock_); + WriterMutexLock mu(Thread::Current(), dex_lock_); RegisterOatFileLocked(oat_file); } void ClassLinker::RegisterOatFileLocked(const OatFile& oat_file) { - dex_lock_.AssertHeld(Thread::Current()); -#ifndef NDEBUG - for (size_t i = 0; i < oat_files_.size(); ++i) { - CHECK_NE(&oat_file, oat_files_[i]) << oat_file.GetLocation(); + dex_lock_.AssertExclusiveHeld(Thread::Current()); + if (kIsDebugBuild) { + for (size_t i = 0; i < oat_files_.size(); ++i) { + CHECK_NE(&oat_file, oat_files_[i]) << oat_file.GetLocation(); + } } -#endif oat_files_.push_back(&oat_file); } -OatFile* ClassLinker::OpenOat(const ImageSpace* space) { - MutexLock mu(Thread::Current(), dex_lock_); +OatFile* ClassLinker::OpenOat(const gc::space::ImageSpace* space) { + WriterMutexLock mu(Thread::Current(), dex_lock_); const Runtime* runtime = Runtime::Current(); const ImageHeader& image_header = space->GetImageHeader(); // Grab location but don't use Object::AsString as we haven't yet initialized the roots to @@ -708,7 +712,7 @@ OatFile* ClassLinker::OpenOat(const ImageSpace* space) { } const OatFile* ClassLinker::FindOpenedOatFileForDexFile(const DexFile& dex_file) { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); return FindOpenedOatFileFromDexLocation(dex_file.GetLocation()); } @@ -724,10 +728,9 @@ const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const std::string& return NULL; } -static const DexFile* FindDexFileInOatLocation(const std::string& dex_location, - uint32_t dex_location_checksum, - const std::string& oat_location) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +const DexFile* ClassLinker::FindDexFileInOatLocation(const std::string& dex_location, + uint32_t dex_location_checksum, + const std::string& oat_location) { UniquePtr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, NULL)); if (oat_file.get() == NULL) { return NULL; @@ -748,13 +751,13 @@ static const DexFile* FindDexFileInOatLocation(const std::string& dex_location, if (oat_dex_file->GetDexFileLocationChecksum() != dex_location_checksum) { return NULL; } - runtime->GetClassLinker()->RegisterOatFile(*oat_file.release()); + RegisterOatFileLocked(*oat_file.release()); return oat_dex_file->OpenDexFile(); } const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation(const std::string& dex_location, const std::string& oat_location) { - MutexLock mu(Thread::Current(), dex_lock_); + WriterMutexLock mu(Thread::Current(), dex_lock_); return FindOrCreateOatFileForDexLocationLocked(dex_location, oat_location); } @@ -856,7 +859,7 @@ const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const OatFile* oat_f } const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const std::string& dex_location) { - MutexLock mu(Thread::Current(), dex_lock_); + WriterMutexLock mu(Thread::Current(), dex_lock_); const OatFile* open_oat_file = FindOpenedOatFileFromDexLocation(dex_location); if (open_oat_file != NULL) { @@ -923,7 +926,7 @@ const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& } const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location) { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); return FindOatFileFromOatLocationLocked(oat_location); } @@ -944,13 +947,15 @@ void ClassLinker::InitFromImage() { VLOG(startup) << "ClassLinker::InitFromImage entering"; CHECK(!init_done_); - Heap* heap = Runtime::Current()->GetHeap(); - ImageSpace* space = heap->GetImageSpace(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::space::ImageSpace* space = heap->GetImageSpace(); OatFile* oat_file = OpenOat(space); CHECK(oat_file != NULL) << "Failed to open oat file for image"; CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U); CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U); CHECK(oat_file->GetOatHeader().GetImageFileLocation().empty()); + portable_resolution_trampoline_ = oat_file->GetOatHeader().GetPortableResolutionTrampoline(); + quick_resolution_trampoline_ = oat_file->GetOatHeader().GetQuickResolutionTrampoline(); mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); mirror::ObjectArray<mirror::DexCache>* dex_caches = dex_caches_object->AsObjectArray<mirror::DexCache>(); @@ -1037,17 +1042,14 @@ void ClassLinker::InitFromImageCallback(mirror::Object* obj, void* arg) { return; } - // Check if object is a method without its code set and point it to the resolution trampoline. + // Set entry points to interpreter for methods in interpreter only mode. if (obj->IsMethod()) { mirror::AbstractMethod* method = obj->AsMethod(); - // Install entry point from interpreter. - if (method->GetEntryPointFromCompiledCode() == NULL && !method->IsNative() && !method->IsProxyMethod()) { - method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter); - } else { - method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry); - } - if (method->GetEntryPointFromCompiledCode() == NULL) { - method->SetEntryPointFromCompiledCode(GetResolutionTrampoline()); + if (Runtime::Current()->GetInstrumentation()->InterpretOnly() && !method->IsNative()) { + method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterEntry); + if (method != Runtime::Current()->GetResolutionMethod()) { + method->SetEntryPointFromCompiledCode(GetInterpreterEntryPoint()); + } } } } @@ -1055,18 +1057,18 @@ void ClassLinker::InitFromImageCallback(mirror::Object* obj, void* arg) { // Keep in sync with InitCallback. Anything we visit, we need to // reinit references to when reinitializing a ClassLinker from a // mapped image. -void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg) { +void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg, bool clean_dirty) { visitor(class_roots_, arg); Thread* self = Thread::Current(); { - MutexLock mu(self, dex_lock_); + ReaderMutexLock mu(self, dex_lock_); for (size_t i = 0; i < dex_caches_.size(); i++) { visitor(dex_caches_[i], arg); } } { - MutexLock mu(self, *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); typedef Table::const_iterator It; // TODO: C++0x auto for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) { visitor(it->second, arg); @@ -1077,11 +1079,13 @@ void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg) { } visitor(array_iftable_, arg); - is_dirty_ = false; + if (clean_dirty) { + is_dirty_ = false; + } } void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) const { - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); typedef Table::const_iterator It; // TODO: C++0x auto for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) { if (!visitor(it->second, arg)) { @@ -1133,7 +1137,7 @@ ClassLinker::~ClassLinker() { } mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) { - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); mirror::Class* dex_cache_class = GetClassRoot(kJavaLangDexCache); SirtRef<mirror::DexCache> dex_cache(self, down_cast<mirror::DexCache*>(heap->AllocObject(self, dex_cache_class, @@ -1186,7 +1190,7 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_fi mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class, size_t class_size) { DCHECK_GE(class_size, sizeof(mirror::Class)); - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); SirtRef<mirror::Class> klass(self, heap->AllocObject(self, java_lang_Class, class_size)->AsClass()); klass->SetPrimitiveType(Primitive::kPrimNot); // default to not being primitive @@ -1608,14 +1612,16 @@ static void LinkCode(SirtRef<mirror::AbstractMethod>& method, const OatFile::Oat oat_method.LinkMethod(method.get()); // Install entry point from interpreter. - if (method->GetEntryPointFromCompiledCode() == NULL && !method->IsNative() && - !method->IsProxyMethod()) { - method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter); + Runtime* runtime = Runtime::Current(); + bool enter_interpreter = method->GetEntryPointFromCompiledCode() == NULL || + (runtime->GetInstrumentation()->InterpretOnly() && + !method->IsNative() && !method->IsProxyMethod()); + if (enter_interpreter) { + method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterEntry); } else { method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry); } - Runtime* runtime = Runtime::Current(); if (method->IsAbstract()) { method->SetEntryPointFromCompiledCode(GetAbstractMethodErrorStub()); return; @@ -1623,7 +1629,7 @@ static void LinkCode(SirtRef<mirror::AbstractMethod>& method, const OatFile::Oat if (method->IsStatic() && !method->IsConstructor()) { // For static methods excluding the class initializer, install the trampoline. - method->SetEntryPointFromCompiledCode(GetResolutionTrampoline()); + method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker())); } if (method->IsNative()) { @@ -1631,8 +1637,8 @@ static void LinkCode(SirtRef<mirror::AbstractMethod>& method, const OatFile::Oat method->UnregisterNative(Thread::Current()); } - if (method->GetEntryPointFromCompiledCode() == NULL) { - // No code? You must mean to go into the interpreter. + if (enter_interpreter) { + // Set entry point from compiled code if there's no code or in interpreter only mode. method->SetEntryPointFromCompiledCode(GetInterpreterEntryPoint()); } @@ -1807,7 +1813,7 @@ void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, SirtRef<mirror: } bool ClassLinker::IsDexFileRegisteredLocked(const DexFile& dex_file) const { - dex_lock_.AssertHeld(Thread::Current()); + dex_lock_.AssertSharedHeld(Thread::Current()); for (size_t i = 0; i != dex_caches_.size(); ++i) { if (dex_caches_[i]->GetDexFile() == &dex_file) { return true; @@ -1817,12 +1823,12 @@ bool ClassLinker::IsDexFileRegisteredLocked(const DexFile& dex_file) const { } bool ClassLinker::IsDexFileRegistered(const DexFile& dex_file) const { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); return IsDexFileRegisteredLocked(dex_file); } void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, SirtRef<mirror::DexCache>& dex_cache) { - dex_lock_.AssertHeld(Thread::Current()); + dex_lock_.AssertExclusiveHeld(Thread::Current()); CHECK(dex_cache.get() != NULL) << dex_file.GetLocation(); CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation())); dex_caches_.push_back(dex_cache.get()); @@ -1833,7 +1839,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, SirtRef<mirror: void ClassLinker::RegisterDexFile(const DexFile& dex_file) { Thread* self = Thread::Current(); { - MutexLock mu(self, dex_lock_); + ReaderMutexLock mu(self, dex_lock_); if (IsDexFileRegisteredLocked(dex_file)) { return; } @@ -1843,7 +1849,7 @@ void ClassLinker::RegisterDexFile(const DexFile& dex_file) { // get to a suspend point. SirtRef<mirror::DexCache> dex_cache(self, AllocDexCache(self, dex_file)); { - MutexLock mu(self, dex_lock_); + WriterMutexLock mu(self, dex_lock_); if (IsDexFileRegisteredLocked(dex_file)) { return; } @@ -1852,12 +1858,12 @@ void ClassLinker::RegisterDexFile(const DexFile& dex_file) { } void ClassLinker::RegisterDexFile(const DexFile& dex_file, SirtRef<mirror::DexCache>& dex_cache) { - MutexLock mu(Thread::Current(), dex_lock_); + WriterMutexLock mu(Thread::Current(), dex_lock_); RegisterDexFileLocked(dex_file, dex_cache); } mirror::DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); // Search assuming unique-ness of dex file. for (size_t i = 0; i != dex_caches_.size(); ++i) { mirror::DexCache* dex_cache = dex_caches_[i]; @@ -1883,7 +1889,7 @@ mirror::DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const { } void ClassLinker::FixupDexCaches(mirror::AbstractMethod* resolution_method) const { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i != dex_caches_.size(); ++i) { dex_caches_[i]->Fixup(resolution_method); } @@ -2017,15 +2023,16 @@ mirror::Class* ClassLinker::CreateArrayClass(const std::string& descriptor, CHECK(array_iftable_ != NULL); new_class->SetIfTable(array_iftable_); - // Inherit access flags from the component type. Arrays can't be - // used as a superclass or interface, so we want to add "final" + // Inherit access flags from the component type. + int access_flags = new_class->GetComponentType()->GetAccessFlags(); + // Lose any implementation detail flags; in particular, arrays aren't finalizable. + access_flags &= kAccJavaFlagsMask; + // Arrays can't be used as a superclass or interface, so we want to add "abstract final" // and remove "interface". - // - // Don't inherit any non-standard flags (e.g., kAccFinal) - // from component_type. We assume that the array class does not - // override finalize(). - new_class->SetAccessFlags(((new_class->GetComponentType()->GetAccessFlags() & - ~kAccInterface) | kAccFinal) & kAccJavaFlagsMask); + access_flags |= kAccAbstract | kAccFinal; + access_flags &= ~kAccInterface; + + new_class->SetAccessFlags(access_flags); mirror::Class* existing = InsertClass(descriptor, new_class.get(), false); if (existing == NULL) { @@ -2068,7 +2075,8 @@ mirror::Class* ClassLinker::FindPrimitiveClass(char type) { return NULL; } -mirror::Class* ClassLinker::InsertClass(const StringPiece& descriptor, mirror::Class* klass, bool image_class) { +mirror::Class* ClassLinker::InsertClass(const StringPiece& descriptor, mirror::Class* klass, + bool image_class) { if (VLOG_IS_ON(class_linker)) { mirror::DexCache* dex_cache = klass->GetDexCache(); std::string source; @@ -2079,9 +2087,10 @@ mirror::Class* ClassLinker::InsertClass(const StringPiece& descriptor, mirror::C LOG(INFO) << "Loaded class " << descriptor << source; } size_t hash = StringPieceHash()(descriptor); - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); Table& classes = image_class ? image_classes_ : classes_; - mirror::Class* existing = LookupClassLocked(descriptor.data(), klass->GetClassLoader(), hash, classes); + mirror::Class* existing = + LookupClassLocked(descriptor.data(), klass->GetClassLoader(), hash, classes); #ifndef NDEBUG // Check we don't have the class in the other table in error Table& other_classes = image_class ? classes_ : image_classes_; @@ -2090,6 +2099,7 @@ mirror::Class* ClassLinker::InsertClass(const StringPiece& descriptor, mirror::C if (existing != NULL) { return existing; } + Runtime::Current()->GetHeap()->VerifyObject(klass); classes.insert(std::make_pair(hash, klass)); Dirty(); return NULL; @@ -2097,7 +2107,7 @@ mirror::Class* ClassLinker::InsertClass(const StringPiece& descriptor, mirror::C bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) { size_t hash = Hash(descriptor); - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); typedef Table::iterator It; // TODO: C++0x auto // TODO: determine if its better to search classes_ or image_classes_ first ClassHelper kh; @@ -2125,7 +2135,7 @@ bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* mirror::Class* ClassLinker::LookupClass(const char* descriptor, const mirror::ClassLoader* class_loader) { size_t hash = Hash(descriptor); - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); // TODO: determine if its better to search classes_ or image_classes_ first mirror::Class* klass = NULL; // Use image class only if the class_loader is null. @@ -2165,7 +2175,7 @@ mirror::Class* ClassLinker::LookupClassLocked(const char* descriptor, void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Class*>& classes) { classes.clear(); size_t hash = Hash(descriptor); - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); typedef Table::const_iterator It; // TODO: C++0x auto // TODO: determine if its better to search classes_ or image_classes_ first ClassHelper kh(NULL, this); @@ -2243,7 +2253,6 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { const DexFile& dex_file = *klass->GetDexCache()->GetDexFile(); mirror::Class::Status oat_file_class_status(mirror::Class::kStatusNotReady); bool preverified = VerifyClassUsingOatFile(dex_file, klass, oat_file_class_status); - verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure; if (oat_file_class_status == mirror::Class::kStatusError) { LOG(WARNING) << "Skipping runtime verification of erroneous class " << PrettyDescriptor(klass) << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); @@ -2252,9 +2261,11 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { klass->SetStatus(mirror::Class::kStatusError); return; } + verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure; std::string error_msg; if (!preverified) { - verifier_failure = verifier::MethodVerifier::VerifyClass(klass, error_msg, Runtime::Current()->IsCompiler()); + verifier_failure = verifier::MethodVerifier::VerifyClass(klass, error_msg, + Runtime::Current()->IsCompiler()); } if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) { if (!preverified && verifier_failure != verifier::MethodVerifier::kNoFailure) { @@ -2286,6 +2297,15 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { ThrowVerifyError(klass, "%s", error_msg.c_str()); klass->SetStatus(mirror::Class::kStatusError); } + if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) { + // Class is verified so we don't need to do any access check in its methods. + // Let the interpreter know it by setting the kAccPreverified flag onto each + // method. + // Note: we're going here during compilation and at runtime. When we set the + // kAccPreverified flag when compiling image classes, the flag is recorded + // in the image and is set when loading the image. + klass->SetPreverifiedFlagOnAllMethods(); + } } bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass, @@ -2499,7 +2519,7 @@ mirror::AbstractMethod* ClassLinker::FindMethodForProxy(const mirror::Class* pro mirror::DexCache* dex_cache = NULL; { mirror::ObjectArray<mirror::Class>* resolved_types = proxy_method->GetDexCacheResolvedTypes(); - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i != dex_caches_.size(); ++i) { if (dex_caches_[i]->GetResolvedTypes() == resolved_types) { dex_cache = dex_caches_[i]; @@ -3880,7 +3900,7 @@ void ClassLinker::DumpAllClasses(int flags) const { // lock held, because it might need to resolve a field's type, which would try to take the lock. std::vector<mirror::Class*> all_classes; { - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); typedef Table::const_iterator It; // TODO: C++0x auto for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) { all_classes.push_back(it->second); @@ -3896,13 +3916,13 @@ void ClassLinker::DumpAllClasses(int flags) const { } void ClassLinker::DumpForSigQuit(std::ostream& os) const { - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); os << "Loaded classes: " << image_classes_.size() << " image classes; " << classes_.size() << " allocated classes\n"; } size_t ClassLinker::NumLoadedClasses() const { - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); return classes_.size() + image_classes_.size(); } diff --git a/src/class_linker.h b/src/class_linker.h index d41373c7d7..df336724fa 100644 --- a/src/class_linker.h +++ b/src/class_linker.h @@ -29,6 +29,11 @@ #include "oat_file.h" namespace art { +namespace gc { +namespace space { + class ImageSpace; +} // namespace space +} // namespace gc namespace mirror { class ClassLoader; class DexCache; @@ -37,7 +42,7 @@ namespace mirror { template<class T> class ObjectArray; class StackTraceElement; } // namespace mirror -class ImageSpace; + class InternTable; class ObjectLock; template<class T> class SirtRef; @@ -219,7 +224,7 @@ class ClassLinker { void VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) const LOCKS_EXCLUDED(Locks::classlinker_classes_lock_); - void VisitRoots(RootVisitor* visitor, void* arg) + void VisitRoots(RootVisitor* visitor, void* arg, bool clean_dirty) LOCKS_EXCLUDED(Locks::classlinker_classes_lock_, dex_lock_); mirror::DexCache* FindDexCache(const DexFile& dex_file) const @@ -240,7 +245,7 @@ class ClassLinker { LOCKS_EXCLUDED(dex_lock_); const OatFile* FindOatFileFromOatLocationLocked(const std::string& location) - EXCLUSIVE_LOCKS_REQUIRED(dex_lock_); + SHARED_LOCKS_REQUIRED(dex_lock_); // Finds the oat file for a dex location, generating the oat file if // it is missing or out of date. Returns the DexFile from within the @@ -334,6 +339,14 @@ class ClassLinker { is_dirty_ = true; } + const void* GetPortableResolutionTrampoline() const { + return portable_resolution_trampoline_; + } + + const void* GetQuickResolutionTrampoline() const { + return quick_resolution_trampoline_; + } + private: explicit ClassLinker(InternTable*); @@ -346,7 +359,7 @@ class ClassLinker { // Initialize class linker from one or more images. void InitFromImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - OatFile* OpenOat(const ImageSpace* space) + OatFile* OpenOat(const gc::space::ImageSpace* space) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void InitFromImageCallback(mirror::Object* obj, void* arg) @@ -420,7 +433,7 @@ class ClassLinker { void RegisterDexFileLocked(const DexFile& dex_file, SirtRef<mirror::DexCache>& dex_cache) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsDexFileRegisteredLocked(const DexFile& dex_file) const EXCLUSIVE_LOCKS_REQUIRED(dex_lock_); + bool IsDexFileRegisteredLocked(const DexFile& dex_file) const SHARED_LOCKS_REQUIRED(dex_lock_); void RegisterOatFileLocked(const OatFile& oat_file) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_); @@ -489,10 +502,15 @@ class ClassLinker { LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const OatFile* FindOpenedOatFileFromDexLocation(const std::string& dex_location) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, dex_lock_); + const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) + SHARED_LOCKS_REQUIRED(dex_lock_); + const DexFile* FindDexFileInOatLocation(const std::string& dex_location, + uint32_t dex_location_checksum, + const std::string& oat_location) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) - EXCLUSIVE_LOCKS_REQUIRED(dex_lock_); + const DexFile* VerifyAndOpenDexFileFromOatFile(const OatFile* oat_file, const std::string& dex_location, uint32_t dex_location_checksum) @@ -508,7 +526,7 @@ class ClassLinker { std::vector<const DexFile*> boot_class_path_; - mutable Mutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; std::vector<mirror::DexCache*> dex_caches_ GUARDED_BY(dex_lock_); std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_); @@ -522,8 +540,7 @@ class ClassLinker { mirror::Class* LookupClassLocked(const char* descriptor, const mirror::ClassLoader* class_loader, size_t hash, const Table& classes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - EXCLUSIVE_LOCKS_REQUIRED(Locks::classlinker_classes_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::classlinker_classes_lock_); // indexes into class_roots_. // needs to be kept in sync with class_roots_descriptors_. @@ -595,6 +612,9 @@ class ClassLinker { InternTable* intern_table_; + const void* portable_resolution_trampoline_; + const void* quick_resolution_trampoline_; + friend class CommonTest; friend class ImageWriter; // for GetClassRoots friend class ObjectTest; diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc index 73bdc613ab..e5844b0038 100644 --- a/src/class_linker_test.cc +++ b/src/class_linker_test.cc @@ -22,7 +22,7 @@ #include "class_linker-inl.h" #include "common_test.h" #include "dex_file.h" -#include "heap.h" +#include "gc/heap.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" #include "mirror/field-inl.h" @@ -90,6 +90,7 @@ class ClassLinkerTest : public CommonTest { EXPECT_TRUE(primitive->GetVTable() == NULL); EXPECT_EQ(0, primitive->GetIfTableCount()); EXPECT_TRUE(primitive->GetIfTable() == NULL); + EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags()); } void AssertArrayClass(const std::string& array_descriptor, @@ -100,6 +101,7 @@ class ClassLinkerTest : public CommonTest { ClassHelper array_component_ch(array->GetComponentType()); EXPECT_STREQ(component_type.c_str(), array_component_ch.GetDescriptor()); EXPECT_EQ(class_loader, array->GetClassLoader()); + EXPECT_EQ(kAccFinal | kAccAbstract, (array->GetAccessFlags() & (kAccFinal | kAccAbstract))); AssertArrayClass(array_descriptor, array); } @@ -331,7 +333,7 @@ class ClassLinkerTest : public CommonTest { const char* descriptor = dex->GetTypeDescriptor(type_id); AssertDexFileClass(class_loader, descriptor); } - class_linker_->VisitRoots(TestRootVisitor, NULL); + class_linker_->VisitRoots(TestRootVisitor, NULL, false); // Verify the dex cache has resolution methods in all resolved method slots DexCache* dex_cache = class_linker_->FindDexCache(*dex); ObjectArray<AbstractMethod>* resolved_methods = dex_cache->GetResolvedMethods(); diff --git a/src/common_test.h b/src/common_test.h index 8d8ad5eb52..11bd4f0957 100644 --- a/src/common_test.h +++ b/src/common_test.h @@ -28,8 +28,8 @@ #include "class_linker.h" #include "compiler/driver/compiler_driver.h" #include "dex_file-inl.h" +#include "gc/heap.h" #include "gtest/gtest.h" -#include "heap.h" #include "instruction_set.h" #include "mirror/class_loader.h" #include "oat_file.h" @@ -296,8 +296,8 @@ class CommonTest : public testing::Test { boot_class_path_.push_back(java_lang_dex_file_); boot_class_path_.push_back(conscrypt_file_); - std::string min_heap_string(StringPrintf("-Xms%zdm", Heap::kDefaultInitialSize / MB)); - std::string max_heap_string(StringPrintf("-Xmx%zdm", Heap::kDefaultMaximumSize / MB)); + std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); + std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB)); Runtime::Options options; options.push_back(std::make_pair("compiler", reinterpret_cast<void*>(NULL))); @@ -313,47 +313,50 @@ class CommonTest : public testing::Test { // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, // give it away now and then switch to a more managable ScopedObjectAccess. Thread::Current()->TransitionFromRunnableToSuspended(kNative); - // Whilst we're in native take the opportunity to initialize well known classes. - WellKnownClasses::InitClasses(Thread::Current()->GetJniEnv()); - ScopedObjectAccess soa(Thread::Current()); - ASSERT_TRUE(runtime_.get() != NULL); - class_linker_ = runtime_->GetClassLinker(); + { + ScopedObjectAccess soa(Thread::Current()); + ASSERT_TRUE(runtime_.get() != NULL); + class_linker_ = runtime_->GetClassLinker(); - InstructionSet instruction_set = kNone; + InstructionSet instruction_set = kNone; #if defined(__arm__) - instruction_set = kThumb2; + instruction_set = kThumb2; #elif defined(__mips__) - instruction_set = kMips; + instruction_set = kMips; #elif defined(__i386__) - instruction_set = kX86; + instruction_set = kX86; #endif - // TODO: make selectable + // TODO: make selectable #if defined(ART_USE_PORTABLE_COMPILER) - CompilerBackend compiler_backend = kPortable; + CompilerBackend compiler_backend = kPortable; #else - CompilerBackend compiler_backend = kQuick; + CompilerBackend compiler_backend = kQuick; #endif - if (!runtime_->HasResolutionMethod()) { - runtime_->SetResolutionMethod(runtime_->CreateResolutionMethod()); - } - for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { - Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i); - if (!runtime_->HasCalleeSaveMethod(type)) { - runtime_->SetCalleeSaveMethod( - runtime_->CreateCalleeSaveMethod(instruction_set, type), type); + if (!runtime_->HasResolutionMethod()) { + runtime_->SetResolutionMethod(runtime_->CreateResolutionMethod()); + } + for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { + Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i); + if (!runtime_->HasCalleeSaveMethod(type)) { + runtime_->SetCalleeSaveMethod( + runtime_->CreateCalleeSaveMethod(instruction_set, type), type); + } } + class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); + compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set, + true, new CompilerDriver::DescriptorSet, + 2, false, true, true)); } - class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); - image_classes_.reset(new std::set<std::string>); - compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set, true, 2, false, - image_classes_.get(), true, true)); + // We typically don't generate an image in unit tests, disable this optimization by default. + compiler_driver_->SetSupportBootImageFixup(false); + // We're back in native, take the opportunity to initialize well known classes. + WellKnownClasses::InitClasses(Thread::Current()->GetJniEnv()); // Create the heap thread pool so that the GC runs in parallel for tests. Normally, the thread // pool is created by the runtime. runtime_->GetHeap()->CreateThreadPool(); - runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption before the test } @@ -389,7 +392,6 @@ class CommonTest : public testing::Test { (*icu_cleanup_fn)(); compiler_driver_.reset(); - image_classes_.reset(); STLDeleteElements(&opened_dex_files_); Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test @@ -522,7 +524,6 @@ class CommonTest : public testing::Test { // Owned by the runtime ClassLinker* class_linker_; UniquePtr<CompilerDriver> compiler_driver_; - UniquePtr<std::set<std::string> > image_classes_; private: std::vector<const DexFile*> opened_dex_files_; diff --git a/src/common_throws.cc b/src/common_throws.cc index dc3627a0b2..66e512eef3 100644 --- a/src/common_throws.cc +++ b/src/common_throws.cc @@ -27,6 +27,7 @@ #include "mirror/object_array-inl.h" #include "object_utils.h" #include "thread.h" +#include "verifier/method_verifier.h" #include <sstream> @@ -67,7 +68,7 @@ static void ThrowException(const ThrowLocation* throw_location, const char* exce // ArithmeticException -void ThrowArithmeticExceptionDivideByZero(Thread* self) { +void ThrowArithmeticExceptionDivideByZero() { ThrowException(NULL, "Ljava/lang/ArithmeticException;", NULL, "divide by zero"); } @@ -283,16 +284,34 @@ void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str()); } -void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx, - InvokeType type) { - mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache(); - const DexFile& dex_file = *dex_cache->GetDexFile(); +static void ThrowNullPointerExceptionForMethodAccessImpl(const ThrowLocation& throw_location, + uint32_t method_idx, + const DexFile& dex_file, + InvokeType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::ostringstream msg; msg << "Attempt to invoke " << type << " method '" << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference"; ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str()); } +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx, + InvokeType type) { + mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method_idx, + dex_file, type); +} + +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, + mirror::AbstractMethod* method, + InvokeType type) { + mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method->GetDexMethodIndex(), + dex_file, type); +} + void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { const DexFile::CodeItem* code = MethodHelper(throw_location.GetMethod()).GetCodeItem(); uint32_t throw_dex_pc = throw_location.GetDexPc(); @@ -317,6 +336,23 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { case Instruction::INVOKE_INTERFACE_RANGE: ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_3rc(), kInterface); break; + case Instruction::INVOKE_VIRTUAL_QUICK: + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { + // Since we replaced the method index, we ask the verifier to tell us which + // method is invoked at this location. + mirror::AbstractMethod* method = + verifier::MethodVerifier::FindInvokedMethodAtDexPc(throw_location.GetMethod(), + throw_location.GetDexPc()); + if (method != NULL) { + // NPE with precise message. + ThrowNullPointerExceptionForMethodAccess(throw_location, method, kVirtual); + } else { + // NPE with imprecise message. + ThrowNullPointerException(&throw_location, + "Attempt to invoke a virtual method on a null object reference"); + } + break; + } case Instruction::IGET: case Instruction::IGET_WIDE: case Instruction::IGET_OBJECT: @@ -330,6 +366,24 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */); break; } + case Instruction::IGET_QUICK: + case Instruction::IGET_WIDE_QUICK: + case Instruction::IGET_OBJECT_QUICK: { + // Since we replaced the field index, we ask the verifier to tell us which + // field is accessed at this location. + mirror::Field* field = + verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(), + throw_location.GetDexPc()); + if (field != NULL) { + // NPE with precise message. + ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */); + } else { + // NPE with imprecise message. + ThrowNullPointerException(&throw_location, + "Attempt to read from a field on a null object reference"); + } + break; + } case Instruction::IPUT: case Instruction::IPUT_WIDE: case Instruction::IPUT_OBJECT: @@ -343,6 +397,24 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */); break; } + case Instruction::IPUT_QUICK: + case Instruction::IPUT_WIDE_QUICK: + case Instruction::IPUT_OBJECT_QUICK: { + // Since we replaced the field index, we ask the verifier to tell us which + // field is accessed at this location. + mirror::Field* field = + verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(), + throw_location.GetDexPc()); + if (field != NULL) { + // NPE with precise message. + ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */); + } else { + // NPE with imprecise message. + ThrowNullPointerException(&throw_location, + "Attempt to write to a field on a null object reference"); + } + break; + } case Instruction::AGET: case Instruction::AGET_WIDE: case Instruction::AGET_OBJECT: diff --git a/src/common_throws.h b/src/common_throws.h index 5555435051..fbaf4c199f 100644 --- a/src/common_throws.h +++ b/src/common_throws.h @@ -32,7 +32,7 @@ class ThrowLocation; // ArithmeticException -void ThrowArithmeticExceptionDivideByZero(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowArithmeticExceptionDivideByZero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // ArrayIndexOutOfBoundsException @@ -153,6 +153,11 @@ void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_locatio InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, + mirror::AbstractMethod* method, + InvokeType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/src/compiler/dex/arena_bit_vector.cc b/src/compiler/dex/arena_bit_vector.cc index 6f664e5565..1fbf7740ac 100644 --- a/src/compiler/dex/arena_bit_vector.cc +++ b/src/compiler/dex/arena_bit_vector.cc @@ -113,18 +113,6 @@ void ArenaBitVector::Union(const ArenaBitVector* src) { } } -// Are we equal to another bit vector? Note: expandability attributes must also match. -bool ArenaBitVector::Equal(const ArenaBitVector* src) { - if (storage_size_ != src->GetStorageSize() || - expandable_ != src->IsExpandable()) - return false; - - for (unsigned int idx = 0; idx < storage_size_; idx++) { - if (storage_[idx] != src->GetRawStorageWord(idx)) return false; - } - return true; -} - // Count the number of bits that are set. int ArenaBitVector::NumSetBits() { @@ -136,43 +124,6 @@ int ArenaBitVector::NumSetBits() return count; } -// Return the position of the next set bit. -1 means end-of-element reached. -// TUNING: Hot function. -int ArenaBitVector::Iterator::Next() -{ - // Did anything obviously change since we started? - DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8); - DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage()); - - if (bit_index_ >= bit_size_) return -1; - - uint32_t word_index = bit_index_ >> 5; - uint32_t end_word_index = bit_size_ >> 5; - uint32_t word = bit_storage_[word_index++]; - - // Mask out any bits in the first word we've already considered. - word &= ~((1 << (bit_index_ & 0x1f))-1); - - for (; word_index <= end_word_index;) { - uint32_t bit_pos = bit_index_ & 0x1f; - if (word == 0) { - bit_index_ += (32 - bit_pos); - word = bit_storage_[word_index++]; - continue; - } - for (; bit_pos < 32; bit_pos++) { - if (word & (1 << bit_pos)) { - bit_index_++; - return bit_index_ - 1; - } - bit_index_++; - } - word = bit_storage_[word_index++]; - } - bit_index_ = bit_size_; - return -1; -} - /* * Mark specified number of bits as "set". Cannot set all bits like ClearAll * since there might be unused bits - setting those to one will confuse the diff --git a/src/compiler/dex/arena_bit_vector.h b/src/compiler/dex/arena_bit_vector.h index f5c471c5d3..a950e82498 100644 --- a/src/compiler/dex/arena_bit_vector.h +++ b/src/compiler/dex/arena_bit_vector.h @@ -39,7 +39,33 @@ class ArenaBitVector { bit_index_(0), bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {}; - int Next(); // Returns -1 when no next. + // Return the position of the next set bit. -1 means end-of-element reached. + int Next() { + // Did anything obviously change since we started? + DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8); + DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage()); + + if (bit_index_ >= bit_size_) return -1; + + uint32_t word_index = bit_index_ / 32; + uint32_t word = bit_storage_[word_index]; + // Mask out any bits in the first word we've already considered. + word >>= bit_index_ & 0x1f; + if (word == 0) { + bit_index_ &= ~0x1f; + do { + word_index++; + if ((word_index * 32) >= bit_size_) { + bit_index_ = bit_size_; + return -1; + } + word = bit_storage_[word_index]; + bit_index_ += 32; + } while (word == 0); + } + bit_index_ += CTZ(word) + 1; + return bit_index_ - 1; + } static void* operator new(size_t size, ArenaAllocator* arena) { return arena->NewMem(sizeof(ArenaBitVector::Iterator), true, @@ -73,13 +99,19 @@ class ArenaBitVector { void Copy(ArenaBitVector* src); void Intersect(const ArenaBitVector* src2); void Union(const ArenaBitVector* src); - bool Equal(const ArenaBitVector* src); + // Are we equal to another bit vector? Note: expandability attributes must also match. + bool Equal(const ArenaBitVector* src) { + return (storage_size_ == src->GetStorageSize()) && + (expandable_ == src->IsExpandable()) && + (memcmp(storage_, src->GetRawStorage(), storage_size_ * 4) == 0); + } int NumSetBits(); uint32_t GetStorageSize() const { return storage_size_; } bool IsExpandable() const { return expandable_; } uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; } uint32_t* GetRawStorage() { return storage_; } + const uint32_t* GetRawStorage() const { return storage_; } private: ArenaAllocator* const arena_; diff --git a/src/compiler/dex/dataflow_iterator-inl.h b/src/compiler/dex/dataflow_iterator-inl.h new file mode 100644 index 0000000000..b20004decc --- /dev/null +++ b/src/compiler/dex/dataflow_iterator-inl.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ +#define ART_SRC_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ + +#include "dataflow_iterator.h" + +namespace art { + +inline BasicBlock* DataflowIterator::NextBody(bool had_change) { + changed_ |= had_change; + BasicBlock* res = NULL; + if (reverse_) { + if (is_iterative_ && changed_ && (idx_ < 0)) { + idx_ = start_idx_; + changed_ = false; + } + if (idx_ >= 0) { + int bb_id = block_id_list_->Get(idx_--); + res = mir_graph_->GetBasicBlock(bb_id); + } + } else { + if (is_iterative_ && changed_ && (idx_ >= end_idx_)) { + idx_ = start_idx_; + changed_ = false; + } + if (idx_ < end_idx_) { + int bb_id = block_id_list_->Get(idx_++); + res = mir_graph_->GetBasicBlock(bb_id); + } + } + return res; +} + +// AllNodes uses the existing GrowableArray iterator, so use different NextBody(). +inline BasicBlock* AllNodesIterator::NextBody(bool had_change) { + changed_ |= had_change; + BasicBlock* res = NULL; + bool keep_looking = true; + while (keep_looking) { + res = all_nodes_iterator_->Next(); + if (is_iterative_ && changed_ && (res == NULL)) { + all_nodes_iterator_->Reset(); + changed_ = false; + } else if ((res == NULL) || (!res->hidden)) { + keep_looking = false; + } + } + return res; +} + +} // namespace art + +#endif // ART_SRC_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ diff --git a/src/compiler/dex/dataflow_iterator.cc b/src/compiler/dex/dataflow_iterator.cc deleted file mode 100644 index bb5b969925..0000000000 --- a/src/compiler/dex/dataflow_iterator.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dataflow_iterator.h" - -namespace art { - - BasicBlock* DataflowIterator::NextBody(bool had_change) { - changed_ |= had_change; - BasicBlock* res = NULL; - if (reverse_) { - if (is_iterative_ && changed_ && (idx_ < 0)) { - idx_ = start_idx_; - changed_ = false; - } - if (idx_ >= 0) { - int bb_id = block_id_list_->Get(idx_--); - res = mir_graph_->GetBasicBlock(bb_id); - } - } else { - if (is_iterative_ && changed_ && (idx_ >= end_idx_)) { - idx_ = start_idx_; - changed_ = false; - } - if (idx_ < end_idx_) { - int bb_id = block_id_list_->Get(idx_++); - res = mir_graph_->GetBasicBlock(bb_id); - } - } - return res; - } - - // AllNodes uses the existing GrowableArray iterator, so use different NextBody(). - BasicBlock* AllNodesIterator::NextBody(bool had_change) { - changed_ |= had_change; - BasicBlock* res = NULL; - bool keep_looking = true; - while (keep_looking) { - res = all_nodes_iterator_->Next(); - if (is_iterative_ && changed_ && (res == NULL)) { - all_nodes_iterator_->Reset(); - changed_ = false; - } else if ((res == NULL) || (!res->hidden)) { - keep_looking = false; - } - } - return res; - } - -} // namespace art diff --git a/src/compiler/dex/dataflow_iterator.h b/src/compiler/dex/dataflow_iterator.h index a4b38bd80f..12cbf9cadf 100644 --- a/src/compiler/dex/dataflow_iterator.h +++ b/src/compiler/dex/dataflow_iterator.h @@ -71,7 +71,7 @@ namespace art { idx_(0), changed_(false) {} - virtual BasicBlock* NextBody(bool had_change); + virtual BasicBlock* NextBody(bool had_change) ALWAYS_INLINE; MIRGraph* const mir_graph_; const bool is_iterative_; @@ -86,7 +86,6 @@ namespace art { class ReachableNodesIterator : public DataflowIterator { public: - ReachableNodesIterator(MIRGraph* mir_graph, bool is_iterative) : DataflowIterator(mir_graph, is_iterative, 0, mir_graph->GetNumReachableBlocks(), false) { @@ -97,7 +96,6 @@ namespace art { class PreOrderDfsIterator : public DataflowIterator { public: - PreOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative) : DataflowIterator(mir_graph, is_iterative, 0, mir_graph->GetNumReachableBlocks(), false) { @@ -119,7 +117,6 @@ namespace art { class ReversePostOrderDfsIterator : public DataflowIterator { public: - ReversePostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative) : DataflowIterator(mir_graph, is_iterative, mir_graph->GetNumReachableBlocks() -1, 0, true) { @@ -130,7 +127,6 @@ namespace art { class PostOrderDOMIterator : public DataflowIterator { public: - PostOrderDOMIterator(MIRGraph* mir_graph, bool is_iterative) : DataflowIterator(mir_graph, is_iterative, 0, mir_graph->GetNumReachableBlocks(), false) { @@ -141,18 +137,17 @@ namespace art { class AllNodesIterator : public DataflowIterator { public: - AllNodesIterator(MIRGraph* mir_graph, bool is_iterative) : DataflowIterator(mir_graph, is_iterative, 0, 0, false) { all_nodes_iterator_ = new (mir_graph->GetArena()) GrowableArray<BasicBlock*>::Iterator (mir_graph->GetBlockList()); } - virtual void Reset() { + void Reset() { all_nodes_iterator_->Reset(); } - virtual BasicBlock* NextBody(bool had_change); + BasicBlock* NextBody(bool had_change) ALWAYS_INLINE; private: GrowableArray<BasicBlock*>::Iterator* all_nodes_iterator_; diff --git a/src/compiler/dex/dex_to_dex_compiler.cc b/src/compiler/dex/dex_to_dex_compiler.cc new file mode 100644 index 0000000000..afb29f4163 --- /dev/null +++ b/src/compiler/dex/dex_to_dex_compiler.cc @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/logging.h" +#include "base/mutex.h" +#include "compiler/driver/compiler_driver.h" +#include "compiler/driver/dex_compilation_unit.h" +#include "dex_file-inl.h" +#include "dex_instruction-inl.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/dex_cache.h" +#include "mirror/field-inl.h" + +namespace art { +namespace optimizer { + +// Controls quickening activation. +const bool kEnableQuickening = true; +// Controls logging. +const bool kEnableLogging = false; + +class DexCompiler { + public: + DexCompiler(art::CompilerDriver& compiler, + const DexCompilationUnit& unit) + : driver_(compiler), + unit_(unit) {}; + + ~DexCompiler() {}; + + void Compile(); + + private: + const DexFile& GetDexFile() const { + return *unit_.GetDexFile(); + } + + // TODO: since the whole compilation pipeline uses a "const DexFile", we need + // to "unconst" here. The DEX-to-DEX compiler should work on a non-const DexFile. + DexFile& GetModifiableDexFile() { + return *const_cast<DexFile*>(unit_.GetDexFile()); + } + + void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_put); + void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_range); + + CompilerDriver& driver_; + const DexCompilationUnit& unit_; + + DISALLOW_COPY_AND_ASSIGN(DexCompiler); +}; + +// Ensures write access to a part of DEX file. +// +// If a DEX file is read-only, it modifies its protection (mprotect) so it allows +// write access to the part of DEX file defined by an address and a length. +// In this case, it also takes the DexFile::modification_lock to prevent from +// concurrent protection modification from a parallel DEX-to-DEX compilation on +// the same DEX file. +// When the instance is destroyed, it recovers original protection and releases +// the lock. +// TODO: as this scoped class is similar to a MutexLock we should use annotalysis +// to capture the locking behavior. +class ScopedDexWriteAccess { + public: + ScopedDexWriteAccess(DexFile& dex_file, Instruction* inst, + size_t length) + : dex_file_(dex_file), + address_(reinterpret_cast<uint8_t*>(inst)), + length_(length), + is_read_only_(dex_file_.IsReadOnly()) { + if (is_read_only_) { + // We need to enable DEX write access. To avoid concurrent DEX write access + // modification, we take the DexFile::modification_lock before. + dex_file_.GetModificationLock().ExclusiveLock(Thread::Current()); + bool success = dex_file_.EnableWrite(address_, length_); + DCHECK(success) << "Failed to enable DEX write access"; + } + } + + ~ScopedDexWriteAccess() { + DCHECK_EQ(is_read_only_, dex_file_.IsReadOnly()); + if (is_read_only_) { + bool success = dex_file_.DisableWrite(address_, length_); + DCHECK(success) << "Failed to disable DEX write access"; + // Now we recovered original read-only protection, we can release the + // DexFile::modification_lock. + dex_file_.GetModificationLock().ExclusiveUnlock(Thread::Current()); + } + } + + private: + DexFile& dex_file_; + // TODO: make address_ const. + uint8_t* address_; + const size_t length_; + const bool is_read_only_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDexWriteAccess); +}; + +void DexCompiler::Compile() { + const DexFile::CodeItem* code_item = unit_.GetCodeItem(); + const uint16_t* insns = code_item->insns_; + const uint32_t insns_size = code_item->insns_size_in_code_units_; + Instruction* inst = const_cast<Instruction*>(Instruction::At(insns)); + + for (uint32_t dex_pc = 0; dex_pc < insns_size; + inst = const_cast<Instruction*>(inst->Next()), dex_pc = inst->GetDexPc(insns)) { + switch (inst->Opcode()) { + case Instruction::IGET: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false); + break; + case Instruction::IGET_WIDE: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false); + break; + + case Instruction::IGET_OBJECT: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false); + break; + + case Instruction::IPUT: + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + case Instruction::IPUT_CHAR: + case Instruction::IPUT_SHORT: + // These opcodes have the same implementation in interpreter so group + // them under IPUT_QUICK. + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true); + break; + + case Instruction::IPUT_WIDE: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true); + break; + + case Instruction::IPUT_OBJECT: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true); + break; + + case Instruction::INVOKE_VIRTUAL: + CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false); + break; + + case Instruction::INVOKE_VIRTUAL_RANGE: + CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true); + break; + + default: + // No optimization. + break; + } + } +} + +void DexCompiler::CompileInstanceFieldAccess(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_put) { + if (!kEnableQuickening) { + return; + } + uint32_t field_idx = inst->VRegC_22c(); + int field_offset; + bool is_volatile; + bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset, + is_volatile, is_put); + if (fast_path && !is_volatile && IsUint(16, field_offset)) { + // TODO: use VLOG ? + if (kEnableLogging) { + LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode()) + << " to " << Instruction::Name(new_opcode) + << " by replacing field index " << field_idx + << " by field offset " << field_offset + << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + } + // We are modifying 4 consecutive bytes. + ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u); + inst->SetOpcode(new_opcode); + // Replace field index by field offset. + inst->SetVRegC_22c(static_cast<uint16_t>(field_offset)); + } +} + +void DexCompiler::CompileInvokeVirtual(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_range) { + if (!kEnableQuickening) { + return; + } + uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + CompilerDriver::MethodReference target_method(&GetDexFile(), method_idx); + InvokeType invoke_type = kVirtual; + InvokeType original_invoke_type = invoke_type; + int vtable_idx; + uintptr_t direct_code; + uintptr_t direct_method; + bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type, + target_method, vtable_idx, + direct_code, direct_method, + false); + // TODO: support devirtualization. + if (fast_path && original_invoke_type == invoke_type) { + if (vtable_idx >= 0 && IsUint(16, vtable_idx)) { + // TODO: use VLOG ? + if (kEnableLogging) { + LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode()) + << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")" + << " to " << Instruction::Name(new_opcode) + << " by replacing method index " << method_idx + << " by vtable index " << vtable_idx + << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + } + // We are modifying 4 consecutive bytes. + ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u); + inst->SetOpcode(new_opcode); + // Replace method index by vtable index. + if (is_range) { + inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx)); + } else { + inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx)); + } + } + } +} + +} // namespace optimizer +} // namespace art + +extern "C" art::CompiledMethod* + ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, + uint32_t access_flags, art::InvokeType invoke_type, + uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, + const art::DexFile& dex_file) { + art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(), + dex_file, code_item, class_def_idx, method_idx, access_flags); + art::optimizer::DexCompiler dex_compiler(compiler, unit); + dex_compiler.Compile(); + return NULL; +} diff --git a/src/compiler/dex/frontend.cc b/src/compiler/dex/frontend.cc index ca751ab849..c528d8680c 100644 --- a/src/compiler/dex/frontend.cc +++ b/src/compiler/dex/frontend.cc @@ -18,7 +18,7 @@ #include "compiler/driver/compiler_driver.h" #include "compiler_internals.h" -#include "dataflow_iterator.h" +#include "dataflow_iterator-inl.h" #if defined(ART_USE_PORTABLE_COMPILER) #include "compiler/llvm/llvm_compilation_unit.h" #include "compiler/dex/portable/mir_to_gbc.h" @@ -29,6 +29,8 @@ #include "backend.h" #include "base/logging.h" + + namespace { #if !defined(ART_USE_PORTABLE_COMPILER) pthread_once_t llvm_multi_init = PTHREAD_ONCE_INIT; @@ -104,6 +106,7 @@ static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes //(1 << kDebugShowSummaryMemoryUsage) | 0; + static CompiledMethod* CompileMethod(CompilerDriver& compiler, const CompilerBackend compiler_backend, const DexFile::CodeItem* code_item, @@ -277,6 +280,8 @@ CompiledMethod* CompileOneMethod(CompilerDriver& compiler, ); } + + } // namespace art extern "C" art::CompiledMethod* diff --git a/src/compiler/dex/frontend.h b/src/compiler/dex/frontend.h index dc57a23485..69d7f7728c 100644 --- a/src/compiler/dex/frontend.h +++ b/src/compiler/dex/frontend.h @@ -20,6 +20,11 @@ #include "dex_file.h" #include "dex_instruction.h" + + + + + namespace llvm { class Module; class LLVMContext; @@ -116,4 +121,6 @@ extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver, jobject class_loader, const art::DexFile& dex_file); + + #endif // ART_SRC_COMPILER_DEX_COMPILER_H_ diff --git a/src/compiler/dex/mir_dataflow.cc b/src/compiler/dex/mir_dataflow.cc index 9f61d73d6b..3b2c1a6c5e 100644 --- a/src/compiler/dex/mir_dataflow.cc +++ b/src/compiler/dex/mir_dataflow.cc @@ -16,7 +16,7 @@ #include "compiler_internals.h" #include "local_value_numbering.h" -#include "dataflow_iterator.h" +#include "dataflow_iterator-inl.h" namespace art { @@ -70,7 +70,7 @@ const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_REF_A, // 0D MOVE_EXCEPTION vAA - DF_DA | DF_REF_A, + DF_DA | DF_REF_A | DF_NON_NULL_DST, // 0E RETURN_VOID DF_NOP, @@ -109,13 +109,13 @@ const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_A_WIDE | DF_SETS_CONST, // 1A CONST_STRING vAA, string@BBBB - DF_DA | DF_REF_A, + DF_DA | DF_REF_A | DF_NON_NULL_DST, // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB - DF_DA | DF_REF_A, + DF_DA | DF_REF_A | DF_NON_NULL_DST, // 1C CONST_CLASS vAA, type@BBBB - DF_DA | DF_REF_A, + DF_DA | DF_REF_A | DF_NON_NULL_DST, // 1D MONITOR_ENTER vAA DF_UA | DF_NULL_CHK_0 | DF_REF_A, @@ -933,11 +933,6 @@ int MIRGraph::AddNewSReg(int v_reg) SetNumSSARegs(ssa_reg + 1); ssa_base_vregs_->Insert(v_reg); ssa_subscripts_->Insert(subscript); - std::string ssa_name = GetSSAName(ssa_reg); - char* name = static_cast<char*>(arena_->NewMem(ssa_name.length() + 1, false, - ArenaAllocator::kAllocDFInfo)); - strncpy(name, ssa_name.c_str(), ssa_name.length() + 1); - ssa_strings_->Insert(name); DCHECK_EQ(ssa_base_vregs_->Size(), ssa_subscripts_->Size()); return ssa_reg; } @@ -1140,8 +1135,6 @@ void MIRGraph::CompilerInitializeSSAConversion() kGrowableArraySSAtoDalvikMap); ssa_subscripts_ = new (arena_) GrowableArray<int>(arena_, num_dalvik_reg + GetDefCount() + 128, kGrowableArraySSAtoDalvikMap); - ssa_strings_ = new (arena_) GrowableArray<char*>(arena_, num_dalvik_reg + GetDefCount() + 128, - kGrowableArraySSAtoDalvikMap); /* * Initial number of SSA registers is equal to the number of Dalvik * registers. @@ -1156,11 +1149,6 @@ void MIRGraph::CompilerInitializeSSAConversion() for (unsigned int i = 0; i < num_dalvik_reg; i++) { ssa_base_vregs_->Insert(i); ssa_subscripts_->Insert(0); - std::string ssa_name = GetSSAName(i); - char* name = static_cast<char*>(arena_->NewMem(ssa_name.length() + 1, true, - ArenaAllocator::kAllocDFInfo)); - strncpy(name, ssa_name.c_str(), ssa_name.length() + 1); - ssa_strings_->Insert(name); } /* @@ -1237,17 +1225,17 @@ bool MIRGraph::InvokeUsesMethodStar(MIR* mir) return false; } DexCompilationUnit m_unit(cu_); - // TODO: add a flag so we don't counts the stats for this twice - uint32_t dex_method_idx = mir->dalvikInsn.vB; + CompilerDriver::MethodReference target_method(cu_->dex_file, mir->dalvikInsn.vB); int vtable_idx; uintptr_t direct_code; uintptr_t direct_method; uint32_t current_offset = static_cast<uint32_t>(current_offset_); bool fast_path = - cu_->compiler_driver->ComputeInvokeInfo(dex_method_idx, current_offset, - &m_unit, type, - vtable_idx, direct_code, - direct_method) && + cu_->compiler_driver->ComputeInvokeInfo(&m_unit, current_offset, + type, target_method, + vtable_idx, + direct_code, direct_method, + false) && !(cu_->enable_debug & (1 << kDebugSlowInvokePath)); return (((type == kDirect) || (type == kStatic)) && fast_path && ((direct_code == 0) || (direct_method == 0))); diff --git a/src/compiler/dex/mir_graph.cc b/src/compiler/dex/mir_graph.cc index 6154eec6ca..11e100dc61 100644 --- a/src/compiler/dex/mir_graph.cc +++ b/src/compiler/dex/mir_graph.cc @@ -77,7 +77,6 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) cu_(cu), ssa_base_vregs_(NULL), ssa_subscripts_(NULL), - ssa_strings_(NULL), vreg_to_ssa_map_(NULL), ssa_last_defs_(NULL), is_constant_v_(NULL), @@ -1037,6 +1036,9 @@ void MIRGraph::ReplaceSpecialChars(std::string& str) std::string MIRGraph::GetSSAName(int ssa_reg) { + // TODO: This value is needed for LLVM and debugging. Currently, we compute this and then copy to + // the arena. We should be smarter and just place straight into the arena, or compute the + // value more lazily. return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); } diff --git a/src/compiler/dex/mir_graph.h b/src/compiler/dex/mir_graph.h index 882a5088d7..2b1c21fd70 100644 --- a/src/compiler/dex/mir_graph.h +++ b/src/compiler/dex/mir_graph.h @@ -452,10 +452,6 @@ class MIRGraph { return ssa_subscripts_->Get(ssa_reg); } - const char* GetSSAString(int ssa_reg) const { - return ssa_strings_->Get(ssa_reg); - } - RegLocation GetRawSrc(MIR* mir, int num) { DCHECK(num < mir->ssa_rep->num_uses); @@ -628,7 +624,6 @@ class MIRGraph { CompilationUnit* const cu_; GrowableArray<int>* ssa_base_vregs_; GrowableArray<int>* ssa_subscripts_; - GrowableArray<char*>* ssa_strings_; // Map original Dalvik virtual reg i to the current SSA name. int* vreg_to_ssa_map_; // length == method->registers_size int* ssa_last_defs_; // length == method->registers_size diff --git a/src/compiler/dex/mir_optimization.cc b/src/compiler/dex/mir_optimization.cc index 534550112a..6b8f3f0915 100644 --- a/src/compiler/dex/mir_optimization.cc +++ b/src/compiler/dex/mir_optimization.cc @@ -16,7 +16,7 @@ #include "compiler_internals.h" #include "local_value_numbering.h" -#include "dataflow_iterator.h" +#include "dataflow_iterator-inl.h" namespace art { @@ -418,6 +418,13 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) static_cast<bool*>(arena_->NewMem(sizeof(bool) * 1, false, ArenaAllocator::kAllocDFInfo)); mir->ssa_rep->fp_def[0] = if_true->ssa_rep->fp_def[0]; + // Match type of uses to def. + mir->ssa_rep->fp_use = + static_cast<bool*>(arena_->NewMem(sizeof(bool) * mir->ssa_rep->num_uses, false, + ArenaAllocator::kAllocDFInfo)); + for (int i = 0; i < mir->ssa_rep->num_uses; i++) { + mir->ssa_rep->fp_use[i] = mir->ssa_rep->fp_def[0]; + } /* * There is usually a Phi node in the join block for our two cases. If the * Phi node only contains our two cases as input, we will use the result @@ -634,8 +641,29 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) int this_reg = cu_->num_dalvik_registers - cu_->num_ins; temp_ssa_register_v_->SetBit(this_reg); } + } else if (bb->predecessors->Size() == 1) { + BasicBlock* pred_bb = bb->predecessors->Get(0); + temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v); + if (pred_bb->block_type == kDalvikByteCode) { + // Check to see if predecessor had an explicit null-check. + MIR* last_insn = pred_bb->last_mir_insn; + Instruction::Code last_opcode = last_insn->dalvikInsn.opcode; + if (last_opcode == Instruction::IF_EQZ) { + if (pred_bb->fall_through == bb) { + // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that + // it can't be null. + temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); + } + } else if (last_opcode == Instruction::IF_NEZ) { + if (pred_bb->taken == bb) { + // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be + // null. + temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); + } + } + } } else { - // Starting state is intesection of all incoming arcs + // Starting state is intersection of all incoming arcs GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors); BasicBlock* pred_bb = iter.Next(); DCHECK(pred_bb != NULL); diff --git a/src/compiler/dex/portable/mir_to_gbc.cc b/src/compiler/dex/portable/mir_to_gbc.cc index 6fccb47d9f..1f9c92a3d2 100644 --- a/src/compiler/dex/portable/mir_to_gbc.cc +++ b/src/compiler/dex/portable/mir_to_gbc.cc @@ -28,7 +28,7 @@ #include <llvm/Support/ToolOutputFile.h> #include "compiler/dex/compiler_internals.h" -#include "compiler/dex/dataflow_iterator.h" +#include "compiler/dex/dataflow_iterator-inl.h" #include "compiler/dex/frontend.h" #include "mir_to_gbc.h" @@ -1964,7 +1964,7 @@ void MirConverter::MethodMIR2Bitcode() ::llvm::Constant* imm_value = mir_graph_->reg_location_[i].wide ? irb_->getJLong(0) : irb_->getJInt(0); val = EmitConst(imm_value, mir_graph_->reg_location_[i]); - val->setName(mir_graph_->GetSSAString(i)); + val->setName(mir_graph_->GetSSAName(i)); llvm_values_.Insert(val); } else { // Recover previously-created argument values diff --git a/src/compiler/dex/quick/arm/assemble_arm.cc b/src/compiler/dex/quick/arm/assemble_arm.cc index 23a87dcec1..36038f7741 100644 --- a/src/compiler/dex/quick/arm/assemble_arm.cc +++ b/src/compiler/dex/quick/arm/assemble_arm.cc @@ -16,6 +16,7 @@ #include "arm_lir.h" #include "codegen_arm.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" namespace art { diff --git a/src/compiler/dex/quick/arm/call_arm.cc b/src/compiler/dex/quick/arm/call_arm.cc index 32d4ed680f..879065f570 100644 --- a/src/compiler/dex/quick/arm/call_arm.cc +++ b/src/compiler/dex/quick/arm/call_arm.cc @@ -18,6 +18,7 @@ #include "arm_lir.h" #include "codegen_arm.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "oat/runtime/oat_support_entrypoints.h" namespace art { @@ -561,7 +562,7 @@ void ArmMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg) int reg_card_no = AllocTemp(); LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); LoadWordDisp(rARM_SELF, Thread::CardTableOffset().Int32Value(), reg_card_base); - OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, CardTable::kCardShift); + OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); LIR* target = NewLIR0(kPseudoTargetLabel); diff --git a/src/compiler/dex/quick/arm/codegen_arm.h b/src/compiler/dex/quick/arm/codegen_arm.h index 9e409e6772..60111d1d06 100644 --- a/src/compiler/dex/quick/arm/codegen_arm.h +++ b/src/compiler/dex/quick/arm/codegen_arm.h @@ -23,143 +23,142 @@ namespace art { class ArmMir2Lir : public Mir2Lir { public: - ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); // Required for target - codegen helpers. - virtual bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src, + bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src, RegLocation rl_dest, int lit); - virtual int LoadHelper(int offset); - virtual LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg); - virtual LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi, - int s_reg); - virtual LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size); - virtual LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, - int r_dest, int r_dest_hi, OpSize size, int s_reg); - virtual LIR* LoadConstantNoClobber(int r_dest, int value); - virtual LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value); - virtual LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size); - virtual LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi); - virtual LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size); - virtual LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, - int r_src, int r_src_hi, OpSize size, int s_reg); - virtual void MarkGCCard(int val_reg, int tgt_addr_reg); + int LoadHelper(int offset); + LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg); + LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi, + int s_reg); + LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size); + LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, + int r_dest, int r_dest_hi, OpSize size, int s_reg); + LIR* LoadConstantNoClobber(int r_dest, int value); + LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value); + LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size); + LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi); + LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size); + LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, + int r_src, int r_src_hi, OpSize size, int s_reg); + void MarkGCCard(int val_reg, int tgt_addr_reg); // Required for target - register utilities. - virtual bool IsFpReg(int reg); - virtual bool SameRegType(int reg1, int reg2); - virtual int AllocTypedTemp(bool fp_hint, int reg_class); - virtual int AllocTypedTempPair(bool fp_hint, int reg_class); - virtual int S2d(int low_reg, int high_reg); - virtual int TargetReg(SpecialTargetRegister reg); - virtual RegisterInfo* GetRegInfo(int reg); - virtual RegLocation GetReturnAlt(); - virtual RegLocation GetReturnWideAlt(); - virtual RegLocation LocCReturn(); - virtual RegLocation LocCReturnDouble(); - virtual RegLocation LocCReturnFloat(); - virtual RegLocation LocCReturnWide(); - virtual uint32_t FpRegMask(); - virtual uint64_t GetRegMaskCommon(int reg); - virtual void AdjustSpillMask(); - virtual void ClobberCalleeSave(); - virtual void FlushReg(int reg); - virtual void FlushRegWide(int reg1, int reg2); - virtual void FreeCallTemps(); - virtual void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free); - virtual void LockCallTemps(); - virtual void MarkPreservedSingle(int v_reg, int reg); - virtual void CompilerInitializeRegAlloc(); + bool IsFpReg(int reg); + bool SameRegType(int reg1, int reg2); + int AllocTypedTemp(bool fp_hint, int reg_class); + int AllocTypedTempPair(bool fp_hint, int reg_class); + int S2d(int low_reg, int high_reg); + int TargetReg(SpecialTargetRegister reg); + RegisterInfo* GetRegInfo(int reg); + RegLocation GetReturnAlt(); + RegLocation GetReturnWideAlt(); + RegLocation LocCReturn(); + RegLocation LocCReturnDouble(); + RegLocation LocCReturnFloat(); + RegLocation LocCReturnWide(); + uint32_t FpRegMask(); + uint64_t GetRegMaskCommon(int reg); + void AdjustSpillMask(); + void ClobberCalleeSave(); + void FlushReg(int reg); + void FlushRegWide(int reg1, int reg2); + void FreeCallTemps(); + void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free); + void LockCallTemps(); + void MarkPreservedSingle(int v_reg, int reg); + void CompilerInitializeRegAlloc(); // Required for target - miscellaneous. - virtual AssemblerStatus AssembleInstructions(uintptr_t start_addr); - virtual void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); - virtual void SetupTargetResourceMasks(LIR* lir); - virtual const char* GetTargetInstFmt(int opcode); - virtual const char* GetTargetInstName(int opcode); - virtual std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); - virtual uint64_t GetPCUseDefEncoding(); - virtual uint64_t GetTargetInstFlags(int opcode); - virtual int GetInsnSize(LIR* lir); - virtual bool IsUnconditionalBranch(LIR* lir); + AssemblerStatus AssembleInstructions(uintptr_t start_addr); + void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); + void SetupTargetResourceMasks(LIR* lir); + const char* GetTargetInstFmt(int opcode); + const char* GetTargetInstName(int opcode); + std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); + uint64_t GetPCUseDefEncoding(); + uint64_t GetTargetInstFlags(int opcode); + int GetInsnSize(LIR* lir); + bool IsUnconditionalBranch(LIR* lir); // Required for target - Dalvik-level generators. - virtual void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2); - virtual void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, - RegLocation rl_src, int scale); - virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_dest, int scale); - virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale); - virtual void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_shift); - virtual void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2); - virtual void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2); - virtual void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2); - virtual void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); - virtual bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); - virtual bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); - virtual bool GenInlinedSqrt(CallInfo* info); - virtual void GenNegLong(RegLocation rl_dest, RegLocation rl_src); - virtual void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, + void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2); + void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, + RegLocation rl_src, int scale); + void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, + RegLocation rl_index, RegLocation rl_dest, int scale); + void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, + RegLocation rl_index, RegLocation rl_src, int scale); + void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_shift); + void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2); + void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2); + void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2); + void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); + bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); + bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); + bool GenInlinedSqrt(CallInfo* info); + void GenNegLong(RegLocation rl_dest, RegLocation rl_src); + void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, ThrowKind kind); - virtual RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); - virtual RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div); - virtual void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenDivZeroCheck(int reg_lo, int reg_hi); - virtual void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); - virtual void GenExitSequence(); - virtual void GenFillArrayData(uint32_t table_offset, RegLocation rl_src); - virtual void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); - virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); - virtual void GenSelect(BasicBlock* bb, MIR* mir); - virtual void GenMemBarrier(MemBarrierKind barrier_kind); - virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src); - virtual void GenMonitorExit(int opt_flags, RegLocation rl_src); - virtual void GenMoveException(RegLocation rl_dest); - virtual void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, + RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); + RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div); + void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenDivZeroCheck(int reg_lo, int reg_hi); + void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); + void GenExitSequence(); + void GenFillArrayData(uint32_t table_offset, RegLocation rl_src); + void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); + void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); + void GenSelect(BasicBlock* bb, MIR* mir); + void GenMemBarrier(MemBarrierKind barrier_kind); + void GenMonitorEnter(int opt_flags, RegLocation rl_src); + void GenMonitorExit(int opt_flags, RegLocation rl_src); + void GenMoveException(RegLocation rl_dest); + void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, int first_bit, int second_bit); - virtual void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); - virtual void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); - virtual void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - virtual void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - virtual void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); + void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); + void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); // Required for target - single operation generators. - virtual LIR* OpUnconditionalBranch(LIR* target); - virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target); - virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target); - virtual LIR* OpCondBranch(ConditionCode cc, LIR* target); - virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target); - virtual LIR* OpFpRegCopy(int r_dest, int r_src); - virtual LIR* OpIT(ConditionCode cond, const char* guide); - virtual LIR* OpMem(OpKind op, int rBase, int disp); - virtual LIR* OpPcRelLoad(int reg, LIR* target); - virtual LIR* OpReg(OpKind op, int r_dest_src); - virtual LIR* OpRegCopy(int r_dest, int r_src); - virtual LIR* OpRegCopyNoInsert(int r_dest, int r_src); - virtual LIR* OpRegImm(OpKind op, int r_dest_src1, int value); - virtual LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); - virtual LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); - virtual LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); - virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); - virtual LIR* OpTestSuspend(LIR* target); - virtual LIR* OpThreadMem(OpKind op, int thread_offset); - virtual LIR* OpVldm(int rBase, int count); - virtual LIR* OpVstm(int rBase, int count); - virtual void OpLea(int rBase, int reg1, int reg2, int scale, int offset); - virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi); - virtual void OpTlsCmp(int offset, int val); + LIR* OpUnconditionalBranch(LIR* target); + LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target); + LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target); + LIR* OpCondBranch(ConditionCode cc, LIR* target); + LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target); + LIR* OpFpRegCopy(int r_dest, int r_src); + LIR* OpIT(ConditionCode cond, const char* guide); + LIR* OpMem(OpKind op, int rBase, int disp); + LIR* OpPcRelLoad(int reg, LIR* target); + LIR* OpReg(OpKind op, int r_dest_src); + LIR* OpRegCopy(int r_dest, int r_src); + LIR* OpRegCopyNoInsert(int r_dest, int r_src); + LIR* OpRegImm(OpKind op, int r_dest_src1, int value); + LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); + LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); + LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); + LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); + LIR* OpTestSuspend(LIR* target); + LIR* OpThreadMem(OpKind op, int thread_offset); + LIR* OpVldm(int rBase, int count); + LIR* OpVstm(int rBase, int count); + void OpLea(int rBase, int reg1, int reg2, int scale, int offset); + void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi); + void OpTlsCmp(int offset, int val); RegLocation ArgLoc(RegLocation loc); LIR* LoadBaseDispBody(int rBase, int displacement, int r_dest, int r_dest_hi, OpSize size, diff --git a/src/compiler/dex/quick/arm/fp_arm.cc b/src/compiler/dex/quick/arm/fp_arm.cc index 4bf8738949..cd71c0798b 100644 --- a/src/compiler/dex/quick/arm/fp_arm.cc +++ b/src/compiler/dex/quick/arm/fp_arm.cc @@ -16,6 +16,7 @@ #include "arm_lir.h" #include "codegen_arm.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" namespace art { diff --git a/src/compiler/dex/quick/arm/int_arm.cc b/src/compiler/dex/quick/arm/int_arm.cc index 586a3a49b5..110e9f4320 100644 --- a/src/compiler/dex/quick/arm/int_arm.cc +++ b/src/compiler/dex/quick/arm/int_arm.cc @@ -18,6 +18,7 @@ #include "arm_lir.h" #include "codegen_arm.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "mirror/array.h" #include "oat/runtime/oat_support_entrypoints.h" diff --git a/src/compiler/dex/quick/arm/target_arm.cc b/src/compiler/dex/quick/arm/target_arm.cc index 0a05a3a431..ee127a8e17 100644 --- a/src/compiler/dex/quick/arm/target_arm.cc +++ b/src/compiler/dex/quick/arm/target_arm.cc @@ -19,6 +19,7 @@ #include "arm_lir.h" #include "codegen_arm.h" #include "compiler/dex/compiler_internals.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" namespace art { diff --git a/src/compiler/dex/quick/arm/utility_arm.cc b/src/compiler/dex/quick/arm/utility_arm.cc index c689f72436..ef0cc72a5c 100644 --- a/src/compiler/dex/quick/arm/utility_arm.cc +++ b/src/compiler/dex/quick/arm/utility_arm.cc @@ -16,7 +16,7 @@ #include "arm_lir.h" #include "codegen_arm.h" -#include "compiler/dex/quick/mir_to_lir.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" namespace art { diff --git a/src/compiler/dex/quick/codegen_util.cc b/src/compiler/dex/quick/codegen_util.cc index 517d1b5c03..ac2828c276 100644 --- a/src/compiler/dex/quick/codegen_util.cc +++ b/src/compiler/dex/quick/codegen_util.cc @@ -17,6 +17,7 @@ #include "compiler/dex/compiler_internals.h" #include "dex_file-inl.h" #include "gc_map.h" +#include "mir_to_lir-inl.h" #include "verifier/dex_gc_map.h" #include "verifier/method_verifier.h" @@ -112,81 +113,6 @@ void Mir2Lir::AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load, } /* - * Mark the corresponding bit(s). - */ -void Mir2Lir::SetupRegMask(uint64_t* mask, int reg) -{ - *mask |= GetRegMaskCommon(reg); -} - -/* - * Set up the proper fields in the resource mask - */ -void Mir2Lir::SetupResourceMasks(LIR* lir) -{ - int opcode = lir->opcode; - - if (opcode <= 0) { - lir->use_mask = lir->def_mask = 0; - return; - } - - uint64_t flags = GetTargetInstFlags(opcode); - - if (flags & NEEDS_FIXUP) { - lir->flags.pcRelFixup = true; - } - - /* Get the starting size of the instruction's template */ - lir->flags.size = GetInsnSize(lir); - - /* Set up the mask for resources that are updated */ - if (flags & (IS_LOAD | IS_STORE)) { - /* Default to heap - will catch specialized classes later */ - SetMemRefType(lir, flags & IS_LOAD, kHeapRef); - } - - /* - * Conservatively assume the branch here will call out a function that in - * turn will trash everything. - */ - if (flags & IS_BRANCH) { - lir->def_mask = lir->use_mask = ENCODE_ALL; - return; - } - - if (flags & REG_DEF0) { - SetupRegMask(&lir->def_mask, lir->operands[0]); - } - - if (flags & REG_DEF1) { - SetupRegMask(&lir->def_mask, lir->operands[1]); - } - - - if (flags & SETS_CCODES) { - lir->def_mask |= ENCODE_CCODE; - } - - if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) { - int i; - - for (i = 0; i < 4; i++) { - if (flags & (1 << (kRegUse0 + i))) { - SetupRegMask(&lir->use_mask, lir->operands[i]); - } - } - } - - if (flags & USES_CCODES) { - lir->use_mask |= ENCODE_CCODE; - } - - // Handle target-specific actions - SetupTargetResourceMasks(lir); -} - -/* * Debugging macros */ #define DUMP_RESOURCE_MASK(X) @@ -361,99 +287,6 @@ void Mir2Lir::CodegenDump() DumpMappingTable("Dex2PC_MappingTable", descriptor, name, signature, dex2pc_mapping_table_); } - -LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0, - int op1, int op2, int op3, int op4, LIR* target) -{ - LIR* insn = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR)); - insn->dalvik_offset = dalvik_offset; - insn->opcode = opcode; - insn->operands[0] = op0; - insn->operands[1] = op1; - insn->operands[2] = op2; - insn->operands[3] = op3; - insn->operands[4] = op4; - insn->target = target; - SetupResourceMasks(insn); - if ((opcode == kPseudoTargetLabel) || (opcode == kPseudoSafepointPC) || - (opcode == kPseudoExportedPC)) { - // Always make labels scheduling barriers - insn->use_mask = insn->def_mask = ENCODE_ALL; - } - return insn; -} - -/* - * The following are building blocks to construct low-level IRs with 0 - 4 - * operands. - */ -LIR* Mir2Lir::NewLIR0(int opcode) -{ - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & NO_OPERAND)) - << GetTargetInstName(opcode) << " " << opcode << " " - << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << current_dalvik_offset_; - LIR* insn = RawLIR(current_dalvik_offset_, opcode); - AppendLIR(insn); - return insn; -} - -LIR* Mir2Lir::NewLIR1(int opcode, int dest) -{ - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP)) - << GetTargetInstName(opcode) << " " << opcode << " " - << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << current_dalvik_offset_; - LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest); - AppendLIR(insn); - return insn; -} - -LIR* Mir2Lir::NewLIR2(int opcode, int dest, int src1) -{ - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP)) - << GetTargetInstName(opcode) << " " << opcode << " " - << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << current_dalvik_offset_; - LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1); - AppendLIR(insn); - return insn; -} - -LIR* Mir2Lir::NewLIR3(int opcode, int dest, int src1, int src2) -{ - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_TERTIARY_OP)) - << GetTargetInstName(opcode) << " " << opcode << " " - << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << current_dalvik_offset_; - LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1, src2); - AppendLIR(insn); - return insn; -} - -LIR* Mir2Lir::NewLIR4(int opcode, int dest, int src1, int src2, int info) -{ - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUAD_OP)) - << GetTargetInstName(opcode) << " " << opcode << " " - << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << current_dalvik_offset_; - LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1, src2, info); - AppendLIR(insn); - return insn; -} - -LIR* Mir2Lir::NewLIR5(int opcode, int dest, int src1, int src2, int info1, - int info2) -{ - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUIN_OP)) - << GetTargetInstName(opcode) << " " << opcode << " " - << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << current_dalvik_offset_; - LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1, src2, info1, info2); - AppendLIR(insn); - return insn; -} - /* * Search the existing constants in the literal pool for an exact or close match * within specified delta (greater or equal to 0). diff --git a/src/compiler/dex/quick/gen_common.cc b/src/compiler/dex/quick/gen_common.cc index 15aa904f46..7aa71cfd10 100644 --- a/src/compiler/dex/quick/gen_common.cc +++ b/src/compiler/dex/quick/gen_common.cc @@ -16,8 +16,10 @@ #include "compiler/dex/compiler_ir.h" #include "compiler/dex/compiler_internals.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "mirror/array.h" #include "oat/runtime/oat_support_entrypoints.h" +#include "verifier/method_verifier.h" namespace art { @@ -881,23 +883,81 @@ void Mir2Lir::GenThrow(RegLocation rl_src) CallRuntimeHelperRegLocation(ENTRYPOINT_OFFSET(pDeliverException), rl_src, true); } -void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, - RegLocation rl_src) -{ +// For final classes there are no sub-classes to check and so we can answer the instance-of +// question with simple comparisons. +void Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest, + RegLocation rl_src) { + RegLocation object = LoadValue(rl_src, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + int result_reg = rl_result.low_reg; + if (result_reg == object.low_reg) { + result_reg = AllocTypedTemp(false, kCoreReg); + } + LoadConstant(result_reg, 0); // assume false + LIR* null_branchover = OpCmpImmBranch(kCondEq, object.low_reg, 0, NULL); + + int check_class = AllocTypedTemp(false, kCoreReg); + int object_class = AllocTypedTemp(false, kCoreReg); + + LoadCurrMethodDirect(check_class); + if (use_declaring_class) { + LoadWordDisp(check_class, mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), + check_class); + LoadWordDisp(object.low_reg, mirror::Object::ClassOffset().Int32Value(), object_class); + } else { + LoadWordDisp(check_class, mirror::AbstractMethod::DexCacheResolvedTypesOffset().Int32Value(), + check_class); + LoadWordDisp(object.low_reg, mirror::Object::ClassOffset().Int32Value(), object_class); + int32_t offset_of_type = + mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + + (sizeof(mirror::Class*) * type_idx); + LoadWordDisp(check_class, offset_of_type, check_class); + } + + LIR* ne_branchover = NULL; + if (cu_->instruction_set == kThumb2) { + OpRegReg(kOpCmp, check_class, object_class); // Same? + OpIT(kCondEq, ""); // if-convert the test + LoadConstant(result_reg, 1); // .eq case - load true + } else { + ne_branchover = OpCmpBranch(kCondNe, check_class, object_class, NULL); + LoadConstant(result_reg, 1); // eq case - load true + } + LIR* target = NewLIR0(kPseudoTargetLabel); + null_branchover->target = target; + if (ne_branchover != NULL) { + ne_branchover->target = target; + } + FreeTemp(object_class); + FreeTemp(check_class); + if (IsTemp(result_reg)) { + OpRegCopy(rl_result.low_reg, result_reg); + FreeTemp(result_reg); + } + StoreValue(rl_dest, rl_result); +} + +void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final, + bool type_known_abstract, bool use_declaring_class, + bool can_assume_type_is_in_dex_cache, + uint32_t type_idx, RegLocation rl_dest, + RegLocation rl_src) { FlushAllRegs(); // May generate a call - use explicit registers LockCallTemps(); LoadCurrMethodDirect(TargetReg(kArg1)); // kArg1 <= current Method* int class_reg = TargetReg(kArg2); // kArg2 will hold the Class* - if (!cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, - *cu_->dex_file, - type_idx)) { + if (needs_access_check) { // Check we have access to type_idx and if not throw IllegalAccessError, // returns Class* in kArg0 CallRuntimeHelperImm(ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode), type_idx, true); OpRegCopy(class_reg, TargetReg(kRet0)); // Align usage with fast path LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref + } else if (use_declaring_class) { + LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref + LoadWordDisp(TargetReg(kArg1), + mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), class_reg); } else { // Load dex cache entry into class_reg (kArg2) LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref @@ -907,8 +967,7 @@ void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*) * type_idx); LoadWordDisp(class_reg, offset_of_type, class_reg); - if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache( - *cu_->dex_file, type_idx)) { + if (!can_assume_type_is_in_dex_cache) { // Need to test presence of type in dex cache at runtime LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL); // Not resolved @@ -924,65 +983,120 @@ void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, /* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result */ RegLocation rl_result = GetReturn(false); if (cu_->instruction_set == kMips) { - LoadConstant(rl_result.low_reg, 0); // store false result for if branch is taken + // On MIPS rArg0 != rl_result, place false in result if branch is taken. + LoadConstant(rl_result.low_reg, 0); } LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0), 0, NULL); + /* load object->klass_ */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */ - LIR* call_inst; LIR* branchover = NULL; - if (cu_->instruction_set == kThumb2) { - /* Uses conditional nullification */ - int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); - OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2)); // Same? - OpIT(kCondEq, "EE"); // if-convert the test - LoadConstant(TargetReg(kArg0), 1); // .eq case - load true - OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .ne case - arg0 <= class - call_inst = OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class) - FreeTemp(r_tgt); + if (type_known_final) { + // rl_result == ref == null == 0. + if (cu_->instruction_set == kThumb2) { + OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2)); // Same? + OpIT(kCondEq, "E"); // if-convert the test + LoadConstant(rl_result.low_reg, 1); // .eq case - load true + LoadConstant(rl_result.low_reg, 0); // .ne case - load false + } else { + LoadConstant(rl_result.low_reg, 0); // ne case - load false + branchover = OpCmpBranch(kCondNe, TargetReg(kArg1), TargetReg(kArg2), NULL); + LoadConstant(rl_result.low_reg, 1); // eq case - load true + } } else { - /* Uses branchovers */ - LoadConstant(rl_result.low_reg, 1); // assume true - branchover = OpCmpBranch(kCondEq, TargetReg(kArg1), TargetReg(kArg2), NULL); - if (cu_->instruction_set != kX86) { + if (cu_->instruction_set == kThumb2) { int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + if (!type_known_abstract) { + /* Uses conditional nullification */ + OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2)); // Same? + OpIT(kCondEq, "EE"); // if-convert the test + LoadConstant(TargetReg(kArg0), 1); // .eq case - load true + } OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .ne case - arg0 <= class - call_inst = OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class) + OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class) FreeTemp(r_tgt); } else { - OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); - call_inst = OpThreadMem(kOpBlx, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + if (!type_known_abstract) { + /* Uses branchovers */ + LoadConstant(rl_result.low_reg, 1); // assume true + branchover = OpCmpBranch(kCondEq, TargetReg(kArg1), TargetReg(kArg2), NULL); + } + if (cu_->instruction_set != kX86) { + int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .ne case - arg0 <= class + OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class) + FreeTemp(r_tgt); + } else { + OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); + OpThreadMem(kOpBlx, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + } } } - MarkSafepointPC(call_inst); + // TODO: only clobber when type isn't final? ClobberCalleeSave(); /* branch targets here */ LIR* target = NewLIR0(kPseudoTargetLabel); StoreValue(rl_dest, rl_result); branch1->target = target; - if (cu_->instruction_set != kThumb2) { + if (branchover != NULL) { branchover->target = target; } } -void Mir2Lir::GenCheckCast(uint32_t type_idx, RegLocation rl_src) +void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src) { + bool type_known_final, type_known_abstract, use_declaring_class; + bool needs_access_check = !cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, + *cu_->dex_file, + type_idx, + &type_known_final, + &type_known_abstract, + &use_declaring_class); + bool can_assume_type_is_in_dex_cache = !needs_access_check && + cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx); + + if ((use_declaring_class || can_assume_type_is_in_dex_cache) && type_known_final) { + GenInstanceofFinal(use_declaring_class, type_idx, rl_dest, rl_src); + } else { + GenInstanceofCallingHelper(needs_access_check, type_known_final, type_known_abstract, + use_declaring_class, can_assume_type_is_in_dex_cache, + type_idx, rl_dest, rl_src); + } +} + +void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_src) { + bool type_known_final, type_known_abstract, use_declaring_class; + bool needs_access_check = !cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, + *cu_->dex_file, + type_idx, + &type_known_final, + &type_known_abstract, + &use_declaring_class); + // Note: currently type_known_final is unused, as optimizing will only improve the performance + // of the exception throw path. + DexCompilationUnit* cu = mir_graph_->GetCurrentDexCompilationUnit(); + const CompilerDriver::MethodReference mr(cu->GetDexFile(), cu->GetDexMethodIndex()); + if (!needs_access_check && cu_->compiler_driver->IsSafeCast(mr, insn_idx)) { + // Verifier type analysis proved this check cast would never cause an exception. + return; + } FlushAllRegs(); // May generate a call - use explicit registers LockCallTemps(); LoadCurrMethodDirect(TargetReg(kArg1)); // kArg1 <= current Method* int class_reg = TargetReg(kArg2); // kArg2 will hold the Class* - if (!cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, - *cu_->dex_file, - type_idx)) { + if (needs_access_check) { // Check we have access to type_idx and if not throw IllegalAccessError, // returns Class* in kRet0 // InitializeTypeAndVerifyAccess(idx, method) CallRuntimeHelperImmReg(ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode), type_idx, TargetReg(kArg1), true); OpRegCopy(class_reg, TargetReg(kRet0)); // Align usage with fast path + } else if (use_declaring_class) { + LoadWordDisp(TargetReg(kArg1), + mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), class_reg); } else { // Load dex cache entry into class_reg (kArg2) LoadWordDisp(TargetReg(kArg1), @@ -991,8 +1105,7 @@ void Mir2Lir::GenCheckCast(uint32_t type_idx, RegLocation rl_src) mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*) * type_idx); LoadWordDisp(class_reg, offset_of_type, class_reg); - if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache( - *cu_->dex_file, type_idx)) { + if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) { // Need to test presence of type in dex cache at runtime LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL); // Not resolved @@ -1014,25 +1127,18 @@ void Mir2Lir::GenCheckCast(uint32_t type_idx, RegLocation rl_src) DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); /* kArg1 now contains object->klass_ */ - LIR* branch2; - if (cu_->instruction_set == kThumb2) { - int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pCheckCastFromCode)); - OpRegReg(kOpCmp, TargetReg(kArg1), class_reg); - branch2 = OpCondBranch(kCondEq, NULL); /* If eq, trivial yes */ - OpRegCopy(TargetReg(kArg0), TargetReg(kArg1)); - OpRegCopy(TargetReg(kArg1), TargetReg(kArg2)); - ClobberCalleeSave(); - LIR* call_inst = OpReg(kOpBlx, r_tgt); - MarkSafepointPC(call_inst); - FreeTemp(r_tgt); - } else { + LIR* branch2 = NULL; + if (!type_known_abstract) { branch2 = OpCmpBranch(kCondEq, TargetReg(kArg1), class_reg, NULL); - CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCheckCastFromCode), TargetReg(kArg1), TargetReg(kArg2), true); } + CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCheckCastFromCode), TargetReg(kArg1), TargetReg(kArg2), + true); /* branch target here */ LIR* target = NewLIR0(kPseudoTargetLabel); branch1->target = target; - branch2->target = target; + if (branch2 != NULL) { + branch2->target = target; + } } void Mir2Lir::GenLong3Addr(OpKind first_op, OpKind second_op, RegLocation rl_dest, diff --git a/src/compiler/dex/quick/gen_invoke.cc b/src/compiler/dex/quick/gen_invoke.cc index afcd9efb7d..4b12bb407a 100644 --- a/src/compiler/dex/quick/gen_invoke.cc +++ b/src/compiler/dex/quick/gen_invoke.cc @@ -15,9 +15,11 @@ */ #include "compiler/dex/compiler_ir.h" +#include "dex_file-inl.h" #include "invoke_type.h" #include "mirror/array.h" #include "mirror/string.h" +#include "mir_to_lir-inl.h" #include "oat/runtime/oat_support_entrypoints.h" #include "x86/codegen_x86.h" @@ -311,7 +313,8 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) * emit the next instruction in static & direct invoke sequences. */ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, - int state, uint32_t dex_idx, uint32_t unused, + int state, const CompilerDriver::MethodReference& target_method, + uint32_t unused, uintptr_t direct_code, uintptr_t direct_method, InvokeType type) { @@ -327,9 +330,11 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, if (direct_code != static_cast<unsigned int>(-1)) { cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); } else { - LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, dex_idx, 0); + CHECK_EQ(cu->dex_file, target_method.dex_file); + LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, + target_method.dex_method_index, 0); if (data_target == NULL) { - data_target = cg->AddWordData(&cg->code_literal_list_, dex_idx); + data_target = cg->AddWordData(&cg->code_literal_list_, target_method.dex_method_index); data_target->operands[1] = type; } LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kInvokeTgt), data_target); @@ -339,9 +344,11 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, if (direct_method != static_cast<unsigned int>(-1)) { cg->LoadConstant(cg->TargetReg(kArg0), direct_method); } else { - LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_, dex_idx, 0); + CHECK_EQ(cu->dex_file, target_method.dex_file); + LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_, + target_method.dex_method_index, 0); if (data_target == NULL) { - data_target = cg->AddWordData(&cg->method_literal_list_, dex_idx); + data_target = cg->AddWordData(&cg->method_literal_list_, target_method.dex_method_index); data_target->operands[1] = type; } LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target); @@ -366,9 +373,11 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, if (direct_code != static_cast<unsigned int>(-1)) { cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); } else { - LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, dex_idx, 0); + CHECK_EQ(cu->dex_file, target_method.dex_file); + LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, + target_method.dex_method_index, 0); if (data_target == NULL) { - data_target = cg->AddWordData(&cg->code_literal_list_, dex_idx); + data_target = cg->AddWordData(&cg->code_literal_list_, target_method.dex_method_index); data_target->operands[1] = type; } LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kInvokeTgt), data_target); @@ -378,8 +387,10 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, } break; case 2: // Grab target method* + CHECK_EQ(cu->dex_file, target_method.dex_file); cg->LoadWordDisp(cg->TargetReg(kArg0), - mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + dex_idx * 4, + mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + + (target_method.dex_method_index * 4), cg-> TargetReg(kArg0)); break; case 3: // Grab the code from the method* @@ -407,8 +418,9 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, * kArg1 here rather than the standard LoadArgRegs. */ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, - int state, uint32_t dex_idx, uint32_t method_idx, - uintptr_t unused, uintptr_t unused2, InvokeType unused3) + int state, const CompilerDriver::MethodReference& target_method, + uint32_t method_idx, uintptr_t unused, uintptr_t unused2, + InvokeType unused3) { Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); /* @@ -455,7 +467,8 @@ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, * which will locate the target and continue on via a tail call. */ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, - uint32_t dex_idx, uint32_t unused, uintptr_t unused2, + const CompilerDriver::MethodReference& target_method, + uint32_t unused, uintptr_t unused2, uintptr_t direct_method, InvokeType unused4) { Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); @@ -476,9 +489,12 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, if (direct_method != static_cast<unsigned int>(-1)) { cg->LoadConstant(cg->TargetReg(kArg0), direct_method); } else { - LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_, dex_idx, 0); + CHECK_EQ(cu->dex_file, target_method.dex_file); + LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_, + target_method.dex_method_index, 0); if (data_target == NULL) { - data_target = cg->AddWordData(&cg->method_literal_list_, dex_idx); + data_target = cg->AddWordData(&cg->method_literal_list_, + target_method.dex_method_index); data_target->operands[1] = kInterface; } LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target); @@ -505,8 +521,10 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, cg->TargetReg(kArg0)); break; case 2: // Grab target method* [set/use kArg0] + CHECK_EQ(cu->dex_file, target_method.dex_file); cg->LoadWordDisp(cg->TargetReg(kArg0), - mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + dex_idx * 4, + mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + + (target_method.dex_method_index * 4), cg->TargetReg(kArg0)); break; default: @@ -517,7 +535,8 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, } static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, int trampoline, - int state, uint32_t dex_idx, uint32_t method_idx) + int state, const CompilerDriver::MethodReference& target_method, + uint32_t method_idx) { Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); /* @@ -530,58 +549,66 @@ static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, int trampoline, cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline, cg->TargetReg(kInvokeTgt)); } // Load kArg0 with method index - cg->LoadConstant(cg->TargetReg(kArg0), dex_idx); + CHECK_EQ(cu->dex_file, target_method.dex_file); + cg->LoadConstant(cg->TargetReg(kArg0), target_method.dex_method_index); return 1; } return -1; } static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info, - int state, uint32_t dex_idx, uint32_t method_idx, + int state, + const CompilerDriver::MethodReference& target_method, + uint32_t method_idx, uintptr_t unused, uintptr_t unused2, - InvokeType unused3) + InvokeType unused3) { int trampoline = ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck); - return NextInvokeInsnSP(cu, info, trampoline, state, dex_idx, 0); + return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, - uint32_t dex_idx, uint32_t method_idx, uintptr_t unused, + const CompilerDriver::MethodReference& target_method, + uint32_t method_idx, uintptr_t unused, uintptr_t unused2, InvokeType unused3) { int trampoline = ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck); - return NextInvokeInsnSP(cu, info, trampoline, state, dex_idx, 0); + return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, - uint32_t dex_idx, uint32_t method_idx, uintptr_t unused, - uintptr_t unused2, InvokeType unused3) + const CompilerDriver::MethodReference& target_method, + uint32_t method_idx, uintptr_t unused, + uintptr_t unused2, InvokeType unused3) { int trampoline = ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck); - return NextInvokeInsnSP(cu, info, trampoline, state, dex_idx, 0); + return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, - uint32_t dex_idx, uint32_t method_idx, uintptr_t unused, + const CompilerDriver::MethodReference& target_method, + uint32_t method_idx, uintptr_t unused, uintptr_t unused2, InvokeType unused3) { int trampoline = ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck); - return NextInvokeInsnSP(cu, info, trampoline, state, dex_idx, 0); + return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu, CallInfo* info, int state, - uint32_t dex_idx, uint32_t unused, - uintptr_t unused2, uintptr_t unused3, - InvokeType unused4) + const CompilerDriver::MethodReference& target_method, + uint32_t unused, + uintptr_t unused2, uintptr_t unused3, + InvokeType unused4) { int trampoline = ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); - return NextInvokeInsnSP(cu, info, trampoline, state, dex_idx, 0); + return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, - NextCallInsn next_call_insn, uint32_t dex_idx, - uint32_t method_idx, uintptr_t direct_code, + NextCallInsn next_call_insn, + const CompilerDriver::MethodReference& target_method, + uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this) { int last_arg_reg = TargetReg(kArg3); @@ -605,8 +632,8 @@ int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, } LoadValueDirectFixed(rl_arg, next_reg); } - call_state = next_call_insn(cu_, info, call_state, dex_idx, method_idx, - direct_code, direct_method, type); + call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, + direct_code, direct_method, type); } return call_state; } @@ -620,7 +647,8 @@ int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, */ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel, NextCallInsn next_call_insn, - uint32_t dex_idx, uint32_t method_idx, uintptr_t direct_code, + const CompilerDriver::MethodReference& target_method, + uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this) { RegLocation rl_arg; @@ -629,8 +657,8 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, if (info->num_arg_words == 0) return call_state; - call_state = next_call_insn(cu_, info, call_state, dex_idx, method_idx, - direct_code, direct_method, type); + call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, + direct_code, direct_method, type); DCHECK_LE(info->num_arg_words, 5); if (info->num_arg_words > 3) { @@ -650,13 +678,13 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, // kArg2 & rArg3 can safely be used here reg = TargetReg(kArg3); LoadWordDisp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg); - call_state = next_call_insn(cu_, info, call_state, dex_idx, - method_idx, direct_code, direct_method, type); + call_state = next_call_insn(cu_, info, call_state, target_method, + vtable_idx, direct_code, direct_method, type); } StoreBaseDisp(TargetReg(kSp), (next_use + 1) * 4, reg, kWord); StoreBaseDisp(TargetReg(kSp), 16 /* (3+1)*4 */, reg, kWord); - call_state = next_call_insn(cu_, info, call_state, dex_idx, method_idx, - direct_code, direct_method, type); + call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, + direct_code, direct_method, type); next_use++; } // Loop through the rest @@ -676,8 +704,8 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, } else { LoadValueDirectFixed(rl_arg, low_reg); } - call_state = next_call_insn(cu_, info, call_state, dex_idx, - method_idx, direct_code, direct_method, type); + call_state = next_call_insn(cu_, info, call_state, target_method, + vtable_idx, direct_code, direct_method, type); } int outs_offset = (next_use + 1) * 4; if (rl_arg.wide) { @@ -687,14 +715,14 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, StoreWordDisp(TargetReg(kSp), outs_offset, low_reg); next_use++; } - call_state = next_call_insn(cu_, info, call_state, dex_idx, method_idx, + call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); } } call_state = LoadArgRegs(info, call_state, next_call_insn, - dex_idx, method_idx, direct_code, direct_method, - type, skip_this); + target_method, vtable_idx, direct_code, direct_method, + type, skip_this); if (pcrLabel) { *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags); @@ -718,15 +746,16 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, * */ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, - LIR** pcrLabel, NextCallInsn next_call_insn, uint32_t dex_idx, - uint32_t method_idx, uintptr_t direct_code, uintptr_t direct_method, + LIR** pcrLabel, NextCallInsn next_call_insn, + const CompilerDriver::MethodReference& target_method, + uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this) { // If we can treat it as non-range (Jumbo ops will use range form) if (info->num_arg_words <= 5) return GenDalvikArgsNoRange(info, call_state, pcrLabel, - next_call_insn, dex_idx, method_idx, + next_call_insn, target_method, vtable_idx, direct_code, direct_method, type, skip_this); /* * First load the non-register arguments. Both forms expect all @@ -772,31 +801,31 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, } else { // Use vldm/vstm pair using kArg3 as a temp int regs_left = std::min(info->num_arg_words - 3, 16); - call_state = next_call_insn(cu_, info, call_state, dex_idx, method_idx, + call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), start_offset); LIR* ld = OpVldm(TargetReg(kArg3), regs_left); //TUNING: loosen barrier ld->def_mask = ENCODE_ALL; SetMemRefType(ld, true /* is_load */, kDalvikReg); - call_state = next_call_insn(cu_, info, call_state, dex_idx, method_idx, + call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), 4 /* Method* */ + (3 * 4)); - call_state = next_call_insn(cu_, info, call_state, dex_idx, method_idx, + call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); LIR* st = OpVstm(TargetReg(kArg3), regs_left); SetMemRefType(st, false /* is_load */, kDalvikReg); st->def_mask = ENCODE_ALL; - call_state = next_call_insn(cu_, info, call_state, dex_idx, method_idx, + call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); } } call_state = LoadArgRegs(info, call_state, next_call_insn, - dex_idx, method_idx, direct_code, direct_method, - type, skip_this); + target_method, vtable_idx, direct_code, direct_method, + type, skip_this); - call_state = next_call_insn(cu_, info, call_state, dex_idx, method_idx, + call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); if (pcrLabel) { *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags); @@ -1150,6 +1179,10 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, // TODO - add Mips implementation return false; } + if (cu_->instruction_set == kX86 && is_object) { + // TODO: fix X86, it exhausts registers for card marking. + return false; + } // Unused - RegLocation rl_src_unsafe = info->args[0]; RegLocation rl_src_obj = info->args[1]; // Object RegLocation rl_src_offset = info->args[2]; // long low @@ -1193,20 +1226,27 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) * method. By doing this during basic block construction, we can also * take advantage of/generate new useful dataflow info. */ - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method.find(" java.lang") != std::string::npos) { + StringPiece tgt_methods_declaring_class( + cu_->dex_file->GetMethodDeclaringClassDescriptor(cu_->dex_file->GetMethodId(info->index))); + if (tgt_methods_declaring_class.starts_with("Ljava/lang/Double;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") { return GenInlinedDoubleCvt(info); } if (tgt_method == "double java.lang.Double.longBitsToDouble(long)") { return GenInlinedDoubleCvt(info); } + } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Float;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "int java.lang.Float.float_to_raw_int_bits(float)") { return GenInlinedFloatCvt(info); } if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") { return GenInlinedFloatCvt(info); } + } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Math;") || + tgt_methods_declaring_class.starts_with("Ljava/lang/StrictMath;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "int java.lang.Math.abs(int)" || tgt_method == "int java.lang.StrictMath.abs(int)") { return GenInlinedAbsInt(info); @@ -1227,6 +1267,8 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) tgt_method == "double java.lang.StrictMath.sqrt(double)") { return GenInlinedSqrt(info); } + } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/String;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "char java.lang.String.charAt(int)") { return GenInlinedCharAt(info); } @@ -1245,10 +1287,13 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) if (tgt_method == "int java.lang.String.length()") { return GenInlinedStringIsEmptyOrLength(info, false /* is_empty */); } + } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Thread;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") { return GenInlinedCurrentThread(info); } - } else if (tgt_method.find(" sun.misc.Unsafe") != std::string::npos) { + } else if (tgt_methods_declaring_class.starts_with("Lsun/misc/Unsafe;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") { return GenInlinedCas32(info, false); } @@ -1327,20 +1372,24 @@ void Mir2Lir::GenInvoke(CallInfo* info) // Explicit register usage LockCallTemps(); - uint32_t dex_method_idx = info->index; + DexCompilationUnit* cUnit = mir_graph_->GetCurrentDexCompilationUnit(); + CompilerDriver::MethodReference target_method(cUnit->GetDexFile(), info->index); int vtable_idx; uintptr_t direct_code; uintptr_t direct_method; bool skip_this; - bool fast_path = cu_->compiler_driver->ComputeInvokeInfo( - dex_method_idx, current_dalvik_offset_, mir_graph_->GetCurrentDexCompilationUnit(), info->type, vtable_idx, - direct_code, direct_method) && !SLOW_INVOKE_PATH; + bool fast_path = + cu_->compiler_driver->ComputeInvokeInfo(mir_graph_->GetCurrentDexCompilationUnit(), + current_dalvik_offset_, + info->type, target_method, + vtable_idx, + direct_code, direct_method, + true) && !SLOW_INVOKE_PATH; if (info->type == kInterface) { if (fast_path) { p_null_ck = &null_ck; } - next_call_insn = fast_path ? NextInterfaceCallInsn - : NextInterfaceCallInsnWithAccessCheck; + next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck; skip_this = false; } else if (info->type == kDirect) { if (fast_path) { @@ -1362,20 +1411,20 @@ void Mir2Lir::GenInvoke(CallInfo* info) } if (!info->is_range) { call_state = GenDalvikArgsNoRange(info, call_state, p_null_ck, - next_call_insn, dex_method_idx, - vtable_idx, direct_code, direct_method, - original_type, skip_this); + next_call_insn, target_method, + vtable_idx, direct_code, direct_method, + original_type, skip_this); } else { call_state = GenDalvikArgsRange(info, call_state, p_null_ck, - next_call_insn, dex_method_idx, vtable_idx, - direct_code, direct_method, original_type, - skip_this); + next_call_insn, target_method, vtable_idx, + direct_code, direct_method, original_type, + skip_this); } // Finish up any of the call sequence not interleaved in arg loading while (call_state >= 0) { - call_state = next_call_insn(cu_, info, call_state, dex_method_idx, - vtable_idx, direct_code, direct_method, - original_type); + call_state = next_call_insn(cu_, info, call_state, target_method, + vtable_idx, direct_code, direct_method, + original_type); } LIR* call_inst; if (cu_->instruction_set != kX86) { diff --git a/src/compiler/dex/quick/gen_loadstore.cc b/src/compiler/dex/quick/gen_loadstore.cc index 1cebd31d19..085f7f518c 100644 --- a/src/compiler/dex/quick/gen_loadstore.cc +++ b/src/compiler/dex/quick/gen_loadstore.cc @@ -16,6 +16,7 @@ #include "compiler/dex/compiler_ir.h" #include "compiler/dex/compiler_internals.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "invoke_type.h" namespace art { diff --git a/src/compiler/dex/quick/mips/assemble_mips.cc b/src/compiler/dex/quick/mips/assemble_mips.cc index 5223a0ecfd..002a23e5ae 100644 --- a/src/compiler/dex/quick/mips/assemble_mips.cc +++ b/src/compiler/dex/quick/mips/assemble_mips.cc @@ -15,6 +15,7 @@ */ #include "codegen_mips.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "mips_lir.h" namespace art { diff --git a/src/compiler/dex/quick/mips/call_mips.cc b/src/compiler/dex/quick/mips/call_mips.cc index b53d1e35a5..ddaf08156a 100644 --- a/src/compiler/dex/quick/mips/call_mips.cc +++ b/src/compiler/dex/quick/mips/call_mips.cc @@ -17,6 +17,7 @@ /* This file contains codegen for the Mips ISA */ #include "codegen_mips.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "mips_lir.h" #include "oat/runtime/oat_support_entrypoints.h" @@ -319,7 +320,7 @@ void MipsMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg) int reg_card_no = AllocTemp(); LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); LoadWordDisp(rMIPS_SELF, Thread::CardTableOffset().Int32Value(), reg_card_base); - OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, CardTable::kCardShift); + OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); LIR* target = NewLIR0(kPseudoTargetLabel); diff --git a/src/compiler/dex/quick/mips/codegen_mips.h b/src/compiler/dex/quick/mips/codegen_mips.h index db262a8e96..9fa8f779fe 100644 --- a/src/compiler/dex/quick/mips/codegen_mips.h +++ b/src/compiler/dex/quick/mips/codegen_mips.h @@ -28,139 +28,139 @@ class MipsMir2Lir : public Mir2Lir { MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); // Required for target - codegen utilities. - virtual bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src, + bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src, RegLocation rl_dest, int lit); - virtual int LoadHelper(int offset); - virtual LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg); - virtual LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi, + int LoadHelper(int offset); + LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg); + LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi, int s_reg); - virtual LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size); - virtual LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, + LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size); + LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, int r_dest, int r_dest_hi, OpSize size, int s_reg); - virtual LIR* LoadConstantNoClobber(int r_dest, int value); - virtual LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value); - virtual LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size); - virtual LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi); - virtual LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size); - virtual LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, + LIR* LoadConstantNoClobber(int r_dest, int value); + LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value); + LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size); + LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi); + LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size); + LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, int r_src, int r_src_hi, OpSize size, int s_reg); - virtual void MarkGCCard(int val_reg, int tgt_addr_reg); + void MarkGCCard(int val_reg, int tgt_addr_reg); // Required for target - register utilities. - virtual bool IsFpReg(int reg); - virtual bool SameRegType(int reg1, int reg2); - virtual int AllocTypedTemp(bool fp_hint, int reg_class); - virtual int AllocTypedTempPair(bool fp_hint, int reg_class); - virtual int S2d(int low_reg, int high_reg); - virtual int TargetReg(SpecialTargetRegister reg); - virtual RegisterInfo* GetRegInfo(int reg); - virtual RegLocation GetReturnAlt(); - virtual RegLocation GetReturnWideAlt(); - virtual RegLocation LocCReturn(); - virtual RegLocation LocCReturnDouble(); - virtual RegLocation LocCReturnFloat(); - virtual RegLocation LocCReturnWide(); - virtual uint32_t FpRegMask(); - virtual uint64_t GetRegMaskCommon(int reg); - virtual void AdjustSpillMask(); - virtual void ClobberCalleeSave(); - virtual void FlushReg(int reg); - virtual void FlushRegWide(int reg1, int reg2); - virtual void FreeCallTemps(); - virtual void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free); - virtual void LockCallTemps(); - virtual void MarkPreservedSingle(int v_reg, int reg); - virtual void CompilerInitializeRegAlloc(); + bool IsFpReg(int reg); + bool SameRegType(int reg1, int reg2); + int AllocTypedTemp(bool fp_hint, int reg_class); + int AllocTypedTempPair(bool fp_hint, int reg_class); + int S2d(int low_reg, int high_reg); + int TargetReg(SpecialTargetRegister reg); + RegisterInfo* GetRegInfo(int reg); + RegLocation GetReturnAlt(); + RegLocation GetReturnWideAlt(); + RegLocation LocCReturn(); + RegLocation LocCReturnDouble(); + RegLocation LocCReturnFloat(); + RegLocation LocCReturnWide(); + uint32_t FpRegMask(); + uint64_t GetRegMaskCommon(int reg); + void AdjustSpillMask(); + void ClobberCalleeSave(); + void FlushReg(int reg); + void FlushRegWide(int reg1, int reg2); + void FreeCallTemps(); + void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free); + void LockCallTemps(); + void MarkPreservedSingle(int v_reg, int reg); + void CompilerInitializeRegAlloc(); // Required for target - miscellaneous. - virtual AssemblerStatus AssembleInstructions(uintptr_t start_addr); - virtual void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); - virtual void SetupTargetResourceMasks(LIR* lir); - virtual const char* GetTargetInstFmt(int opcode); - virtual const char* GetTargetInstName(int opcode); - virtual std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); - virtual uint64_t GetPCUseDefEncoding(); - virtual uint64_t GetTargetInstFlags(int opcode); - virtual int GetInsnSize(LIR* lir); - virtual bool IsUnconditionalBranch(LIR* lir); + AssemblerStatus AssembleInstructions(uintptr_t start_addr); + void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); + void SetupTargetResourceMasks(LIR* lir); + const char* GetTargetInstFmt(int opcode); + const char* GetTargetInstName(int opcode); + std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); + uint64_t GetPCUseDefEncoding(); + uint64_t GetTargetInstFlags(int opcode); + int GetInsnSize(LIR* lir); + bool IsUnconditionalBranch(LIR* lir); // Required for target - Dalvik-level generators. - virtual void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, + void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, RegLocation rl_src, int scale); - virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, + void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_dest, int scale); - virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, + void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_src, int scale); - virtual void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift); - virtual void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, + void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, + void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); - virtual bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); - virtual bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); - virtual bool GenInlinedSqrt(CallInfo* info); - virtual void GenNegLong(RegLocation rl_dest, RegLocation rl_src); - virtual void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, + void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); + bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); + bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); + bool GenInlinedSqrt(CallInfo* info); + void GenNegLong(RegLocation rl_dest, RegLocation rl_src); + void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, ThrowKind kind); - virtual RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); - virtual RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div); - virtual void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenDivZeroCheck(int reg_lo, int reg_hi); - virtual void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); - virtual void GenExitSequence(); - virtual void GenFillArrayData(uint32_t table_offset, RegLocation rl_src); - virtual void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); - virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); - virtual void GenSelect(BasicBlock* bb, MIR* mir); - virtual void GenMemBarrier(MemBarrierKind barrier_kind); - virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src); - virtual void GenMonitorExit(int opt_flags, RegLocation rl_src); - virtual void GenMoveException(RegLocation rl_dest); - virtual void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, + RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); + RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div); + void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenDivZeroCheck(int reg_lo, int reg_hi); + void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); + void GenExitSequence(); + void GenFillArrayData(uint32_t table_offset, RegLocation rl_src); + void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); + void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); + void GenSelect(BasicBlock* bb, MIR* mir); + void GenMemBarrier(MemBarrierKind barrier_kind); + void GenMonitorEnter(int opt_flags, RegLocation rl_src); + void GenMonitorExit(int opt_flags, RegLocation rl_src); + void GenMoveException(RegLocation rl_dest); + void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, int first_bit, int second_bit); - virtual void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); - virtual void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); - virtual void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - virtual void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - virtual void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); + void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); + void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); // Required for target - single operation generators. - virtual LIR* OpUnconditionalBranch(LIR* target); - virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target); - virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target); - virtual LIR* OpCondBranch(ConditionCode cc, LIR* target); - virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target); - virtual LIR* OpFpRegCopy(int r_dest, int r_src); - virtual LIR* OpIT(ConditionCode cond, const char* guide); - virtual LIR* OpMem(OpKind op, int rBase, int disp); - virtual LIR* OpPcRelLoad(int reg, LIR* target); - virtual LIR* OpReg(OpKind op, int r_dest_src); - virtual LIR* OpRegCopy(int r_dest, int r_src); - virtual LIR* OpRegCopyNoInsert(int r_dest, int r_src); - virtual LIR* OpRegImm(OpKind op, int r_dest_src1, int value); - virtual LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); - virtual LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); - virtual LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); - virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); - virtual LIR* OpTestSuspend(LIR* target); - virtual LIR* OpThreadMem(OpKind op, int thread_offset); - virtual LIR* OpVldm(int rBase, int count); - virtual LIR* OpVstm(int rBase, int count); - virtual void OpLea(int rBase, int reg1, int reg2, int scale, int offset); - virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi); - virtual void OpTlsCmp(int offset, int val); + LIR* OpUnconditionalBranch(LIR* target); + LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target); + LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target); + LIR* OpCondBranch(ConditionCode cc, LIR* target); + LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target); + LIR* OpFpRegCopy(int r_dest, int r_src); + LIR* OpIT(ConditionCode cond, const char* guide); + LIR* OpMem(OpKind op, int rBase, int disp); + LIR* OpPcRelLoad(int reg, LIR* target); + LIR* OpReg(OpKind op, int r_dest_src); + LIR* OpRegCopy(int r_dest, int r_src); + LIR* OpRegCopyNoInsert(int r_dest, int r_src); + LIR* OpRegImm(OpKind op, int r_dest_src1, int value); + LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); + LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); + LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); + LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); + LIR* OpTestSuspend(LIR* target); + LIR* OpThreadMem(OpKind op, int thread_offset); + LIR* OpVldm(int rBase, int count); + LIR* OpVstm(int rBase, int count); + void OpLea(int rBase, int reg1, int reg2, int scale, int offset); + void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi); + void OpTlsCmp(int offset, int val); LIR* LoadBaseDispBody(int rBase, int displacement, int r_dest, int r_dest_hi, OpSize size, int s_reg); diff --git a/src/compiler/dex/quick/mips/fp_mips.cc b/src/compiler/dex/quick/mips/fp_mips.cc index 5ddec00e80..f384da1a5b 100644 --- a/src/compiler/dex/quick/mips/fp_mips.cc +++ b/src/compiler/dex/quick/mips/fp_mips.cc @@ -16,6 +16,7 @@ #include "codegen_mips.h" #include "mips_lir.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "oat/runtime/oat_support_entrypoints.h" namespace art { diff --git a/src/compiler/dex/quick/mips/int_mips.cc b/src/compiler/dex/quick/mips/int_mips.cc index fbff397bd9..fe9e83f879 100644 --- a/src/compiler/dex/quick/mips/int_mips.cc +++ b/src/compiler/dex/quick/mips/int_mips.cc @@ -17,6 +17,7 @@ /* This file contains codegen for the Mips ISA */ #include "codegen_mips.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "mips_lir.h" #include "mirror/array.h" #include "oat/runtime/oat_support_entrypoints.h" diff --git a/src/compiler/dex/quick/mips/target_mips.cc b/src/compiler/dex/quick/mips/target_mips.cc index 46a625e83f..356104c86c 100644 --- a/src/compiler/dex/quick/mips/target_mips.cc +++ b/src/compiler/dex/quick/mips/target_mips.cc @@ -16,6 +16,7 @@ #include "codegen_mips.h" #include "compiler/dex/compiler_internals.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "mips_lir.h" #include <string> diff --git a/src/compiler/dex/quick/mips/utility_mips.cc b/src/compiler/dex/quick/mips/utility_mips.cc index 5f9f8c58a7..257b0f6cb2 100644 --- a/src/compiler/dex/quick/mips/utility_mips.cc +++ b/src/compiler/dex/quick/mips/utility_mips.cc @@ -15,6 +15,7 @@ */ #include "codegen_mips.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "mips_lir.h" namespace art { diff --git a/src/compiler/dex/quick/mir_to_lir-inl.h b/src/compiler/dex/quick/mir_to_lir-inl.h new file mode 100644 index 0000000000..f7546924c8 --- /dev/null +++ b/src/compiler/dex/quick/mir_to_lir-inl.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_INL_H_ +#define ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_INL_H_ + +#include "mir_to_lir.h" + +#include "compiler/dex/compiler_internals.h" + +namespace art { + +/* Mark a temp register as dead. Does not affect allocation state. */ +inline void Mir2Lir::ClobberBody(RegisterInfo* p) { + if (p->is_temp) { + DCHECK(!(p->live && p->dirty)) << "Live & dirty temp in clobber"; + p->live = false; + p->s_reg = INVALID_SREG; + p->def_start = NULL; + p->def_end = NULL; + if (p->pair) { + p->pair = false; + Clobber(p->partner); + } + } +} + +inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0, + int op1, int op2, int op3, int op4, LIR* target) { + LIR* insn = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR)); + insn->dalvik_offset = dalvik_offset; + insn->opcode = opcode; + insn->operands[0] = op0; + insn->operands[1] = op1; + insn->operands[2] = op2; + insn->operands[3] = op3; + insn->operands[4] = op4; + insn->target = target; + SetupResourceMasks(insn); + if ((opcode == kPseudoTargetLabel) || (opcode == kPseudoSafepointPC) || + (opcode == kPseudoExportedPC)) { + // Always make labels scheduling barriers + insn->use_mask = insn->def_mask = ENCODE_ALL; + } + return insn; +} + +/* + * The following are building blocks to construct low-level IRs with 0 - 4 + * operands. + */ +inline LIR* Mir2Lir::NewLIR0(int opcode) { + DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & NO_OPERAND)) + << GetTargetInstName(opcode) << " " << opcode << " " + << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " + << current_dalvik_offset_; + LIR* insn = RawLIR(current_dalvik_offset_, opcode); + AppendLIR(insn); + return insn; +} + +inline LIR* Mir2Lir::NewLIR1(int opcode, int dest) { + DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP)) + << GetTargetInstName(opcode) << " " << opcode << " " + << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " + << current_dalvik_offset_; + LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest); + AppendLIR(insn); + return insn; +} + +inline LIR* Mir2Lir::NewLIR2(int opcode, int dest, int src1) { + DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP)) + << GetTargetInstName(opcode) << " " << opcode << " " + << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " + << current_dalvik_offset_; + LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1); + AppendLIR(insn); + return insn; +} + +inline LIR* Mir2Lir::NewLIR3(int opcode, int dest, int src1, int src2) { + DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_TERTIARY_OP)) + << GetTargetInstName(opcode) << " " << opcode << " " + << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " + << current_dalvik_offset_; + LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1, src2); + AppendLIR(insn); + return insn; +} + +inline LIR* Mir2Lir::NewLIR4(int opcode, int dest, int src1, int src2, int info) { + DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUAD_OP)) + << GetTargetInstName(opcode) << " " << opcode << " " + << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " + << current_dalvik_offset_; + LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1, src2, info); + AppendLIR(insn); + return insn; +} + +inline LIR* Mir2Lir::NewLIR5(int opcode, int dest, int src1, int src2, int info1, + int info2) { + DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUIN_OP)) + << GetTargetInstName(opcode) << " " << opcode << " " + << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " + << current_dalvik_offset_; + LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1, src2, info1, info2); + AppendLIR(insn); + return insn; +} + +/* + * Mark the corresponding bit(s). + */ +inline void Mir2Lir::SetupRegMask(uint64_t* mask, int reg) { + *mask |= GetRegMaskCommon(reg); +} + +/* + * Set up the proper fields in the resource mask + */ +inline void Mir2Lir::SetupResourceMasks(LIR* lir) { + int opcode = lir->opcode; + + if (opcode <= 0) { + lir->use_mask = lir->def_mask = 0; + return; + } + + uint64_t flags = GetTargetInstFlags(opcode); + + if (flags & NEEDS_FIXUP) { + lir->flags.pcRelFixup = true; + } + + /* Get the starting size of the instruction's template */ + lir->flags.size = GetInsnSize(lir); + + /* Set up the mask for resources that are updated */ + if (flags & (IS_LOAD | IS_STORE)) { + /* Default to heap - will catch specialized classes later */ + SetMemRefType(lir, flags & IS_LOAD, kHeapRef); + } + + /* + * Conservatively assume the branch here will call out a function that in + * turn will trash everything. + */ + if (flags & IS_BRANCH) { + lir->def_mask = lir->use_mask = ENCODE_ALL; + return; + } + + if (flags & REG_DEF0) { + SetupRegMask(&lir->def_mask, lir->operands[0]); + } + + if (flags & REG_DEF1) { + SetupRegMask(&lir->def_mask, lir->operands[1]); + } + + + if (flags & SETS_CCODES) { + lir->def_mask |= ENCODE_CCODE; + } + + if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) { + int i; + + for (i = 0; i < 4; i++) { + if (flags & (1 << (kRegUse0 + i))) { + SetupRegMask(&lir->use_mask, lir->operands[i]); + } + } + } + + if (flags & USES_CCODES) { + lir->use_mask |= ENCODE_CCODE; + } + + // Handle target-specific actions + SetupTargetResourceMasks(lir); +} + +} // namespace art + +#endif // ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_INL_H_ diff --git a/src/compiler/dex/quick/mir_to_lir.cc b/src/compiler/dex/quick/mir_to_lir.cc index 481078d7d5..754aae42dd 100644 --- a/src/compiler/dex/quick/mir_to_lir.cc +++ b/src/compiler/dex/quick/mir_to_lir.cc @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "object_utils.h" - #include "compiler/dex/compiler_internals.h" -#include "compiler/dex/dataflow_iterator.h" +#include "compiler/dex/dataflow_iterator-inl.h" +#include "mir_to_lir-inl.h" +#include "object_utils.h" namespace art { @@ -184,10 +184,10 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list GenMonitorExit(opt_flags, rl_src[0]); break; - case Instruction::CHECK_CAST: - GenCheckCast(vB, rl_src[0]); + case Instruction::CHECK_CAST: { + GenCheckCast(mir->offset, vB, rl_src[0]); break; - + } case Instruction::INSTANCE_OF: GenInstanceof(vC, rl_dest, rl_src[0]); break; diff --git a/src/compiler/dex/quick/mir_to_lir.h b/src/compiler/dex/quick/mir_to_lir.h index 21a0aacc13..9eb4524d0d 100644 --- a/src/compiler/dex/quick/mir_to_lir.h +++ b/src/compiler/dex/quick/mir_to_lir.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_SRC_COMPILER_DEX_QUICK_CODEGEN_H_ -#define ART_SRC_COMPILER_DEX_QUICK_CODEGEN_H_ +#ifndef ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_H_ +#define ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_H_ #include "invoke_type.h" #include "compiled_method.h" @@ -24,6 +24,7 @@ #include "compiler/dex/backend.h" #include "compiler/dex/growable_array.h" #include "compiler/dex/arena_allocator.h" +#include "compiler/driver/compiler_driver.h" #include "safe_map.h" namespace art { @@ -98,7 +99,8 @@ struct RegisterInfo; class MIRGraph; class Mir2Lir; -typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int, uint32_t dex_idx, +typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int, + const CompilerDriver::MethodReference& target_method, uint32_t method_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type); @@ -312,8 +314,10 @@ class Mir2Lir : public Backend { void DumpRegPool(RegisterInfo* p, int num_regs); void DumpCoreRegPool(); void DumpFpRegPool(); - void ClobberBody(RegisterInfo* p); - void Clobber(int reg); + /* Mark a temp register as dead. Does not affect allocation state. */ + void Clobber(int reg) { + ClobberBody(GetRegInfo(reg)); + } void ClobberSRegBody(RegisterInfo* p, int num_regs, int s_reg); void ClobberSReg(int s_reg); int SRegToPMap(int s_reg); @@ -337,7 +341,6 @@ class Mir2Lir : public Backend { RegisterInfo* IsPromoted(int reg); bool IsDirty(int reg); void LockTemp(int reg); - void ResetDefBody(RegisterInfo* p); void ResetDef(int reg); void NullifyRange(LIR *start, LIR *finish, int s_reg1, int s_reg2); void MarkDef(RegLocation rl, LIR *start, LIR *finish); @@ -410,7 +413,8 @@ class Mir2Lir : public Backend { void GenThrow(RegLocation rl_src); void GenInstanceof(uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src); - void GenCheckCast(uint32_t type_idx, RegLocation rl_src); + void GenCheckCast(uint32_t insn_idx, uint32_t type_idx, + RegLocation rl_src); void GenLong3Addr(OpKind first_op, OpKind second_op, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, @@ -462,11 +466,15 @@ class Mir2Lir : public Backend { void GenInvoke(CallInfo* info); void FlushIns(RegLocation* ArgLocs, RegLocation rl_method); int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel, - NextCallInsn next_call_insn, uint32_t dex_idx, uint32_t method_idx, + NextCallInsn next_call_insn, + const CompilerDriver::MethodReference& target_method, + uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this); int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel, - NextCallInsn next_call_insn, uint32_t dex_idx, uint32_t method_idx, + NextCallInsn next_call_insn, + const CompilerDriver::MethodReference& target_method, + uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this); RegLocation InlineTarget(CallInfo* info); @@ -486,7 +494,9 @@ class Mir2Lir : public Backend { bool is_volatile, bool is_ordered); bool GenIntrinsic(CallInfo* info); int LoadArgRegs(CallInfo* info, int call_state, - NextCallInsn next_call_insn, uint32_t dex_idx, uint32_t method_idx, + NextCallInsn next_call_insn, + const CompilerDriver::MethodReference& target_method, + uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this); @@ -681,11 +691,6 @@ class Mir2Lir : public Backend { // Temp workaround void Workaround7250540(RegLocation rl_dest, int value); - // TODO: add accessors for these. - LIR* literal_list_; // Constants. - LIR* method_literal_list_; // Method literals requiring patching. - LIR* code_literal_list_; // Code literals requiring patching. - protected: Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); @@ -693,6 +698,28 @@ class Mir2Lir : public Backend { return cu_; } + private: + void GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest, + RegLocation rl_src); + void GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final, + bool type_known_abstract, bool use_declaring_class, + bool can_assume_type_is_in_dex_cache, + uint32_t type_idx, RegLocation rl_dest, + RegLocation rl_src); + + void ClobberBody(RegisterInfo* p); + void ResetDefBody(RegisterInfo* p) { + p->def_start = NULL; + p->def_end = NULL; + } + + public: + // TODO: add accessors for these. + LIR* literal_list_; // Constants. + LIR* method_literal_list_; // Method literals requiring patching. + LIR* code_literal_list_; // Code literals requiring patching. + + protected: CompilationUnit* const cu_; MIRGraph* const mir_graph_; GrowableArray<SwitchTable*> switch_tables_; @@ -749,4 +776,4 @@ class Mir2Lir : public Backend { } // namespace art -#endif // ART_SRC_COMPILER_DEX_QUICK_CODEGEN_H_ +#endif //ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_H_ diff --git a/src/compiler/dex/quick/ralloc_util.cc b/src/compiler/dex/quick/ralloc_util.cc index 30ed1b7db2..8e0dba3a4f 100644 --- a/src/compiler/dex/quick/ralloc_util.cc +++ b/src/compiler/dex/quick/ralloc_util.cc @@ -18,6 +18,7 @@ #include "compiler/dex/compiler_ir.h" #include "compiler/dex/compiler_internals.h" +#include "mir_to_lir-inl.h" namespace art { @@ -84,28 +85,6 @@ void Mir2Lir::DumpFpRegPool() DumpRegPool(reg_pool_->FPRegs, reg_pool_->num_fp_regs); } -/* Mark a temp register as dead. Does not affect allocation state. */ -void Mir2Lir::ClobberBody(RegisterInfo* p) -{ - if (p->is_temp) { - DCHECK(!(p->live && p->dirty)) << "Live & dirty temp in clobber"; - p->live = false; - p->s_reg = INVALID_SREG; - p->def_start = NULL; - p->def_end = NULL; - if (p->pair) { - p->pair = false; - Clobber(p->partner); - } - } -} - -/* Mark a temp register as dead. Does not affect allocation state. */ -void Mir2Lir::Clobber(int reg) -{ - ClobberBody(GetRegInfo(reg)); -} - void Mir2Lir::ClobberSRegBody(RegisterInfo* p, int num_regs, int s_reg) { int i; @@ -555,12 +534,6 @@ void Mir2Lir::LockTemp(int reg) LOG(FATAL) << "Tried to lock a non-existant temp: r" << reg; } -void Mir2Lir::ResetDefBody(RegisterInfo* p) -{ - p->def_start = NULL; - p->def_end = NULL; -} - void Mir2Lir::ResetDef(int reg) { ResetDefBody(GetRegInfo(reg)); diff --git a/src/compiler/dex/quick/x86/assemble_x86.cc b/src/compiler/dex/quick/x86/assemble_x86.cc index f7c1594908..83dabe6c9a 100644 --- a/src/compiler/dex/quick/x86/assemble_x86.cc +++ b/src/compiler/dex/quick/x86/assemble_x86.cc @@ -15,6 +15,7 @@ */ #include "codegen_x86.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "x86_lir.h" namespace art { diff --git a/src/compiler/dex/quick/x86/call_x86.cc b/src/compiler/dex/quick/x86/call_x86.cc index 614a72d6c2..dba0e24fab 100644 --- a/src/compiler/dex/quick/x86/call_x86.cc +++ b/src/compiler/dex/quick/x86/call_x86.cc @@ -17,6 +17,7 @@ /* This file contains codegen for the X86 ISA */ #include "codegen_x86.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "x86_lir.h" namespace art { @@ -212,7 +213,7 @@ void X86Mir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg) int reg_card_no = AllocTemp(); LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); NewLIR2(kX86Mov32RT, reg_card_base, Thread::CardTableOffset().Int32Value()); - OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, CardTable::kCardShift); + OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); LIR* target = NewLIR0(kPseudoTargetLabel); diff --git a/src/compiler/dex/quick/x86/codegen_x86.h b/src/compiler/dex/quick/x86/codegen_x86.h index 99e5148f9b..9050656071 100644 --- a/src/compiler/dex/quick/x86/codegen_x86.h +++ b/src/compiler/dex/quick/x86/codegen_x86.h @@ -28,139 +28,139 @@ class X86Mir2Lir : public Mir2Lir { X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); // Required for target - codegen helpers. - virtual bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src, + bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src, RegLocation rl_dest, int lit); - virtual int LoadHelper(int offset); - virtual LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg); - virtual LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi, + int LoadHelper(int offset); + LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg); + LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi, int s_reg); - virtual LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size); - virtual LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, + LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size); + LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, int r_dest, int r_dest_hi, OpSize size, int s_reg); - virtual LIR* LoadConstantNoClobber(int r_dest, int value); - virtual LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value); - virtual LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size); - virtual LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi); - virtual LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size); - virtual LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, + LIR* LoadConstantNoClobber(int r_dest, int value); + LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value); + LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size); + LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi); + LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size); + LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement, int r_src, int r_src_hi, OpSize size, int s_reg); - virtual void MarkGCCard(int val_reg, int tgt_addr_reg); + void MarkGCCard(int val_reg, int tgt_addr_reg); // Required for target - register utilities. - virtual bool IsFpReg(int reg); - virtual bool SameRegType(int reg1, int reg2); - virtual int AllocTypedTemp(bool fp_hint, int reg_class); - virtual int AllocTypedTempPair(bool fp_hint, int reg_class); - virtual int S2d(int low_reg, int high_reg); - virtual int TargetReg(SpecialTargetRegister reg); - virtual RegisterInfo* GetRegInfo(int reg); - virtual RegLocation GetReturnAlt(); - virtual RegLocation GetReturnWideAlt(); - virtual RegLocation LocCReturn(); - virtual RegLocation LocCReturnDouble(); - virtual RegLocation LocCReturnFloat(); - virtual RegLocation LocCReturnWide(); - virtual uint32_t FpRegMask(); - virtual uint64_t GetRegMaskCommon(int reg); - virtual void AdjustSpillMask(); - virtual void ClobberCalleeSave(); - virtual void FlushReg(int reg); - virtual void FlushRegWide(int reg1, int reg2); - virtual void FreeCallTemps(); - virtual void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free); - virtual void LockCallTemps(); - virtual void MarkPreservedSingle(int v_reg, int reg); - virtual void CompilerInitializeRegAlloc(); + bool IsFpReg(int reg); + bool SameRegType(int reg1, int reg2); + int AllocTypedTemp(bool fp_hint, int reg_class); + int AllocTypedTempPair(bool fp_hint, int reg_class); + int S2d(int low_reg, int high_reg); + int TargetReg(SpecialTargetRegister reg); + RegisterInfo* GetRegInfo(int reg); + RegLocation GetReturnAlt(); + RegLocation GetReturnWideAlt(); + RegLocation LocCReturn(); + RegLocation LocCReturnDouble(); + RegLocation LocCReturnFloat(); + RegLocation LocCReturnWide(); + uint32_t FpRegMask(); + uint64_t GetRegMaskCommon(int reg); + void AdjustSpillMask(); + void ClobberCalleeSave(); + void FlushReg(int reg); + void FlushRegWide(int reg1, int reg2); + void FreeCallTemps(); + void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free); + void LockCallTemps(); + void MarkPreservedSingle(int v_reg, int reg); + void CompilerInitializeRegAlloc(); // Required for target - miscellaneous. - virtual AssemblerStatus AssembleInstructions(uintptr_t start_addr); - virtual void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); - virtual void SetupTargetResourceMasks(LIR* lir); - virtual const char* GetTargetInstFmt(int opcode); - virtual const char* GetTargetInstName(int opcode); - virtual std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); - virtual uint64_t GetPCUseDefEncoding(); - virtual uint64_t GetTargetInstFlags(int opcode); - virtual int GetInsnSize(LIR* lir); - virtual bool IsUnconditionalBranch(LIR* lir); + AssemblerStatus AssembleInstructions(uintptr_t start_addr); + void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); + void SetupTargetResourceMasks(LIR* lir); + const char* GetTargetInstFmt(int opcode); + const char* GetTargetInstName(int opcode); + std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); + uint64_t GetPCUseDefEncoding(); + uint64_t GetTargetInstFlags(int opcode); + int GetInsnSize(LIR* lir); + bool IsUnconditionalBranch(LIR* lir); // Required for target - Dalvik-level generators. - virtual void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenArrayObjPut(int opt_flags, RegLocation rl_array, + void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, RegLocation rl_src, int scale); - virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, + void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_dest, int scale); - virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, + void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_src, int scale); - virtual void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift); - virtual void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, + void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, + void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); - virtual bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); - virtual bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); - virtual bool GenInlinedSqrt(CallInfo* info); - virtual void GenNegLong(RegLocation rl_dest, RegLocation rl_src); - virtual void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, + void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); + bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); + bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); + bool GenInlinedSqrt(CallInfo* info); + void GenNegLong(RegLocation rl_dest, RegLocation rl_src); + void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, ThrowKind kind); - virtual RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); - virtual RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div); - virtual void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - virtual void GenDivZeroCheck(int reg_lo, int reg_hi); - virtual void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); - virtual void GenExitSequence(); - virtual void GenFillArrayData(uint32_t table_offset, RegLocation rl_src); - virtual void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); - virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); - virtual void GenSelect(BasicBlock* bb, MIR* mir); - virtual void GenMemBarrier(MemBarrierKind barrier_kind); - virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src); - virtual void GenMonitorExit(int opt_flags, RegLocation rl_src); - virtual void GenMoveException(RegLocation rl_dest); - virtual void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, + RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); + RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div); + void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenDivZeroCheck(int reg_lo, int reg_hi); + void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); + void GenExitSequence(); + void GenFillArrayData(uint32_t table_offset, RegLocation rl_src); + void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); + void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); + void GenSelect(BasicBlock* bb, MIR* mir); + void GenMemBarrier(MemBarrierKind barrier_kind); + void GenMonitorEnter(int opt_flags, RegLocation rl_src); + void GenMonitorExit(int opt_flags, RegLocation rl_src); + void GenMoveException(RegLocation rl_dest); + void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, int first_bit, int second_bit); - virtual void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); - virtual void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); - virtual void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - virtual void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - virtual void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); + void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); + void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); // Single operation generators. - virtual LIR* OpUnconditionalBranch(LIR* target); - virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target); - virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target); - virtual LIR* OpCondBranch(ConditionCode cc, LIR* target); - virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target); - virtual LIR* OpFpRegCopy(int r_dest, int r_src); - virtual LIR* OpIT(ConditionCode cond, const char* guide); - virtual LIR* OpMem(OpKind op, int rBase, int disp); - virtual LIR* OpPcRelLoad(int reg, LIR* target); - virtual LIR* OpReg(OpKind op, int r_dest_src); - virtual LIR* OpRegCopy(int r_dest, int r_src); - virtual LIR* OpRegCopyNoInsert(int r_dest, int r_src); - virtual LIR* OpRegImm(OpKind op, int r_dest_src1, int value); - virtual LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); - virtual LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); - virtual LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); - virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); - virtual LIR* OpTestSuspend(LIR* target); - virtual LIR* OpThreadMem(OpKind op, int thread_offset); - virtual LIR* OpVldm(int rBase, int count); - virtual LIR* OpVstm(int rBase, int count); - virtual void OpLea(int rBase, int reg1, int reg2, int scale, int offset); - virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi); - virtual void OpTlsCmp(int offset, int val); + LIR* OpUnconditionalBranch(LIR* target); + LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target); + LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target); + LIR* OpCondBranch(ConditionCode cc, LIR* target); + LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target); + LIR* OpFpRegCopy(int r_dest, int r_src); + LIR* OpIT(ConditionCode cond, const char* guide); + LIR* OpMem(OpKind op, int rBase, int disp); + LIR* OpPcRelLoad(int reg, LIR* target); + LIR* OpReg(OpKind op, int r_dest_src); + LIR* OpRegCopy(int r_dest, int r_src); + LIR* OpRegCopyNoInsert(int r_dest, int r_src); + LIR* OpRegImm(OpKind op, int r_dest_src1, int value); + LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); + LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); + LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); + LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); + LIR* OpTestSuspend(LIR* target); + LIR* OpThreadMem(OpKind op, int thread_offset); + LIR* OpVldm(int rBase, int count); + LIR* OpVstm(int rBase, int count); + void OpLea(int rBase, int reg1, int reg2, int scale, int offset); + void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi); + void OpTlsCmp(int offset, int val); void OpRegThreadMem(OpKind op, int r_dest, int thread_offset); void SpillCoreRegs(); diff --git a/src/compiler/dex/quick/x86/fp_x86.cc b/src/compiler/dex/quick/x86/fp_x86.cc index db2cf289d8..3341e28ce5 100644 --- a/src/compiler/dex/quick/x86/fp_x86.cc +++ b/src/compiler/dex/quick/x86/fp_x86.cc @@ -15,6 +15,7 @@ */ #include "codegen_x86.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "x86_lir.h" namespace art { diff --git a/src/compiler/dex/quick/x86/int_x86.cc b/src/compiler/dex/quick/x86/int_x86.cc index b2ee949f22..fffb900ec9 100644 --- a/src/compiler/dex/quick/x86/int_x86.cc +++ b/src/compiler/dex/quick/x86/int_x86.cc @@ -18,6 +18,7 @@ #include "codegen_x86.h" #include "mirror/array.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "x86_lir.h" namespace art { diff --git a/src/compiler/dex/quick/x86/target_x86.cc b/src/compiler/dex/quick/x86/target_x86.cc index e6a49f8ce3..9110b704b8 100644 --- a/src/compiler/dex/quick/x86/target_x86.cc +++ b/src/compiler/dex/quick/x86/target_x86.cc @@ -16,6 +16,7 @@ #include "codegen_x86.h" #include "compiler/dex/compiler_internals.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "x86_lir.h" #include <string> diff --git a/src/compiler/dex/quick/x86/utility_x86.cc b/src/compiler/dex/quick/x86/utility_x86.cc index 45c0e9cb41..82466d496b 100644 --- a/src/compiler/dex/quick/x86/utility_x86.cc +++ b/src/compiler/dex/quick/x86/utility_x86.cc @@ -15,6 +15,7 @@ */ #include "codegen_x86.h" +#include "compiler/dex/quick/mir_to_lir-inl.h" #include "x86_lir.h" namespace art { diff --git a/src/compiler/dex/ssa_transformation.cc b/src/compiler/dex/ssa_transformation.cc index a90d705c6f..41820720d8 100644 --- a/src/compiler/dex/ssa_transformation.cc +++ b/src/compiler/dex/ssa_transformation.cc @@ -15,7 +15,7 @@ */ #include "compiler_internals.h" -#include "dataflow_iterator.h" +#include "dataflow_iterator-inl.h" #define NOTVISITED (-1) diff --git a/src/compiler/dex/vreg_analysis.cc b/src/compiler/dex/vreg_analysis.cc index d4223f1ab1..b941140b00 100644 --- a/src/compiler/dex/vreg_analysis.cc +++ b/src/compiler/dex/vreg_analysis.cc @@ -15,7 +15,7 @@ */ #include "compiler_internals.h" -#include "dataflow_iterator.h" +#include "compiler/dex/dataflow_iterator-inl.h" namespace art { @@ -292,18 +292,10 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) is_high |= is_phi && rl_temp.wide && rl_temp.high_word; } /* - * TODO: cleaner fix - * We don't normally expect to see a Dalvik register - * definition used both as a floating point and core - * value. However, the instruction rewriting that occurs - * during verification can eliminate some type information, - * leaving us confused. The real fix here is either to - * add explicit type information to Dalvik byte codes, - * or to recognize THROW_VERIFICATION_ERROR as - * an unconditional branch and support dead code elimination. - * As a workaround we can detect this situation and - * disable register promotion (which is the only thing that - * relies on distinctions between core and fp usages. + * We don't normally expect to see a Dalvik register definition used both as a + * floating point and core value, though technically it could happen with constants. + * Until we have proper typing, detect this situation and disable register promotion + * (which relies on the distinction between core a fp usages). */ if ((defined_fp && (defined_core | defined_ref)) && ((cu_->disable_opt & (1 << kPromoteRegs)) == 0)) { diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 698517277f..24299ea9bc 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -24,17 +24,19 @@ #include "base/stl_util.h" #include "base/timing_logger.h" #include "class_linker.h" +#include "compiler/stubs/stubs.h" #include "dex_compilation_unit.h" #include "dex_file-inl.h" #include "jni_internal.h" #include "oat_file.h" #include "object_utils.h" #include "runtime.h" -#include "gc/card_table-inl.h" -#include "gc/space.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/space/space.h" #include "mirror/class_loader.h" #include "mirror/class-inl.h" -#include "mirror/dex_cache.h" +#include "mirror/dex_cache-inl.h" #include "mirror/field-inl.h" #include "mirror/abstract_method-inl.h" #include "mirror/object-inl.h" @@ -72,7 +74,8 @@ class AOTCompilationStats { resolved_types_(0), unresolved_types_(0), resolved_instance_fields_(0), unresolved_instance_fields_(0), resolved_local_static_fields_(0), resolved_static_fields_(0), unresolved_static_fields_(0), - type_based_devirtualization_(0) { + type_based_devirtualization_(0), + safe_casts_(0), not_safe_casts_(0) { for (size_t i = 0; i <= kMaxInvokeType; i++) { resolved_methods_[i] = 0; unresolved_methods_[i] = 0; @@ -91,8 +94,14 @@ class AOTCompilationStats { "static fields resolved"); DumpStat(resolved_local_static_fields_, resolved_static_fields_ + unresolved_static_fields_, "static fields local to a class"); - DumpStat(type_based_devirtualization_,virtual_made_direct_[kInterface] + virtual_made_direct_[kVirtual] - - type_based_devirtualization_, "sharpened calls based on type information"); + DumpStat(safe_casts_, not_safe_casts_, "check-casts removed based on type information"); + // Note, the code below subtracts the stat value so that when added to the stat value we have + // 100% of samples. TODO: clean this up. + DumpStat(type_based_devirtualization_, + resolved_methods_[kVirtual] + unresolved_methods_[kVirtual] + + resolved_methods_[kInterface] + unresolved_methods_[kInterface] - + type_based_devirtualization_, + "virtual/interface calls made direct based on type information"); for (size_t i = 0; i <= kMaxInvokeType; i++) { std::ostringstream oss; @@ -184,40 +193,61 @@ class AOTCompilationStats { unresolved_static_fields_++; } + // Indicate that type information from the verifier led to devirtualization. void PreciseTypeDevirtualization() { STATS_LOCK(); type_based_devirtualization_++; } + + // Indicate that a method of the given type was resolved at compile time. void ResolvedMethod(InvokeType type) { DCHECK_LE(type, kMaxInvokeType); STATS_LOCK(); resolved_methods_[type]++; } + // Indicate that a method of the given type was unresolved at compile time as it was in an + // unknown dex file. void UnresolvedMethod(InvokeType type) { DCHECK_LE(type, kMaxInvokeType); STATS_LOCK(); unresolved_methods_[type]++; } + // Indicate that a type of virtual method dispatch has been converted into a direct method + // dispatch. void VirtualMadeDirect(InvokeType type) { - DCHECK_LE(type, kMaxInvokeType); + DCHECK(type == kVirtual || type == kInterface || type == kSuper); STATS_LOCK(); virtual_made_direct_[type]++; } + // Indicate that a method of the given type was able to call directly into boot. void DirectCallsToBoot(InvokeType type) { DCHECK_LE(type, kMaxInvokeType); STATS_LOCK(); direct_calls_to_boot_[type]++; } + // Indicate that a method of the given type was able to be resolved directly from boot. void DirectMethodsToBoot(InvokeType type) { DCHECK_LE(type, kMaxInvokeType); STATS_LOCK(); direct_methods_to_boot_[type]++; } + // A check-cast could be eliminated due to verifier type analysis. + void SafeCast() { + STATS_LOCK(); + safe_casts_++; + } + + // A check-cast couldn't be eliminated due to verifier type analysis. + void NotASafeCast() { + STATS_LOCK(); + not_safe_casts_++; + } + private: Mutex stats_lock_; @@ -245,6 +275,9 @@ class AOTCompilationStats { size_t direct_calls_to_boot_[kMaxInvokeType + 1]; size_t direct_methods_to_boot_[kMaxInvokeType + 1]; + size_t safe_casts_; + size_t not_safe_casts_; + DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats); }; @@ -292,8 +325,8 @@ static Fn FindFunction(const std::string& compiler_so_name, void* library, const } CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, - bool image, size_t thread_count, bool support_debugging, - const std::set<std::string>* image_classes, + bool image, DescriptorSet* image_classes, + size_t thread_count, bool support_debugging, bool dump_stats, bool dump_timings) : compiler_backend_(compiler_backend), instruction_set_(instruction_set), @@ -301,18 +334,20 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet compiled_classes_lock_("compiled classes lock"), compiled_methods_lock_("compiled method lock"), image_(image), + image_classes_(image_classes), thread_count_(thread_count), support_debugging_(support_debugging), start_ns_(0), stats_(new AOTCompilationStats), dump_stats_(dump_stats), dump_timings_(dump_timings), - image_classes_(image_classes), compiler_library_(NULL), compiler_(NULL), compiler_context_(NULL), jni_compiler_(NULL), - compiler_get_method_code_addr_(NULL) + compiler_enable_auto_elf_loading_(NULL), + compiler_get_method_code_addr_(NULL), + support_boot_image_fixup_(true) { std::string compiler_so_name(MakeCompilerSoName(compiler_backend_)); compiler_library_ = dlopen(compiler_so_name.c_str(), RTLD_LAZY); @@ -337,6 +372,13 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtQuickCompileMethod"); } + dex_to_dex_compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtCompileDEX"); + + sea_ir_compiler_ = NULL; + if (Runtime::Current()->IsSeaIRMode()) { + sea_ir_compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "SeaIrCompileMethod"); + } + init_compiler_context(*this); if (compiler_backend_ == kPortable) { @@ -347,7 +389,7 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet CHECK(!Runtime::Current()->IsStarted()); if (!image_) { - CHECK(image_classes_ == NULL); + CHECK(image_classes_.get() == NULL); } } @@ -416,6 +458,66 @@ CompilerTls* CompilerDriver::GetTls() { return res; } +const std::vector<uint8_t>* CompilerDriver::CreatePortableResolutionTrampoline() const { + switch (instruction_set_) { + case kArm: + case kThumb2: + return arm::CreatePortableResolutionTrampoline(); + case kMips: + return mips::CreatePortableResolutionTrampoline(); + case kX86: + return x86::CreatePortableResolutionTrampoline(); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + return NULL; + } +} + +const std::vector<uint8_t>* CompilerDriver::CreateQuickResolutionTrampoline() const { + switch (instruction_set_) { + case kArm: + case kThumb2: + return arm::CreateQuickResolutionTrampoline(); + case kMips: + return mips::CreateQuickResolutionTrampoline(); + case kX86: + return x86::CreateQuickResolutionTrampoline(); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + return NULL; + } +} + +const std::vector<uint8_t>* CompilerDriver::CreateInterpreterToInterpreterEntry() const { + switch (instruction_set_) { + case kArm: + case kThumb2: + return arm::CreateInterpreterToInterpreterEntry(); + case kMips: + return mips::CreateInterpreterToInterpreterEntry(); + case kX86: + return x86::CreateInterpreterToInterpreterEntry(); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + return NULL; + } +} + +const std::vector<uint8_t>* CompilerDriver::CreateInterpreterToQuickEntry() const { + switch (instruction_set_) { + case kArm: + case kThumb2: + return arm::CreateInterpreterToQuickEntry(); + case kMips: + return mips::CreateInterpreterToQuickEntry(); + case kX86: + return x86::CreateInterpreterToQuickEntry(); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + return NULL; + } +} + void CompilerDriver::CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files) { DCHECK(!Runtime::Current()->IsStarted()); @@ -436,10 +538,33 @@ void CompilerDriver::CompileAll(jobject class_loader, } } +static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader, + const DexFile& dex_file, + const DexFile::ClassDef& class_def) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Do not allow DEX-to-DEX compilation of image classes. This is to prevent the + // verifier from passing on "quick" instruction at compilation time. It must + // only pass on quick instructions at runtime. + if (class_loader == NULL) { + return false; + } + const char* descriptor = dex_file.GetClassDescriptor(class_def); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::Class* klass = class_linker->FindClass(descriptor, class_loader); + if (klass == NULL) { + Thread* self = Thread::Current(); + CHECK(self->IsExceptionPending()); + self->ClearException(); + return false; + } + // DEX-to-DEX compilation is only allowed on preverified classes. + return klass->IsVerified(); +} + void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) { DCHECK(!Runtime::Current()->IsStarted()); Thread* self = Thread::Current(); - jobject class_loader; + jobject jclass_loader; const DexFile* dex_file; uint32_t class_def_idx; { @@ -447,7 +572,7 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) { ScopedLocalRef<jobject> local_class_loader(soa.Env(), soa.AddLocalReference<jobject>(method->GetDeclaringClass()->GetClassLoader())); - class_loader = soa.Env()->NewGlobalRef(local_class_loader.get()); + jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get()); // Find the dex_file MethodHelper mh(method); dex_file = &mh.GetDexFile(); @@ -460,14 +585,22 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) { UniquePtr<ThreadPool> thread_pool(new ThreadPool(1U)); TimingLogger timings("CompileOne", false); - PreCompile(class_loader, dex_files, *thread_pool.get(), timings); + PreCompile(jclass_loader, dex_files, *thread_pool.get(), timings); uint32_t method_idx = method->GetDexMethodIndex(); const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); + // Can we run DEX-to-DEX compiler on this class ? + bool allow_dex_compilation; + { + ScopedObjectAccess soa(Thread::Current()); + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); + mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); + allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, *dex_file, class_def); + } CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(), - class_def_idx, method_idx, class_loader, *dex_file); + class_def_idx, method_idx, jclass_loader, *dex_file, allow_dex_compilation); - self->GetJniEnv()->DeleteGlobalRef(class_loader); + self->GetJniEnv()->DeleteGlobalRef(jclass_loader); self->TransitionFromSuspendedToRunnable(); } @@ -483,20 +616,199 @@ void CompilerDriver::Resolve(jobject class_loader, const std::vector<const DexFi void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, ThreadPool& thread_pool, TimingLogger& timings) { + LoadImageClasses(timings); + Resolve(class_loader, dex_files, thread_pool, timings); Verify(class_loader, dex_files, thread_pool, timings); InitializeClasses(class_loader, dex_files, thread_pool, timings); + + UpdateImageClasses(timings); } -bool CompilerDriver::IsImageClass(const std::string& descriptor) const { - if (image_classes_ == NULL) { - return false; +bool CompilerDriver::IsImageClass(const char* descriptor) const { + DCHECK(descriptor != NULL); + if (image_classes_.get() == NULL) { + return true; } return image_classes_->find(descriptor) != image_classes_->end(); } +static void ResolveExceptionsForMethod(MethodHelper* mh, + std::set<std::pair<uint16_t, const DexFile*> >& exceptions_to_resolve) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::CodeItem* code_item = mh->GetCodeItem(); + if (code_item == NULL) { + return; // native or abstract method + } + if (code_item->tries_size_ == 0) { + return; // nothing to process + } + const byte* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0); + size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list); + for (size_t i = 0; i < num_encoded_catch_handlers; i++) { + int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list); + bool has_catch_all = false; + if (encoded_catch_handler_size <= 0) { + encoded_catch_handler_size = -encoded_catch_handler_size; + has_catch_all = true; + } + for (int32_t j = 0; j < encoded_catch_handler_size; j++) { + uint16_t encoded_catch_handler_handlers_type_idx = + DecodeUnsignedLeb128(&encoded_catch_handler_list); + // Add to set of types to resolve if not already in the dex cache resolved types + if (!mh->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { + exceptions_to_resolve.insert( + std::pair<uint16_t, const DexFile*>(encoded_catch_handler_handlers_type_idx, + &mh->GetDexFile())); + } + // ignore address associated with catch handler + DecodeUnsignedLeb128(&encoded_catch_handler_list); + } + if (has_catch_all) { + // ignore catch all address + DecodeUnsignedLeb128(&encoded_catch_handler_list); + } + } +} + +static bool ResolveCatchBlockExceptionsClassVisitor(mirror::Class* c, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + std::set<std::pair<uint16_t, const DexFile*> >* exceptions_to_resolve = + reinterpret_cast<std::set<std::pair<uint16_t, const DexFile*> >*>(arg); + MethodHelper mh; + for (size_t i = 0; i < c->NumVirtualMethods(); ++i) { + mirror::AbstractMethod* m = c->GetVirtualMethod(i); + mh.ChangeMethod(m); + ResolveExceptionsForMethod(&mh, *exceptions_to_resolve); + } + for (size_t i = 0; i < c->NumDirectMethods(); ++i) { + mirror::AbstractMethod* m = c->GetDirectMethod(i); + mh.ChangeMethod(m); + ResolveExceptionsForMethod(&mh, *exceptions_to_resolve); + } + return true; +} + +static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CompilerDriver::DescriptorSet* image_classes = + reinterpret_cast<CompilerDriver::DescriptorSet*>(arg); + image_classes->insert(ClassHelper(klass).GetDescriptor()); + return true; +} + +// Make a list of descriptors for classes to include in the image +void CompilerDriver::LoadImageClasses(TimingLogger& timings) + LOCKS_EXCLUDED(Locks::mutator_lock_) { + if (image_classes_.get() == NULL) { + return; + } + + // Make a first class to load all classes explicitly listed in the file + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + typedef DescriptorSet::iterator It; // TODO: C++0x auto + for (It it = image_classes_->begin(), end = image_classes_->end(); it != end;) { + std::string descriptor(*it); + SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(descriptor.c_str())); + if (klass.get() == NULL) { + image_classes_->erase(it++); + LOG(WARNING) << "Failed to find class " << descriptor; + Thread::Current()->ClearException(); + } else { + ++it; + } + } + + // Resolve exception classes referenced by the loaded classes. The catch logic assumes + // exceptions are resolved by the verifier when there is a catch block in an interested method. + // Do this here so that exception classes appear to have been specified image classes. + std::set<std::pair<uint16_t, const DexFile*> > unresolved_exception_types; + SirtRef<mirror::Class> java_lang_Throwable(self, + class_linker->FindSystemClass("Ljava/lang/Throwable;")); + do { + unresolved_exception_types.clear(); + class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor, + &unresolved_exception_types); + typedef std::set<std::pair<uint16_t, const DexFile*> >::const_iterator It; // TODO: C++0x auto + for (It it = unresolved_exception_types.begin(), + end = unresolved_exception_types.end(); + it != end; ++it) { + uint16_t exception_type_idx = it->first; + const DexFile* dex_file = it->second; + mirror::DexCache* dex_cache = class_linker->FindDexCache(*dex_file); + mirror:: ClassLoader* class_loader = NULL; + SirtRef<mirror::Class> klass(self, class_linker->ResolveType(*dex_file, exception_type_idx, + dex_cache, class_loader)); + if (klass.get() == NULL) { + const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); + const char* descriptor = dex_file->GetTypeDescriptor(type_id); + LOG(FATAL) << "Failed to resolve class " << descriptor; + } + DCHECK(java_lang_Throwable->IsAssignableFrom(klass.get())); + } + // Resolving exceptions may load classes that reference more exceptions, iterate until no + // more are found + } while (!unresolved_exception_types.empty()); + + // We walk the roots looking for classes so that we'll pick up the + // above classes plus any classes them depend on such super + // classes, interfaces, and the required ClassLinker roots. + class_linker->VisitClasses(RecordImageClassesVisitor, image_classes_.get()); + + CHECK_NE(image_classes_->size(), 0U); + timings.AddSplit("LoadImageClasses"); +} + +static void MaybeAddToImageClasses(mirror::Class* klass, CompilerDriver::DescriptorSet* image_classes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + while (!klass->IsObjectClass()) { + ClassHelper kh(klass); + const char* descriptor = kh.GetDescriptor(); + std::pair<CompilerDriver::DescriptorSet::iterator, bool> result = + image_classes->insert(descriptor); + if (result.second) { + LOG(INFO) << "Adding " << descriptor << " to image classes"; + } else { + return; + } + for (size_t i = 0; i < kh.NumDirectInterfaces(); ++i) { + MaybeAddToImageClasses(kh.GetDirectInterface(i), image_classes); + } + if (klass->IsArrayClass()) { + MaybeAddToImageClasses(klass->GetComponentType(), image_classes); + } + klass = klass->GetSuperClass(); + } +} + +void CompilerDriver::FindClinitImageClassesCallback(mirror::Object* object, void* arg) { + DCHECK(object != NULL); + DCHECK(arg != NULL); + CompilerDriver* compiler_driver = reinterpret_cast<CompilerDriver*>(arg); + MaybeAddToImageClasses(object->GetClass(), compiler_driver->image_classes_.get()); +} + +void CompilerDriver::UpdateImageClasses(TimingLogger& timings) { + if (image_classes_.get() == NULL) { + return; + } + + // Update image_classes_ with classes for objects created by <clinit> methods. + Thread* self = Thread::Current(); + const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); + gc::Heap* heap = Runtime::Current()->GetHeap(); + // TODO: Image spaces only? + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this); + self->EndAssertNoThreadSuspension(old_cause); + timings.AddSplit("UpdateImageClasses"); +} + void CompilerDriver::RecordClassStatus(ClassReference ref, CompiledClass* compiled_class) { MutexLock mu(Thread::Current(), CompilerDriver::compiled_classes_lock_); compiled_classes_.Put(ref, compiled_class); @@ -504,24 +816,19 @@ void CompilerDriver::RecordClassStatus(ClassReference ref, CompiledClass* compil bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) { - ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); - if (!IsImage()) { - stats_->TypeNotInDexCache(); - return false; - } - mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); - if (resolved_class == NULL) { - stats_->TypeNotInDexCache(); - return false; - } - bool result = IsImageClass(ClassHelper(resolved_class).GetDescriptor()); - if (result) { + if (IsImage() && IsImageClass(dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)))) { + if (kIsDebugBuild) { + ScopedObjectAccess soa(Thread::Current()); + mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); + mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); + CHECK(resolved_class != NULL); + } stats_->TypeInDexCache(); + return true; } else { stats_->TypeNotInDexCache(); + return false; } - return result; } bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, @@ -545,7 +852,18 @@ bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, } bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file, - uint32_t type_idx) { + uint32_t type_idx, + bool* type_known_final, bool* type_known_abstract, + bool* equals_referrers_class) { + if (type_known_final != NULL) { + *type_known_final = false; + } + if (type_known_abstract != NULL) { + *type_known_abstract = false; + } + if (equals_referrers_class != NULL) { + *equals_referrers_class = false; + } ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); // Get type from dex cache assuming it was populated by the verifier @@ -555,6 +873,9 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const Dex return false; // Unknown class needs access checks. } const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx); + if (equals_referrers_class != NULL) { + *equals_referrers_class = (method_id.class_idx_ == type_idx); + } mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); if (referrer_class == NULL) { stats_->TypeNeedsAccessCheck(); @@ -565,6 +886,12 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const Dex bool result = referrer_class->CanAccess(resolved_class); if (result) { stats_->TypeDoesntNeedAccessCheck(); + if (type_known_final != NULL) { + *type_known_final = resolved_class->IsFinal() && !resolved_class->IsArrayClass(); + } + if (type_known_abstract != NULL) { + *type_known_abstract = resolved_class->IsAbstract() && !resolved_class->IsArrayClass(); + } } else { stats_->TypeNeedsAccessCheck(); } @@ -600,9 +927,14 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_id } static mirror::Class* ComputeCompilingMethodsClass(ScopedObjectAccess& soa, + mirror::DexCache* dex_cache, const DexCompilationUnit* mUnit) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); + // The passed dex_cache is a hint, sanity check before asking the class linker that will take a + // lock. + if (dex_cache->GetDexFile() != mUnit->GetDexFile()) { + dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); + } mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()); const DexFile::MethodId& referrer_method_id = mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); return mUnit->GetClassLinker()->ResolveType(*mUnit->GetDexFile(), referrer_method_id.class_idx_, @@ -639,7 +971,9 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi // Try to resolve field and ignore if an Incompatible Class Change Error (ie is static). mirror::Field* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && !resolved_field->IsStatic()) { - mirror::Class* referrer_class = ComputeCompilingMethodsClass(soa, mUnit); + mirror::Class* referrer_class = + ComputeCompilingMethodsClass(soa, resolved_field->GetDeclaringClass()->GetDexCache(), + mUnit); if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); bool access_ok = referrer_class->CanAccess(fields_class) && @@ -688,7 +1022,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila // Try to resolve field and ignore if an Incompatible Class Change Error (ie isn't static). mirror::Field* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && resolved_field->IsStatic()) { - mirror::Class* referrer_class = ComputeCompilingMethodsClass(soa, mUnit); + mirror::Class* referrer_class = + ComputeCompilingMethodsClass(soa, resolved_field->GetDeclaringClass()->GetDexCache(), + mUnit); if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); if (fields_class == referrer_class) { @@ -733,9 +1069,8 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila } // Search dex file for localized ssb index, may fail if field's class is a parent // of the class mentioned in the dex file and there is no dex cache entry. - std::string descriptor(FieldHelper(resolved_field).GetDeclaringClassDescriptor()); const DexFile::StringId* string_id = - mUnit->GetDexFile()->FindStringId(descriptor); + mUnit->GetDexFile()->FindStringId(FieldHelper(resolved_field).GetDeclaringClassDescriptor()); if (string_id != NULL) { const DexFile::TypeId* type_id = mUnit->GetDexFile()->FindTypeId(mUnit->GetDexFile()->GetIndexForStringId(*string_id)); @@ -764,7 +1099,8 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType s mirror::Class* referrer_class, mirror::AbstractMethod* method, uintptr_t& direct_code, - uintptr_t& direct_method) { + uintptr_t& direct_method, + bool update_stats) { // For direct and static methods compute possible direct_code and direct_method values, ie // an address for the Method* being invoked and an address of the code for that Method*. // For interface calls compute a value for direct_method that is the interface method being @@ -789,14 +1125,15 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType s // Ensure we run the clinit trampoline unless we are invoking a static method in the same class. return; } - if (sharp_type != kInterface) { // Interfaces always go via a trampoline. - stats_->DirectCallsToBoot(type); + if (update_stats) { + if (sharp_type != kInterface) { // Interfaces always go via a trampoline. + stats_->DirectCallsToBoot(type); + } + stats_->DirectMethodsToBoot(type); } - stats_->DirectMethodsToBoot(type); - bool compiling_boot = Runtime::Current()->GetHeap()->GetSpaces().size() == 1; + bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1; if (compiling_boot) { - const bool kSupportBootImageFixup = true; - if (kSupportBootImageFixup) { + if (support_boot_image_fixup_) { MethodHelper mh(method); if (IsImageClass(mh.GetDeclaringClassDescriptor())) { // We can only branch directly to Methods that are resolved in the DexCache. @@ -806,33 +1143,33 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType s } } } else { - if (Runtime::Current()->GetHeap()->FindSpaceFromObject(method)->IsImageSpace()) { + if (Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace()) { direct_method = reinterpret_cast<uintptr_t>(method); } direct_code = reinterpret_cast<uintptr_t>(method->GetEntryPointFromCompiledCode()); } } -bool CompilerDriver::ComputeInvokeInfo(uint32_t method_idx,const uint32_t dex_pc, - const DexCompilationUnit* mUnit, InvokeType& type, - int& vtable_idx, uintptr_t& direct_code, - uintptr_t& direct_method) { +bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc, + InvokeType& invoke_type, + MethodReference& target_method, + int& vtable_idx, + uintptr_t& direct_code, uintptr_t& direct_method, + bool update_stats) { ScopedObjectAccess soa(Thread::Current()); - - const bool kEnableVerifierBasedSharpening = true; - const CompilerDriver::MethodReference ref_caller(mUnit->GetDexFile(), mUnit->GetDexMethodIndex()); - const CompilerDriver::MethodReference* ref_sharpen = verifier::MethodVerifier::GetDevirtMap(ref_caller, dex_pc); - bool can_devirtualize = (dex_pc != art::kDexPCNotReady) && (ref_sharpen != NULL); vtable_idx = -1; direct_code = 0; direct_method = 0; mirror::AbstractMethod* resolved_method = - ComputeMethodReferencedFromCompilingMethod(soa, mUnit, method_idx, type); + ComputeMethodReferencedFromCompilingMethod(soa, mUnit, target_method.dex_method_index, + invoke_type); if (resolved_method != NULL) { // Don't try to fast-path if we don't understand the caller's class or this appears to be an // Incompatible Class Change Error. - mirror::Class* referrer_class = ComputeCompilingMethodsClass(soa, mUnit); - bool icce = resolved_method->CheckIncompatibleClassChange(type); + mirror::Class* referrer_class = + ComputeCompilingMethodsClass(soa, resolved_method->GetDeclaringClass()->GetDexCache(), + mUnit); + bool icce = resolved_method->CheckIncompatibleClassChange(invoke_type); if (referrer_class != NULL && !icce) { mirror::Class* methods_class = resolved_method->GetDeclaringClass(); if (!referrer_class->CanAccess(methods_class) || @@ -842,74 +1179,168 @@ bool CompilerDriver::ComputeInvokeInfo(uint32_t method_idx,const uint32_t dex_pc // protected method being made public by implementing an interface that re-declares the // method public. Resort to the dex file to determine the correct class for the access // check. - const DexFile& dex_file = *referrer_class->GetDexCache()->GetDexFile(); - methods_class = - mUnit->GetClassLinker()->ResolveType(dex_file, - dex_file.GetMethodId(method_idx).class_idx_, - referrer_class); + uint16_t class_idx = + target_method.dex_file->GetMethodId(target_method.dex_method_index).class_idx_; + methods_class = mUnit->GetClassLinker()->ResolveType(*target_method.dex_file, + class_idx, referrer_class); } if (referrer_class->CanAccess(methods_class) && referrer_class->CanAccessMember(methods_class, resolved_method->GetAccessFlags())) { - vtable_idx = resolved_method->GetMethodIndex(); - const bool kEnableSharpening = true; - // Sharpen a virtual call into a direct call when the target is known. - bool can_sharpen = type == kVirtual && (resolved_method->IsFinal() || - methods_class->IsFinal()); - // Ensure the vtable index will be correct to dispatch in the vtable of the super class. - can_sharpen = can_sharpen || (type == kSuper && referrer_class != methods_class && - referrer_class->IsSubClass(methods_class) && - vtable_idx < methods_class->GetVTable()->GetLength() && - methods_class->GetVTable()->Get(vtable_idx) == resolved_method); - - if (kEnableSharpening && can_sharpen) { - stats_->ResolvedMethod(type); + const bool kEnableFinalBasedSharpening = true; + // Sharpen a virtual call into a direct call when the target is known not to have been + // overridden (ie is final). + bool can_sharpen_virtual_based_on_type = + (invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal()); + // For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of + // the super class. + bool can_sharpen_super_based_on_type = (invoke_type == kSuper) && + (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) && + resolved_method->GetMethodIndex() < methods_class->GetVTable()->GetLength() && + (methods_class->GetVTable()->Get(resolved_method->GetMethodIndex()) == resolved_method); + + if (kEnableFinalBasedSharpening && (can_sharpen_virtual_based_on_type || + can_sharpen_super_based_on_type)) { // Sharpen a virtual call into a direct call. The method_idx is into referrer's // dex cache, check that this resolved method is where we expect it. - CHECK(referrer_class->GetDexCache()->GetResolvedMethod(method_idx) == resolved_method) - << PrettyMethod(resolved_method); - stats_->VirtualMadeDirect(type); - GetCodeAndMethodForDirectCall(type, kDirect, referrer_class, resolved_method, - direct_code, direct_method); - type = kDirect; + CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method.dex_method_index) == + resolved_method) << PrettyMethod(resolved_method); + if (update_stats) { + stats_->ResolvedMethod(invoke_type); + stats_->VirtualMadeDirect(invoke_type); + } + GetCodeAndMethodForDirectCall(invoke_type, kDirect, referrer_class, resolved_method, + direct_code, direct_method, update_stats); + invoke_type = kDirect; return true; - } else if(can_devirtualize && kEnableSharpening && kEnableVerifierBasedSharpening) { - // If traditional sharpening fails, try the sharpening based on type information (Devirtualization) - mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*ref_sharpen->first); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()); - mirror::AbstractMethod* concrete_method = mUnit->GetClassLinker()->ResolveMethod( - *ref_sharpen->first, ref_sharpen->second, dex_cache, class_loader, NULL, kVirtual); - CHECK(concrete_method != NULL); - CHECK(!concrete_method->IsAbstract()); - // TODO: fix breakage in image patching to be able to devirtualize cases with different - // resolved and concrete methods. - if(resolved_method == concrete_method) { - GetCodeAndMethodForDirectCall(type, kDirect, referrer_class, concrete_method, direct_code, direct_method); - stats_->VirtualMadeDirect(type); - type = kDirect; - stats_->PreciseTypeDevirtualization(); + } + const bool kEnableVerifierBasedSharpening = true; + if (kEnableVerifierBasedSharpening && (invoke_type == kVirtual || + invoke_type == kInterface)) { + // Did the verifier record a more precise invoke target based on its type information? + const CompilerDriver::MethodReference caller_method(mUnit->GetDexFile(), + mUnit->GetDexMethodIndex()); + const CompilerDriver::MethodReference* devirt_map_target = + verifier::MethodVerifier::GetDevirtMap(caller_method, dex_pc); + if (devirt_map_target != NULL) { + mirror::DexCache* target_dex_cache = + mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file); + mirror::ClassLoader* class_loader = + soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()); + mirror::AbstractMethod* called_method = + mUnit->GetClassLinker()->ResolveMethod(*devirt_map_target->dex_file, + devirt_map_target->dex_method_index, + target_dex_cache, class_loader, NULL, + kVirtual); + CHECK(called_method != NULL); + CHECK(!called_method->IsAbstract()); + GetCodeAndMethodForDirectCall(invoke_type, kDirect, referrer_class, called_method, + direct_code, direct_method, update_stats); + bool compiler_needs_dex_cache = + (GetCompilerBackend() == kPortable) || + (GetCompilerBackend() == kQuick && instruction_set_ != kThumb2) || + (direct_code == 0) || (direct_code == static_cast<unsigned int>(-1)) || + (direct_method == 0) || (direct_method == static_cast<unsigned int>(-1)); + if ((devirt_map_target->dex_file != target_method.dex_file) && + compiler_needs_dex_cache) { + // We need to use the dex cache to find either the method or code, and the dex file + // containing the method isn't the one expected for the target method. Try to find + // the method within the expected target dex file. + // TODO: the -1 could be handled as direct code if the patching new the target dex + // file. + // TODO: quick only supports direct pointers with Thumb2. + // TODO: the following should be factored into a common helper routine to find + // one dex file's method within another. + const DexFile* dexfile = target_method.dex_file; + const DexFile* cm_dexfile = + called_method->GetDeclaringClass()->GetDexCache()->GetDexFile(); + const DexFile::MethodId& cm_method_id = + cm_dexfile->GetMethodId(called_method->GetDexMethodIndex()); + const char* cm_descriptor = cm_dexfile->StringByTypeIdx(cm_method_id.class_idx_); + const DexFile::StringId* descriptor = dexfile->FindStringId(cm_descriptor); + if (descriptor != NULL) { + const DexFile::TypeId* type_id = + dexfile->FindTypeId(dexfile->GetIndexForStringId(*descriptor)); + if (type_id != NULL) { + const char* cm_name = cm_dexfile->GetMethodName(cm_method_id); + const DexFile::StringId* name = dexfile->FindStringId(cm_name); + if (name != NULL) { + uint16_t return_type_idx; + std::vector<uint16_t> param_type_idxs; + bool success = dexfile->CreateTypeList(&return_type_idx, ¶m_type_idxs, + cm_dexfile->GetMethodSignature(cm_method_id)); + if (success) { + const DexFile::ProtoId* sig = + dexfile->FindProtoId(return_type_idx, param_type_idxs); + if (sig != NULL) { + const DexFile::MethodId* method_id = dexfile->FindMethodId(*type_id, + *name, *sig); + if (method_id != NULL) { + if (update_stats) { + stats_->ResolvedMethod(invoke_type); + stats_->VirtualMadeDirect(invoke_type); + stats_->PreciseTypeDevirtualization(); + } + target_method.dex_method_index = dexfile->GetIndexForMethodId(*method_id); + invoke_type = kDirect; + return true; + } + } + } + } + } + } + // TODO: the stats for direct code and method are off as we failed to find the direct + // method in the referring method's dex cache/file. + } else { + if (update_stats) { + stats_->ResolvedMethod(invoke_type); + stats_->VirtualMadeDirect(invoke_type); + stats_->PreciseTypeDevirtualization(); + } + target_method = *devirt_map_target; + invoke_type = kDirect; + return true; } - stats_->ResolvedMethod(type); - return true; + } } - else if (type == kSuper) { + if (invoke_type == kSuper) { // Unsharpened super calls are suspicious so go slow-path. } else { - stats_->ResolvedMethod(type); - GetCodeAndMethodForDirectCall(type, type, referrer_class, resolved_method, - direct_code, direct_method); + // Sharpening failed so generate a regular resolved method dispatch. + if (update_stats) { + stats_->ResolvedMethod(invoke_type); + } + if (invoke_type == kVirtual || invoke_type == kSuper) { + vtable_idx = resolved_method->GetMethodIndex(); + } + GetCodeAndMethodForDirectCall(invoke_type, invoke_type, referrer_class, resolved_method, + direct_code, direct_method, update_stats); return true; } } } } - // Clean up any exception left by method/type resolution + // Clean up any exception left by method/invoke_type resolution if (soa.Self()->IsExceptionPending()) { soa.Self()->ClearException(); } - stats_->UnresolvedMethod(type); + if (update_stats) { + stats_->UnresolvedMethod(invoke_type); + } return false; // Incomplete knowledge needs slow path. } +bool CompilerDriver::IsSafeCast(const MethodReference& mr, uint32_t dex_pc) { + bool result = verifier::MethodVerifier::IsSafeCast(mr, dex_pc); + if (result) { + stats_->SafeCast(); + } else { + stats_->NotASafeCast(); + } + return result; +} + + void CompilerDriver::AddCodePatch(const DexFile* dex_file, uint32_t referrer_method_idx, InvokeType referrer_invoke_type, @@ -990,7 +1421,7 @@ class ParallelCompilationManager { CHECK_NE(self->GetState(), kRunnable); // Wait for all the worker threads to finish. - thread_pool_->Wait(self); + thread_pool_->Wait(self, true, false); } private: @@ -1404,10 +1835,13 @@ static const char* class_initializer_black_list[] = { "Ljava/io/ObjectStreamClass;", // Calls to Class.forName -> java.io.FileDescriptor. "Ljava/io/ObjectStreamConstants;", // Instance of non-image class SerializablePermission. "Ljava/lang/ClassLoader$SystemClassLoader;", // Calls System.getProperty -> OsConstants.initConstants. + "Ljava/lang/HexStringParser;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. + "Ljava/lang/ProcessManager;", // Calls Thread.currentThread. "Ljava/lang/Runtime;", // Calls System.getProperty -> OsConstants.initConstants. "Ljava/lang/System;", // Calls OsConstants.initConstants. "Ljava/math/BigDecimal;", // Calls native ... -> java.math.NativeBN.BN_new(). "Ljava/math/BigInteger;", // Calls native ... -> java.math.NativeBN.BN_new(). + "Ljava/math/Primality;", // Calls native ... -> java.math.NativeBN.BN_new(). "Ljava/math/Multiplication;", // Calls native ... -> java.math.NativeBN.BN_new(). "Ljava/net/InetAddress;", // Requires libcore.io.OsConstants. "Ljava/net/Inet4Address;", // Sub-class of InetAddress. @@ -1416,23 +1850,58 @@ static const char* class_initializer_black_list[] = { "Ljava/nio/charset/Charset;", // Calls Charset.getDefaultCharset -> System.getProperty -> OsConstants.initConstants. "Ljava/nio/charset/CharsetICU;", // Sub-class of Charset. "Ljava/nio/charset/Charsets;", // Calls Charset.forName. + "Ljava/nio/charset/StandardCharsets;", // Calls OsConstants.initConstants. + "Ljava/security/AlgorithmParameterGenerator;", // Calls OsConstants.initConstants. + "Ljava/security/KeyPairGenerator$KeyPairGeneratorImpl;", // Calls OsConstants.initConstants. "Ljava/security/KeyPairGenerator;", // Calls OsConstants.initConstants. "Ljava/security/Security;", // Tries to do disk IO for "security.properties". + "Ljava/security/spec/RSAKeyGenParameterSpec;", // java.math.NativeBN.BN_new() "Ljava/sql/Date;", // Calls OsConstants.initConstants. + "Ljava/sql/DriverManager;", // Calls OsConstants.initConstants. + "Ljava/sql/Time;", // Calls OsConstants.initConstants. + "Ljava/sql/Timestamp;", // Calls OsConstants.initConstants. "Ljava/util/Date;", // Calls Date.<init> -> System.currentTimeMillis -> OsConstants.initConstants. + "Ljava/util/ListResourceBundle;", // Calls OsConstants.initConstants. "Ljava/util/Locale;", // Calls System.getProperty -> OsConstants.initConstants. + "Ljava/util/PropertyResourceBundle;", // Calls OsConstants.initConstants. + "Ljava/util/ResourceBundle;", // Calls OsConstants.initConstants. + "Ljava/util/ResourceBundle$MissingBundle;", // Calls OsConstants.initConstants. + "Ljava/util/Scanner;", // regex.Pattern.compileImpl. "Ljava/util/SimpleTimeZone;", // Sub-class of TimeZone. "Ljava/util/TimeZone;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. "Ljava/util/concurrent/ConcurrentHashMap$Segment;", // Calls Runtime.getRuntime().availableProcessors(). + "Ljava/util/concurrent/ConcurrentSkipListMap;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/Exchanger;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/ForkJoinPool;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/LinkedTransferQueue;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/Phaser;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/ScheduledThreadPoolExecutor;", // Calls AtomicLong.VMSupportsCS8() + "Ljava/util/concurrent/SynchronousQueue;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/atomic/AtomicLong;", // Calls AtomicLong.VMSupportsCS8() "Ljava/util/logging/LogManager;", // Calls System.getProperty -> OsConstants.initConstants. + "Ljava/util/prefs/AbstractPreferences;", // Calls OsConstants.initConstants. + "Ljava/util/prefs/FilePreferencesImpl;", // Calls OsConstants.initConstants. + "Ljava/util/prefs/FilePreferencesFactoryImpl;", // Calls OsConstants.initConstants. + "Ljava/util/prefs/Preferences;", // Calls OsConstants.initConstants. + "Ljavax/crypto/KeyAgreement;", // Calls OsConstants.initConstants. + "Ljavax/crypto/KeyGenerator;", // Calls OsConstants.initConstants. + "Ljavax/security/cert/X509Certificate;", // Calls VMClassLoader.getBootClassPathSize. + "Ljavax/security/cert/X509Certificate$1;", // Calls VMClassLoader.getBootClassPathSize. "Ljavax/microedition/khronos/egl/EGL10;", // Requires EGLContext. "Ljavax/microedition/khronos/egl/EGLContext;", // Requires com.google.android.gles_jni.EGLImpl. "Ljavax/net/ssl/HttpsURLConnection;", // Calls SSLSocketFactory.getDefault -> java.security.Security.getProperty. + "Ljavax/xml/datatype/DatatypeConstants;", // Calls OsConstants.initConstants. + "Ljavax/xml/datatype/FactoryFinder;", // Calls OsConstants.initConstants. + "Ljavax/xml/namespace/QName;", // Calls OsConstants.initConstants. + "Ljavax/xml/validation/SchemaFactoryFinder;", // Calls OsConstants.initConstants. + "Ljavax/xml/xpath/XPathConstants;", // Calls OsConstants.initConstants. + "Ljavax/xml/xpath/XPathFactoryFinder;", // Calls OsConstants.initConstants. "Llibcore/icu/LocaleData;", // Requires java.util.Locale. "Llibcore/icu/TimeZoneNames;", // Requires java.util.TimeZone. "Llibcore/io/IoUtils;", // Calls Random.<init> -> System.currentTimeMillis -> FileDescriptor -> OsConstants.initConstants. "Llibcore/io/OsConstants;", // Platform specific. "Llibcore/net/MimeUtils;", // Calls libcore.net.MimeUtils.getContentTypesPropertiesStream -> System.getProperty. + "Llibcore/reflect/Types;", // Calls OsConstants.initConstants. "Llibcore/util/ZoneInfo;", // Sub-class of TimeZone. "Llibcore/util/ZoneInfoDB;", // Calls System.getenv -> OsConstants.initConstants. "Lorg/apache/commons/logging/LogFactory;", // Calls System.getProperty. @@ -1440,17 +1909,40 @@ static const char* class_initializer_black_list[] = { "Lorg/apache/harmony/security/provider/cert/X509CertFactoryImpl;", // Requires java.nio.charsets.Charsets. "Lorg/apache/harmony/security/provider/crypto/RandomBitsSupplier;", // Requires java.io.File. "Lorg/apache/harmony/security/utils/AlgNameMapper;", // Requires java.util.Locale. + "Lorg/apache/harmony/security/pkcs10/CertificationRequest;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs10/CertificationRequestInfo;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs7/AuthenticatedAttributes;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs7/SignedData;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs7/SignerInfo;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs8/PrivateKeyInfo;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl;", // Calls OsConstants.initConstants. "Lorg/apache/harmony/security/x501/AttributeTypeAndValue;", // Calls IntegralToString.convertInt -> Thread.currentThread. "Lorg/apache/harmony/security/x501/DirectoryString;", // Requires BigInteger. "Lorg/apache/harmony/security/x501/Name;", // Requires org.apache.harmony.security.x501.AttributeTypeAndValue. + "Lorg/apache/harmony/security/x509/AccessDescription;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/AuthorityKeyIdentifier;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/CRLDistributionPoints;", // Calls Thread.currentThread. "Lorg/apache/harmony/security/x509/Certificate;", // Requires org.apache.harmony.security.x509.TBSCertificate. - "Lorg/apache/harmony/security/x509/TBSCertificate;", // Requires org.apache.harmony.security.x501.Name. + "Lorg/apache/harmony/security/x509/CertificateIssuer;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/CertificateList;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/DistributionPoint;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/DistributionPointName;", // Calls Thread.currentThread. "Lorg/apache/harmony/security/x509/EDIPartyName;", // Calls native ... -> java.math.NativeBN.BN_new(). "Lorg/apache/harmony/security/x509/GeneralName;", // Requires org.apache.harmony.security.x501.Name. "Lorg/apache/harmony/security/x509/GeneralNames;", // Requires GeneralName. + "Lorg/apache/harmony/security/x509/GeneralSubtree;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/GeneralSubtrees;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/InfoAccessSyntax;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/IssuingDistributionPoint;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/NameConstraints;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/TBSCertList$RevokedCertificate;", // Calls NativeBN.BN_new(). + "Lorg/apache/harmony/security/x509/TBSCertList;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/TBSCertificate;", // Requires org.apache.harmony.security.x501.Name. "Lorg/apache/harmony/security/x509/Time;", // Calls native ... -> java.math.NativeBN.BN_new(). "Lorg/apache/harmony/security/x509/Validity;", // Requires x509.Time. + "Lorg/apache/harmony/security/x509/tsp/TSTInfo;", // Calls Thread.currentThread. "Lorg/apache/harmony/xml/ExpatParser;", // Calls native ExpatParser.staticInitialize. + "Lorg/apache/harmony/xml/ExpatParser$EntityParser;", // Calls ExpatParser.staticInitialize. "Lorg/apache/http/conn/params/ConnRouteParams;", // Requires java.util.Locale. "Lorg/apache/http/conn/ssl/SSLSocketFactory;", // Calls java.security.Security.getProperty. "Lorg/apache/http/conn/util/InetAddressUtils;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. @@ -1463,7 +1955,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()); const char* descriptor = manager->GetDexFile()->GetClassDescriptor(class_def); mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader); - bool compiling_boot = Runtime::Current()->GetHeap()->GetSpaces().size() == 1; + bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1; bool can_init_static_fields = compiling_boot && manager->GetCompiler()->IsImageClass(descriptor); if (klass != NULL) { @@ -1473,13 +1965,17 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl // on a second thread the sub-class is initialized (holding its lock) after first initializing // its parents, whose locks are acquired. This leads to a parent-to-child and a child-to-parent // lock ordering and consequent potential deadlock. - static Mutex lock1("Initializer lock", kMonitorLock); - MutexLock mu(soa.Self(), lock1); + // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather + // than use a special Object for the purpose we use the Class of java.lang.Class. + ObjectLock lock1(soa.Self(), klass->GetClass()); // The lock required to initialize the class. ObjectLock lock2(soa.Self(), klass); // Only try to initialize classes that were successfully verified. if (klass->IsVerified()) { manager->GetClassLinker()->EnsureInitialized(klass, false, can_init_static_fields); + if (soa.Self()->IsExceptionPending()) { + soa.Self()->GetException(NULL)->Dump(); + } if (!klass->IsInitialized()) { if (can_init_static_fields) { bool is_black_listed = false; @@ -1517,7 +2013,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl compiled_class = new CompiledClass(status); manager->GetCompiler()->RecordClassStatus(ref, compiled_class); } else { - DCHECK_EQ(status, compiled_class->GetStatus()); + DCHECK_GE(status, compiled_class->GetStatus()) << descriptor; } } // Clear any class not found or verification exceptions. @@ -1558,12 +2054,12 @@ void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFi } void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, size_t class_def_index) { - jobject class_loader = manager->GetClassLoader(); + jobject jclass_loader = manager->GetClassLoader(); const DexFile& dex_file = *manager->GetDexFile(); const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); { ScopedObjectAccess soa(Thread::Current()); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()); + mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); if (SkipClass(class_loader, dex_file, class_def)) { return; } @@ -1578,6 +2074,13 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz // empty class, probably a marker interface return; } + // Can we run DEX-to-DEX compiler on this class ? + bool allow_dex_compilation; + { + ScopedObjectAccess soa(Thread::Current()); + mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); + allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, dex_file, class_def); + } ClassDataItemIterator it(dex_file, class_data); // Skip fields while (it.HasNextStaticField()) { @@ -1599,7 +2102,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz previous_direct_method_idx = method_idx; manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMethodInvokeType(class_def), class_def_index, - method_idx, class_loader, dex_file); + method_idx, jclass_loader, dex_file, allow_dex_compilation); it.Next(); } // Compile virtual methods @@ -1615,7 +2118,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz previous_virtual_method_idx = method_idx; manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMethodInvokeType(class_def), class_def_index, - method_idx, class_loader, dex_file); + method_idx, jclass_loader, dex_file, allow_dex_compilation); it.Next(); } DCHECK(!it.HasNext()); @@ -1631,7 +2134,8 @@ void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_fil void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, - const DexFile& dex_file) { + const DexFile& dex_file, + bool allow_dex_to_dex_compilation) { CompiledMethod* compiled_method = NULL; uint64_t start_ns = NanoTime(); @@ -1641,7 +2145,8 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t } else if ((access_flags & kAccAbstract) != 0) { } else { // In small mode we only compile image classes. - bool dont_compile = Runtime::Current()->IsSmallMode() && ((image_classes_ == NULL) || (image_classes_->size() == 0)); + bool dont_compile = (Runtime::Current()->IsSmallMode() && + ((image_classes_.get() == NULL) || (image_classes_->size() == 0))); // Don't compile class initializers, ever. if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { @@ -1650,11 +2155,30 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t // Do compile small methods. dont_compile = false; } - if (!dont_compile) { - compiled_method = (*compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, + bool use_sea = false; + + if (Runtime::Current()->IsSeaIRMode()) { + use_sea = true; + } + if (use_sea) { + use_sea = (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci")); + } + if (!use_sea) { + compiled_method = (*compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file); + } else { + compiled_method = (*sea_ir_compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, + method_idx, class_loader, dex_file); + } CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file); + } else if (allow_dex_to_dex_compilation) { + // TODO: add a mode to disable DEX-to-DEX compilation ? + compiled_method = (*dex_to_dex_compiler_)(*this, code_item, access_flags, + invoke_type, class_def_idx, + method_idx, class_loader, dex_file); + // No native code is generated. + CHECK(compiled_method == NULL) << PrettyMethod(method_idx, dex_file); } } uint64_t duration_ns = NanoTime() - start_ns; diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index 75d276d5b1..b37b74b042 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -39,7 +39,6 @@ class ParallelCompilationManager; class DexCompilationUnit; class TimingLogger; -const uint32_t kDexPCNotReady = 0xFFFFFF; enum CompilerBackend { kQuick, kPortable, @@ -62,14 +61,16 @@ class CompilerTls { class CompilerDriver { public: + typedef std::set<std::string> DescriptorSet; + // Create a compiler targeting the requested "instruction_set". // "image" should be true if image specific optimizations should be // enabled. "image_classes" lets the compiler know what classes it // can assume will be in the image, with NULL implying all available // classes. - explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, bool image, + explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, + bool image, DescriptorSet* image_classes, size_t thread_count, bool support_debugging, - const std::set<std::string>* image_classes, bool dump_stats, bool dump_timings); ~CompilerDriver(); @@ -97,8 +98,22 @@ class CompilerDriver { return image_; } + DescriptorSet* GetImageClasses() const { + return image_classes_.get(); + } + CompilerTls* GetTls(); + // Generate the trampolines that are invoked by unresolved direct methods. + const std::vector<uint8_t>* CreatePortableResolutionTrampoline() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const std::vector<uint8_t>* CreateInterpreterToQuickEntry() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // A class is uniquely located by its DexFile and the class_defs_ table index into that DexFile typedef std::pair<const DexFile*, uint32_t> ClassReference; @@ -106,7 +121,22 @@ class CompilerDriver { LOCKS_EXCLUDED(compiled_classes_lock_); // A method is uniquely located by its DexFile and the method_ids_ table index into that DexFile - typedef std::pair<const DexFile*, uint32_t> MethodReference; + struct MethodReference { + MethodReference(const DexFile* file, uint32_t index) : dex_file(file), dex_method_index(index) { + } + const DexFile* dex_file; + uint32_t dex_method_index; + }; + + struct MethodReferenceComparator { + bool operator()(MethodReference mr1, MethodReference mr2) const { + if (mr1.dex_file == mr2.dex_file) { + return mr1.dex_method_index < mr2.dex_method_index; + } else { + return mr1.dex_file < mr2.dex_file; + } + } + }; CompiledMethod* GetCompiledMethod(MethodReference ref) const LOCKS_EXCLUDED(compiled_methods_lock_); @@ -124,7 +154,9 @@ class CompilerDriver { // Are runtime access checks necessary in the compiled code? bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file, - uint32_t type_idx) + uint32_t type_idx, bool* type_known_final = NULL, + bool* type_known_abstract = NULL, + bool* equals_referrers_class = NULL) LOCKS_EXCLUDED(Locks::mutator_lock_); // Are runtime access and instantiable checks necessary in the code? @@ -146,11 +178,13 @@ class CompilerDriver { // Can we fastpath a interface, super class or virtual method call? Computes method's vtable // index. - bool ComputeInvokeInfo(uint32_t method_idx, uint32_t dex_pc, - const DexCompilationUnit* mUnit, InvokeType& type, int& vtable_idx, - uintptr_t& direct_code, uintptr_t& direct_method) + bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc, + InvokeType& type, MethodReference& target_method, int& vtable_idx, + uintptr_t& direct_code, uintptr_t& direct_method, bool update_stats) LOCKS_EXCLUDED(Locks::mutator_lock_); + bool IsSafeCast(const MethodReference& mr, uint32_t dex_pc); + // Record patch information for later fix up. void AddCodePatch(const DexFile* dex_file, uint32_t referrer_method_idx, @@ -169,6 +203,15 @@ class CompilerDriver { void SetBitcodeFileName(std::string const& filename); + bool GetSupportBootImageFixup() const { + return support_boot_image_fixup_; + } + + void SetSupportBootImageFixup(bool support_boot_image_fixup) { + support_boot_image_fixup_ = support_boot_image_fixup; + } + + // TODO: remove these Elf wrappers when libart links against LLVM (when separate compiler library is gone) bool WriteElf(const std::string& android_root, bool is_host, @@ -253,7 +296,7 @@ class CompilerDriver { } // Checks if class specified by type_idx is one of the image_classes_ - bool IsImageClass(const std::string& descriptor) const; + bool IsImageClass(const char* descriptor) const; void RecordClassStatus(ClassReference ref, CompiledClass* compiled_class); @@ -262,13 +305,16 @@ class CompilerDriver { void GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type, mirror::Class* referrer_class, mirror::AbstractMethod* method, - uintptr_t& direct_code, uintptr_t& direct_method) + uintptr_t& direct_code, uintptr_t& direct_method, + bool update_stats) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); + void LoadImageClasses(TimingLogger& timings); + // Attempt to resolve all type, methods, fields, and strings // referenced from code in the dex file following PathClassLoader // ordering semantics. @@ -292,6 +338,10 @@ class CompilerDriver { ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_, compiled_classes_lock_); + void UpdateImageClasses(TimingLogger& timings); + static void FindClinitImageClassesCallback(mirror::Object* object, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files, ThreadPool& thread_pool, TimingLogger& timings); void CompileDexFile(jobject class_loader, const DexFile& dex_file, @@ -299,7 +349,8 @@ class CompilerDriver { LOCKS_EXCLUDED(Locks::mutator_lock_); void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, - jobject class_loader, const DexFile& dex_file) + jobject class_loader, const DexFile& dex_file, + bool allow_dex_to_dex_compilation) LOCKS_EXCLUDED(compiled_methods_lock_); static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index) @@ -321,12 +372,18 @@ class CompilerDriver { mutable Mutex compiled_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; ClassTable compiled_classes_ GUARDED_BY(compiled_classes_lock_); - typedef SafeMap<const MethodReference, CompiledMethod*> MethodTable; + typedef SafeMap<const MethodReference, CompiledMethod*, MethodReferenceComparator> MethodTable; // All method references that this compiler has compiled. mutable Mutex compiled_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; MethodTable compiled_methods_ GUARDED_BY(compiled_methods_lock_); - bool image_; + const bool image_; + + // If image_ is true, specifies the classes that will be included in + // the image. Note if image_classes_ is NULL, all classes are + // included in the image. + UniquePtr<DescriptorSet> image_classes_; + size_t thread_count_; bool support_debugging_; uint64_t start_ns_; @@ -336,8 +393,6 @@ class CompilerDriver { bool dump_stats_; bool dump_timings_; - const std::set<std::string>* image_classes_; - typedef void (*CompilerCallbackFn)(CompilerDriver& driver); typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver); @@ -349,6 +404,9 @@ class CompilerDriver { uint32_t class_dex_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file); CompilerFn compiler_; + CompilerFn sea_ir_compiler_; + + CompilerFn dex_to_dex_compiler_; void* compiler_context_; @@ -366,6 +424,8 @@ class CompilerDriver { (const CompilerDriver& driver, const CompiledMethod* cm, const mirror::AbstractMethod* method); CompilerGetMethodCodeAddrFn compiler_get_method_code_addr_; + bool support_boot_image_fixup_; + DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; diff --git a/src/compiler/driver/compiler_driver_test.cc b/src/compiler/driver/compiler_driver_test.cc index c87fefda16..abf8a9a3f7 100644 --- a/src/compiler/driver/compiler_driver_test.cc +++ b/src/compiler/driver/compiler_driver_test.cc @@ -23,10 +23,10 @@ #include "class_linker.h" #include "common_test.h" #include "dex_file.h" -#include "heap.h" +#include "gc/heap.h" #include "mirror/class.h" #include "mirror/class-inl.h" -#include "mirror/dex_cache.h" +#include "mirror/dex_cache-inl.h" #include "mirror/abstract_method-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" diff --git a/src/compiler/driver/dex_compilation_unit.cc b/src/compiler/driver/dex_compilation_unit.cc index 962df42a21..c7a4df6ea4 100644 --- a/src/compiler/driver/dex_compilation_unit.cc +++ b/src/compiler/driver/dex_compilation_unit.cc @@ -31,18 +31,17 @@ DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu) code_item_(cu->code_item), class_def_idx_(cu->class_def_idx), dex_method_idx_(cu->method_idx), - access_flags_(cu->access_flags), - symbol_(StringPrintf("dex_%s", MangleForJni(PrettyMethod(dex_method_idx_, *dex_file_)).c_str())) { + access_flags_(cu->access_flags) { } -DexCompilationUnit:: DexCompilationUnit(CompilationUnit* cu, - jobject class_loader, - ClassLinker* class_linker, - const DexFile& dex_file, - const DexFile::CodeItem* code_item, - uint32_t class_def_idx, - uint32_t method_idx, - uint32_t access_flags) +DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu, + jobject class_loader, + ClassLinker* class_linker, + const DexFile& dex_file, + const DexFile::CodeItem* code_item, + uint32_t class_def_idx, + uint32_t method_idx, + uint32_t access_flags) : cu_(cu), class_loader_(class_loader), class_linker_(class_linker), @@ -50,8 +49,15 @@ DexCompilationUnit:: DexCompilationUnit(CompilationUnit* cu, code_item_(code_item), class_def_idx_(class_def_idx), dex_method_idx_(method_idx), - access_flags_(access_flags), - symbol_(StringPrintf("dex_%s", MangleForJni(PrettyMethod(dex_method_idx_, *dex_file_)).c_str())) { + access_flags_(access_flags) { +} + +const std::string& DexCompilationUnit::GetSymbol() { + if (symbol_.empty()) { + symbol_ = "dex_"; + symbol_ += MangleForJni(PrettyMethod(dex_method_idx_, *dex_file_)); + } + return symbol_; } } // namespace art diff --git a/src/compiler/driver/dex_compilation_unit.h b/src/compiler/driver/dex_compilation_unit.h index 0b90aaafdf..3c6129d642 100644 --- a/src/compiler/driver/dex_compilation_unit.h +++ b/src/compiler/driver/dex_compilation_unit.h @@ -92,9 +92,7 @@ class DexCompilationUnit { return ((access_flags_ & kAccSynchronized) != 0); } - const std::string& GetSymbol() const { - return symbol_; - } + const std::string& GetSymbol(); private: CompilationUnit* const cu_; @@ -110,7 +108,7 @@ class DexCompilationUnit { const uint32_t dex_method_idx_; const uint32_t access_flags_; - const std::string symbol_; + std::string symbol_; }; } // namespace art diff --git a/src/compiler/llvm/gbc_expander.cc b/src/compiler/llvm/gbc_expander.cc index 99c8fd5ca7..bdf9aca68f 100644 --- a/src/compiler/llvm/gbc_expander.cc +++ b/src/compiler/llvm/gbc_expander.cc @@ -776,7 +776,8 @@ llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) { art::InvokeType invoke_type = static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0))); bool is_static = (invoke_type == art::kStatic); - uint32_t callee_method_idx = LV2UInt(call_inst.getArgOperand(1)); + art::CompilerDriver::MethodReference target_method(dex_compilation_unit_->GetDexFile(), + LV2UInt(call_inst.getArgOperand(1))); // Load *this* actual parameter llvm::Value* this_addr = (!is_static) ? call_inst.getArgOperand(3) : NULL; @@ -785,18 +786,17 @@ llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) { int vtable_idx = -1; uintptr_t direct_code = 0; uintptr_t direct_method = 0; - // TODO: pass actual value of dex PC (instead of kDexPCNotready) needed by verifier based - // sharpening after LLVM re-factoring is finished. - bool is_fast_path = driver_-> - ComputeInvokeInfo(callee_method_idx, art::kDexPCNotReady, dex_compilation_unit_, - invoke_type, vtable_idx, direct_code, direct_method); - + bool is_fast_path = driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc, + invoke_type, target_method, + vtable_idx, + direct_code, direct_method, + true); // Load the method object llvm::Value* callee_method_object_addr = NULL; if (!is_fast_path) { callee_method_object_addr = - EmitCallRuntimeForCalleeMethodObjectAddr(callee_method_idx, invoke_type, + EmitCallRuntimeForCalleeMethodObjectAddr(target_method.dex_method_index, invoke_type, this_addr, dex_pc, is_fast_path); } else { switch (invoke_type) { @@ -809,7 +809,7 @@ llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) { irb_.getJObjectTy()); } else { callee_method_object_addr = - EmitLoadSDCalleeMethodObjectAddr(callee_method_idx); + EmitLoadSDCalleeMethodObjectAddr(target_method.dex_method_index); } break; @@ -826,7 +826,7 @@ llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) { case art::kInterface: callee_method_object_addr = - EmitCallRuntimeForCalleeMethodObjectAddr(callee_method_idx, + EmitCallRuntimeForCalleeMethodObjectAddr(target_method.dex_method_index, invoke_type, this_addr, dex_pc, is_fast_path); break; @@ -844,7 +844,7 @@ llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) { llvm::Value* code_addr; llvm::Type* func_type = GetFunctionType(call_inst.getType(), - callee_method_idx, is_static); + target_method.dex_method_index, is_static); if (direct_code != 0u && direct_code != static_cast<uintptr_t>(-1)) { code_addr = irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_code), diff --git a/src/compiler/llvm/llvm_compilation_unit.h b/src/compiler/llvm/llvm_compilation_unit.h index d96e778912..857d924840 100644 --- a/src/compiler/llvm/llvm_compilation_unit.h +++ b/src/compiler/llvm/llvm_compilation_unit.h @@ -81,10 +81,10 @@ class LlvmCompilationUnit { void SetCompilerDriver(CompilerDriver* driver) { driver_ = driver; } - const DexCompilationUnit* GetDexCompilationUnit() { + DexCompilationUnit* GetDexCompilationUnit() { return dex_compilation_unit_; } - void SetDexCompilationUnit(const DexCompilationUnit* dex_compilation_unit) { + void SetDexCompilationUnit(DexCompilationUnit* dex_compilation_unit) { dex_compilation_unit_ = dex_compilation_unit; } @@ -113,7 +113,7 @@ class LlvmCompilationUnit { UniquePtr<IntrinsicHelper> intrinsic_helper_; UniquePtr<LLVMInfo> llvm_info_; CompilerDriver* driver_; - const DexCompilationUnit* dex_compilation_unit_; + DexCompilationUnit* dex_compilation_unit_; std::string bitcode_filename_; diff --git a/src/compiler/llvm/runtime_support_builder.cc b/src/compiler/llvm/runtime_support_builder.cc index b4ddb55483..2be2ddf36f 100644 --- a/src/compiler/llvm/runtime_support_builder.cc +++ b/src/compiler/llvm/runtime_support_builder.cc @@ -16,7 +16,7 @@ #include "runtime_support_builder.h" -#include "gc/card_table.h" +#include "gc/accounting/card_table.h" #include "ir_builder.h" #include "monitor.h" #include "mirror/object.h" @@ -266,9 +266,11 @@ void RuntimeSupportBuilder::EmitMarkGCCard(::llvm::Value* value, ::llvm::Value* irb_.getInt8Ty()->getPointerTo(), kTBAAConstJObject); Value* target_addr_int = irb_.CreatePtrToInt(target_addr, irb_.getPtrEquivIntTy()); - Value* card_no = irb_.CreateLShr(target_addr_int, irb_.getPtrEquivInt(CardTable::kCardShift)); + Value* card_no = irb_.CreateLShr(target_addr_int, + irb_.getPtrEquivInt(gc::accounting::CardTable::kCardShift)); Value* card_table_entry = irb_.CreateGEP(card_table, card_no); - irb_.CreateStore(irb_.getInt8(CardTable::kCardDirty), card_table_entry, kTBAARuntimeInfo); + irb_.CreateStore(irb_.getInt8(gc::accounting::CardTable::kCardDirty), card_table_entry, + kTBAARuntimeInfo); irb_.CreateBr(bb_cont); irb_.SetInsertPoint(bb_cont); diff --git a/src/compiler/llvm/runtime_support_llvm.cc b/src/compiler/llvm/runtime_support_llvm.cc index bd6b01b211..bff13f9dc9 100644 --- a/src/compiler/llvm/runtime_support_llvm.cc +++ b/src/compiler/llvm/runtime_support_llvm.cc @@ -24,6 +24,7 @@ #include "dex_instruction.h" #include "mirror/abstract_method-inl.h" #include "mirror/class-inl.h" +#include "mirror/dex_cache-inl.h" #include "mirror/field-inl.h" #include "mirror/object.h" #include "mirror/object-inl.h" @@ -176,7 +177,7 @@ bool art_portable_is_exception_pending_from_code() { } void art_portable_throw_div_zero_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ThrowArithmeticExceptionDivideByZero(Thread::Current()); + ThrowArithmeticExceptionDivideByZero(); } void art_portable_throw_array_bounds_from_code(int32_t index, int32_t length) @@ -435,7 +436,7 @@ int32_t art_portable_set32_static_from_code(uint32_t field_idx, mirror::Abstract return 0; } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveWrite, sizeof(uint32_t)); + StaticPrimitiveWrite, sizeof(uint32_t), true); if (LIKELY(field != NULL)) { field->Set32(field->GetDeclaringClass(), new_value); return 0; @@ -451,7 +452,7 @@ int32_t art_portable_set64_static_from_code(uint32_t field_idx, mirror::Abstract return 0; } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveWrite, sizeof(uint64_t)); + StaticPrimitiveWrite, sizeof(uint64_t), true); if (LIKELY(field != NULL)) { field->Set64(field->GetDeclaringClass(), new_value); return 0; @@ -467,7 +468,7 @@ int32_t art_portable_set_obj_static_from_code(uint32_t field_idx, mirror::Abstra return 0; } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticObjectWrite, sizeof(mirror::Object*)); + StaticObjectWrite, sizeof(mirror::Object*), true); if (LIKELY(field != NULL)) { field->SetObj(field->GetDeclaringClass(), new_value); return 0; @@ -482,7 +483,7 @@ int32_t art_portable_get32_static_from_code(uint32_t field_idx, mirror::Abstract return field->Get32(field->GetDeclaringClass()); } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveRead, sizeof(uint32_t)); + StaticPrimitiveRead, sizeof(uint32_t), true); if (LIKELY(field != NULL)) { return field->Get32(field->GetDeclaringClass()); } @@ -496,7 +497,7 @@ int64_t art_portable_get64_static_from_code(uint32_t field_idx, mirror::Abstract return field->Get64(field->GetDeclaringClass()); } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveRead, sizeof(uint64_t)); + StaticPrimitiveRead, sizeof(uint64_t), true); if (LIKELY(field != NULL)) { return field->Get64(field->GetDeclaringClass()); } @@ -510,7 +511,7 @@ mirror::Object* art_portable_get_obj_static_from_code(uint32_t field_idx, mirror return field->GetObj(field->GetDeclaringClass()); } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticObjectRead, sizeof(mirror::Object*)); + StaticObjectRead, sizeof(mirror::Object*), true); if (LIKELY(field != NULL)) { return field->GetObj(field->GetDeclaringClass()); } @@ -526,7 +527,7 @@ int32_t art_portable_set32_instance_from_code(uint32_t field_idx, mirror::Abstra return 0; } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveWrite, sizeof(uint32_t)); + InstancePrimitiveWrite, sizeof(uint32_t), true); if (LIKELY(field != NULL)) { field->Set32(obj, new_value); return 0; @@ -543,7 +544,7 @@ int32_t art_portable_set64_instance_from_code(uint32_t field_idx, mirror::Abstra return 0; } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveWrite, sizeof(uint64_t)); + InstancePrimitiveWrite, sizeof(uint64_t), true); if (LIKELY(field != NULL)) { field->Set64(obj, new_value); return 0; @@ -560,7 +561,7 @@ int32_t art_portable_set_obj_instance_from_code(uint32_t field_idx, mirror::Abst return 0; } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstanceObjectWrite, sizeof(mirror::Object*)); + InstanceObjectWrite, sizeof(mirror::Object*), true); if (LIKELY(field != NULL)) { field->SetObj(obj, new_value); return 0; @@ -575,7 +576,7 @@ int32_t art_portable_get32_instance_from_code(uint32_t field_idx, mirror::Abstra return field->Get32(obj); } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveRead, sizeof(uint32_t)); + InstancePrimitiveRead, sizeof(uint32_t), true); if (LIKELY(field != NULL)) { return field->Get32(obj); } @@ -589,7 +590,7 @@ int64_t art_portable_get64_instance_from_code(uint32_t field_idx, mirror::Abstra return field->Get64(obj); } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveRead, sizeof(uint64_t)); + InstancePrimitiveRead, sizeof(uint64_t), true); if (LIKELY(field != NULL)) { return field->Get64(obj); } @@ -603,7 +604,7 @@ mirror::Object* art_portable_get_obj_instance_from_code(uint32_t field_idx, mirr return field->GetObj(obj); } field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstanceObjectRead, sizeof(mirror::Object*)); + InstanceObjectRead, sizeof(mirror::Object*), true); if (LIKELY(field != NULL)) { return field->GetObj(obj); } diff --git a/src/compiler/sea_ir/frontend.cc b/src/compiler/sea_ir/frontend.cc new file mode 100644 index 0000000000..d4e1c7e740 --- /dev/null +++ b/src/compiler/sea_ir/frontend.cc @@ -0,0 +1,81 @@ + +#include <llvm/Support/Threading.h> + +#include "compiler/driver/compiler_driver.h" + + +#include "compiler/llvm/llvm_compilation_unit.h" +#include "compiler/dex/portable/mir_to_gbc.h" + +#include "leb128.h" +#include "mirror/object.h" +#include "runtime.h" +#include "base/logging.h" + +#ifdef ART_SEA_IR_MODE +#include "compiler/sea_ir/sea.h" +#endif + + + + +#ifdef ART_SEA_IR_MODE +#include "compiler/sea_ir/sea.h" +namespace art { + +static CompiledMethod* CompileMethodWithSeaIr(CompilerDriver& compiler, + const CompilerBackend compiler_backend, + const DexFile::CodeItem* code_item, + uint32_t access_flags, InvokeType invoke_type, + uint32_t class_def_idx, uint32_t method_idx, + jobject class_loader, const DexFile& dex_file +#if defined(ART_USE_PORTABLE_COMPILER) + , llvm::LlvmCompilationUnit* llvm_compilation_unit +#endif +) +{ + VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "..."; + sea_ir::SeaGraph* sg = sea_ir::SeaGraph::GetCurrentGraph(); + sg->CompileMethod(code_item, class_def_idx, method_idx, dex_file); + sg->DumpSea("/tmp/temp.dot"); + CHECK(0 && "No SEA compiled function exists yet."); + return NULL; +} + + +CompiledMethod* SeaIrCompileOneMethod(CompilerDriver& compiler, + const CompilerBackend backend, + const DexFile::CodeItem* code_item, + uint32_t access_flags, + InvokeType invoke_type, + uint32_t class_def_idx, + uint32_t method_idx, + jobject class_loader, + const DexFile& dex_file, + llvm::LlvmCompilationUnit* llvm_compilation_unit) +{ + return CompileMethodWithSeaIr(compiler, backend, code_item, access_flags, invoke_type, class_def_idx, + method_idx, class_loader, dex_file +#if defined(ART_USE_PORTABLE_COMPILER) + , llvm_compilation_unit +#endif + + ); +} + +extern "C" art::CompiledMethod* + SeaIrCompileMethod(art::CompilerDriver& compiler, + const art::DexFile::CodeItem* code_item, + uint32_t access_flags, art::InvokeType invoke_type, + uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, + const art::DexFile& dex_file) +{ + // TODO: check method fingerprint here to determine appropriate backend type. Until then, use build default + art::CompilerBackend backend = compiler.GetCompilerBackend(); + return art::SeaIrCompileOneMethod(compiler, backend, code_item, access_flags, invoke_type, + class_def_idx, method_idx, class_loader, dex_file, + NULL /* use thread llvm_info */); +} +#endif + +} // end namespace art diff --git a/src/compiler/sea_ir/sea.cc b/src/compiler/sea_ir/sea.cc new file mode 100644 index 0000000000..e08558fa2d --- /dev/null +++ b/src/compiler/sea_ir/sea.cc @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "compiler/sea_ir/sea.h" +#include "file_output_stream.h" + + + +namespace sea_ir { + + +SeaGraph SeaGraph::graph_; +int SeaNode::current_max_node_id_ = 0; + +SeaGraph* SeaGraph::GetCurrentGraph() { + return &sea_ir::SeaGraph::graph_; +} + +void SeaGraph::DumpSea(std::string filename) const { + std::string result; + result += "digraph seaOfNodes {\n"; + for(std::vector<Region*>::const_iterator cit = regions_.begin(); cit != regions_.end(); cit++) { + result += (*cit)->ToDot(); + } + result += "}\n"; + art::File* file = art::OS::OpenFile(filename.c_str(), true, true); + art::FileOutputStream fos(file); + fos.WriteFully(result.c_str(), result.size()); + LOG(INFO) << "Written SEA string to file..."; +} + +void SeaGraph::CompileMethod(const art::DexFile::CodeItem* code_item, + uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file) { + const uint16_t* code = code_item->insns_; + const size_t size_in_code_units = code_item->insns_size_in_code_units_; + + Region* r = NULL; + // This maps target instruction pointers to their corresponding region objects. + std::map<const uint16_t*, Region*> target_regions; + size_t i = 0; + + // Pass 1: Find the start instruction of basic blocks, as targets and flow-though of branches. + while (i < size_in_code_units) { + const art::Instruction* inst = art::Instruction::At(&code[i]); + if (inst->IsBranch()||inst->IsUnconditional()) { + int32_t offset = inst->GetTargetOffset(); + if (target_regions.end() == target_regions.find(&code[i+offset])) { + Region* region = GetNewRegion(); + target_regions.insert(std::pair<const uint16_t*, Region*>(&code[i+offset], region)); + } + if (inst->IsFlowthrough() && + (target_regions.end() == target_regions.find(&code[i+inst->SizeInCodeUnits()]))) { + Region* region = GetNewRegion(); + target_regions.insert(std::pair<const uint16_t*, Region*>(&code[i+inst->SizeInCodeUnits()], region)); + } + } + i += inst->SizeInCodeUnits(); + } + + + // Pass 2: Assign instructions to region nodes and + // assign branches their control flow successors. + i = 0; + r = GetNewRegion(); + sea_ir::SeaNode* last_node = NULL; + sea_ir::SeaNode* node = NULL; + while (i < size_in_code_units) { + const art::Instruction* inst = art::Instruction::At(&code[i]); //TODO: find workaround for this + last_node = node; + node = new sea_ir::SeaNode(inst); + + if (inst->IsBranch() || inst->IsUnconditional()) { + int32_t offset = inst->GetTargetOffset(); + std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i+offset]); + DCHECK(it != target_regions.end()); + node->AddSuccessor(it->second); + } + + std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i]); + if (target_regions.end() != it) { + // Get the already created region because this is a branch target. + Region* nextRegion = it->second; + if (last_node->GetInstruction()->IsBranch() && last_node->GetInstruction()->IsFlowthrough()) { + last_node->AddSuccessor(nextRegion); + + } + r = nextRegion; + } + + LOG(INFO) << inst->GetDexPc(code) << "*** " << inst->DumpString(&dex_file) + << " region:" <<r->StringId() << std::endl; + r->AddChild(node); + i += inst->SizeInCodeUnits(); + } + +} + + +Region* SeaGraph::GetNewRegion() { + Region* new_region = new Region(); + AddRegion(new_region); + return new_region; +} + +void SeaGraph::AddRegion(Region* r) { + DCHECK(r) << "Tried to add NULL region to SEA graph."; + regions_.push_back(r); +} +void Region::AddChild(sea_ir::SeaNode* instruction) { + DCHECK(inst) << "Tried to add NULL instruction to region node."; + instructions_.push_back(instruction); +} + +SeaNode* Region::GetLastChild() const { + if (instructions_.size()>0) { + return instructions_.back(); + } + return NULL; +} + +std::string SeaNode::ToDot() const { + std::string node = "// Instruction: \n" + StringId() + + " [label=\"" + instruction_->DumpString(NULL) + "\"];\n"; + + for(std::vector<SeaNode*>::const_iterator cit = successors_.begin(); + cit != successors_.end(); cit++) { + DCHECK(NULL != *cit) << "Null successor found for SeaNode" << StringId() << "."; + node += StringId() + " -> " + (*cit)->StringId() + ";\n\n"; + } + return node; +} + +std::string SeaNode::StringId() const { + std::stringstream ss; + ss << id_; + return ss.str(); +} + +std::string Region::ToDot() const { + std::string result = "// Region: \n" + + StringId() + " [label=\"region " + StringId() + "\"];"; + + for(std::vector<SeaNode*>::const_iterator cit = instructions_.begin(); + cit != instructions_.end(); cit++) { + result += (*cit)->ToDot(); + result += StringId() + " -> " + (*cit)->StringId() + ";\n"; + } + + result += "// End Region.\n"; + return result; +} + +void SeaNode::AddSuccessor(SeaNode* successor) { + DCHECK(successor) << "Tried to add NULL successor to SEA node."; + successors_.push_back(successor); + return; +} + +} // end namespace diff --git a/src/compiler/sea_ir/sea.h b/src/compiler/sea_ir/sea.h new file mode 100644 index 0000000000..0ebd4d02d4 --- /dev/null +++ b/src/compiler/sea_ir/sea.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dex_file.h" +#include "dex_instruction.h" + +#ifndef SEA_IR_H_ +#define SEA_IR_H_ + +#include <set> +#include <map> + +namespace sea_ir { + + +class SeaNode { + public: + explicit SeaNode(const art::Instruction* in):id_(GetNewId()), instruction_(in), successors_() {}; + explicit SeaNode():id_(GetNewId()), instruction_(NULL) {}; + void AddSuccessor(SeaNode* successor); + const art::Instruction* GetInstruction() { + DCHECK(NULL != instruction_); + return instruction_; + } + std::string StringId() const; + // Returns a dot language formatted string representing the node and + // (by convention) outgoing edges, so that the composition of theToDot() of all nodes + // builds a complete dot graph (without prolog and epilog though). + virtual std::string ToDot() const; + virtual ~SeaNode(){}; + + protected: + // Returns the id of the current block as string + + static int GetNewId() { + return current_max_node_id_++; + } + + + private: + const int id_; + const art::Instruction* const instruction_; + std::vector<sea_ir::SeaNode*> successors_; + static int current_max_node_id_; +}; + + + +class Region : public SeaNode { + public: + explicit Region():SeaNode() {} + void AddChild(sea_ir::SeaNode* instruction); + SeaNode* GetLastChild() const; + + // Returns a dot language formatted string representing the node and + // (by convention) outgoing edges, so that the composition of theToDot() of all nodes + // builds a complete dot graph (without prolog and epilog though). + virtual std::string ToDot() const; + + private: + std::vector<sea_ir::SeaNode*> instructions_; +}; + + + +class SeaGraph { + public: + static SeaGraph* GetCurrentGraph(); + void CompileMethod(const art::DexFile::CodeItem* code_item, + uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file); + // Returns a string representation of the region and its Instruction children + void DumpSea(std::string filename) const; + /*** Static helper functions follow: ***/ + static int ParseInstruction(const uint16_t* code_ptr, + art::DecodedInstruction* decoded_instruction); + static bool IsInstruction(const uint16_t* code_ptr); + + private: + // Registers the parameter as a child region of the SeaGraph instance + void AddRegion(Region* r); + // Returns new region and registers it with the SeaGraph instance + Region* GetNewRegion(); + static SeaGraph graph_; + std::vector<Region*> regions_; +}; + + +} // end namespace sea_ir +#endif diff --git a/src/compiler/stubs/portable/stubs.cc b/src/compiler/stubs/portable/stubs.cc new file mode 100644 index 0000000000..db551bf368 --- /dev/null +++ b/src/compiler/stubs/portable/stubs.cc @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "compiler/stubs/stubs.h" +#include "jni_internal.h" +#include "oat/utils/arm/assembler_arm.h" +#include "oat/utils/mips/assembler_mips.h" +#include "oat/utils/x86/assembler_x86.h" +#include "oat/runtime/oat_support_entrypoints.h" +#include "stack_indirect_reference_table.h" +#include "sirt_ref.h" + +#define __ assembler-> + +namespace art { + +namespace arm { +const std::vector<uint8_t>* CreatePortableResolutionTrampoline() { + UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm))); + RegList save = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << LR); + + __ PushList(save); + __ LoadFromOffset(kLoadWord, R12, TR, ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode)); + __ mov(R3, ShifterOperand(TR)); // Pass Thread::Current() in R3 + __ mov(R2, ShifterOperand(SP)); // Pass sp for Method** callee_addr + __ IncreaseFrameSize(12); // 3 words of space for alignment + // Call to resolution trampoline (callee, receiver, callee_addr, Thread*) + __ blx(R12); + __ mov(R12, ShifterOperand(R0)); // Save code address returned into R12 + __ DecreaseFrameSize(12); + __ PopList(save); + __ cmp(R12, ShifterOperand(0)); + __ bx(R12, NE); // If R12 != 0 tail call method's code + __ bx(LR); // Return to caller to handle exception + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} +} // namespace arm + +namespace mips { +const std::vector<uint8_t>* CreatePortableResolutionTrampoline() { + UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips))); + // Build frame and save argument registers and RA. + __ AddConstant(SP, SP, -32); + __ StoreToOffset(kStoreWord, RA, SP, 28); + __ StoreToOffset(kStoreWord, A3, SP, 12); + __ StoreToOffset(kStoreWord, A2, SP, 8); + __ StoreToOffset(kStoreWord, A1, SP, 4); + __ StoreToOffset(kStoreWord, A0, SP, 0); + + __ LoadFromOffset(kLoadWord, T9, S1, + ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode)); + __ Move(A3, S1); // Pass Thread::Current() in A3 + __ Move(A2, SP); // Pass SP for Method** callee_addr + __ Jalr(T9); // Call to resolution trampoline (callee, receiver, callee_addr, Thread*) + + // Restore frame, argument registers, and RA. + __ LoadFromOffset(kLoadWord, A0, SP, 0); + __ LoadFromOffset(kLoadWord, A1, SP, 4); + __ LoadFromOffset(kLoadWord, A2, SP, 8); + __ LoadFromOffset(kLoadWord, A3, SP, 12); + __ LoadFromOffset(kLoadWord, RA, SP, 28); + __ AddConstant(SP, SP, 32); + + Label resolve_fail; + __ EmitBranch(V0, ZERO, &resolve_fail, true); + __ Jr(V0); // If V0 != 0 tail call method's code + __ Bind(&resolve_fail, false); + __ Jr(RA); // Return to caller to handle exception + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} +} // namespace mips + +namespace x86 { +const std::vector<uint8_t>* CreatePortableResolutionTrampoline() { + UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86))); + + __ pushl(EBP); + __ movl(EBP, ESP); // save ESP + __ subl(ESP, Immediate(8)); // Align stack + __ movl(EAX, Address(EBP, 8)); // Method* called + __ leal(EDX, Address(EBP, 8)); // Method** called_addr + __ fs()->pushl(Address::Absolute(Thread::SelfOffset())); // pass thread + __ pushl(EDX); // pass called_addr + __ pushl(ECX); // pass receiver + __ pushl(EAX); // pass called + // Call to resolve method. + __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode)), + X86ManagedRegister::FromCpuRegister(ECX)); + __ leave(); + + Label resolve_fail; // forward declaration + __ cmpl(EAX, Immediate(0)); + __ j(kEqual, &resolve_fail); + __ jmp(EAX); + // Tail call to intended method. + __ Bind(&resolve_fail); + __ ret(); + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} +} // namespace x86 + +} // namespace art diff --git a/src/compiler/stubs/quick/stubs.cc b/src/compiler/stubs/quick/stubs.cc new file mode 100644 index 0000000000..a8e691f35b --- /dev/null +++ b/src/compiler/stubs/quick/stubs.cc @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "compiler/stubs/stubs.h" +#include "jni_internal.h" +#include "oat/utils/arm/assembler_arm.h" +#include "oat/utils/mips/assembler_mips.h" +#include "oat/utils/x86/assembler_x86.h" +#include "oat/runtime/oat_support_entrypoints.h" +#include "stack_indirect_reference_table.h" +#include "sirt_ref.h" + +#define __ assembler-> + +namespace art { + +namespace arm { +const std::vector<uint8_t>* CreateQuickResolutionTrampoline() { + UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm))); + // | Out args | + // | Method* | <- SP on entry + // | LR | return address into caller + // | ... | callee saves + // | R3 | possible argument + // | R2 | possible argument + // | R1 | possible argument + // | R0 | junk on call to QuickResolutionTrampolineFromCode, holds result Method* + // | Method* | Callee save Method* set up by QuickResoltuionTrampolineFromCode + // Save callee saves and ready frame for exception delivery + RegList save = (1 << R1) | (1 << R2) | (1 << R3) | (1 << R5) | (1 << R6) | (1 << R7) | (1 << R8) | + (1 << R10) | (1 << R11) | (1 << LR); + // TODO: enable when GetCalleeSaveMethod is available at stub generation time + // DCHECK_EQ(save, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetCoreSpillMask()); + __ PushList(save); + __ LoadFromOffset(kLoadWord, R12, TR, ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode)); + __ mov(R3, ShifterOperand(TR)); // Pass Thread::Current() in R3 + __ IncreaseFrameSize(8); // 2 words of space for alignment + __ mov(R2, ShifterOperand(SP)); // Pass SP + // Call to resolution trampoline (method_idx, receiver, sp, Thread*) + __ blx(R12); + __ mov(R12, ShifterOperand(R0)); // Save code address returned into R12 + // Restore registers which may have been modified by GC, "R0" will hold the Method* + __ DecreaseFrameSize(4); + __ PopList((1 << R0) | save); + __ bx(R12); // Leaf call to method's code + __ bkpt(0); + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} + +const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() { + UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm))); + + __ LoadFromOffset(kLoadWord, PC, R0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry)); + __ bkpt(0); + + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} + +const std::vector<uint8_t>* CreateInterpreterToQuickEntry() { + UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm))); + + __ LoadFromOffset(kLoadWord, PC, R0, ENTRYPOINT_OFFSET(pInterpreterToQuickEntry)); + __ bkpt(0); + + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} +} // namespace arm + +namespace mips { +const std::vector<uint8_t>* CreateQuickResolutionTrampoline() { + UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips))); + // | Out args | + // | Method* | <- SP on entry + // | RA | return address into caller + // | ... | callee saves + // | A3 | possible argument + // | A2 | possible argument + // | A1 | possible argument + // | A0/Method* | Callee save Method* set up by UnresolvedDirectMethodTrampolineFromCode + // Save callee saves and ready frame for exception delivery + __ AddConstant(SP, SP, -64); + __ StoreToOffset(kStoreWord, RA, SP, 60); + __ StoreToOffset(kStoreWord, FP, SP, 56); + __ StoreToOffset(kStoreWord, GP, SP, 52); + __ StoreToOffset(kStoreWord, S7, SP, 48); + __ StoreToOffset(kStoreWord, S6, SP, 44); + __ StoreToOffset(kStoreWord, S5, SP, 40); + __ StoreToOffset(kStoreWord, S4, SP, 36); + __ StoreToOffset(kStoreWord, S3, SP, 32); + __ StoreToOffset(kStoreWord, S2, SP, 28); + __ StoreToOffset(kStoreWord, A3, SP, 12); + __ StoreToOffset(kStoreWord, A2, SP, 8); + __ StoreToOffset(kStoreWord, A1, SP, 4); + + __ LoadFromOffset(kLoadWord, T9, S1, ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode)); + __ Move(A3, S1); // Pass Thread::Current() in A3 + __ Move(A2, SP); // Pass SP for Method** callee_addr + __ Jalr(T9); // Call to resolution trampoline (method_idx, receiver, sp, Thread*) + + // Restore registers which may have been modified by GC + __ LoadFromOffset(kLoadWord, A0, SP, 0); + __ LoadFromOffset(kLoadWord, A1, SP, 4); + __ LoadFromOffset(kLoadWord, A2, SP, 8); + __ LoadFromOffset(kLoadWord, A3, SP, 12); + __ LoadFromOffset(kLoadWord, S2, SP, 28); + __ LoadFromOffset(kLoadWord, S3, SP, 32); + __ LoadFromOffset(kLoadWord, S4, SP, 36); + __ LoadFromOffset(kLoadWord, S5, SP, 40); + __ LoadFromOffset(kLoadWord, S6, SP, 44); + __ LoadFromOffset(kLoadWord, S7, SP, 48); + __ LoadFromOffset(kLoadWord, GP, SP, 52); + __ LoadFromOffset(kLoadWord, FP, SP, 56); + __ LoadFromOffset(kLoadWord, RA, SP, 60); + __ AddConstant(SP, SP, 64); + + __ Move(T9, V0); // Put method's code in T9 + __ Jr(T9); // Leaf call to method's code + + __ Break(); + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} + +const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() { + UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips))); + + __ LoadFromOffset(kLoadWord, T9, A0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry)); + __ Jr(T9); + __ Break(); + + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} + +const std::vector<uint8_t>* CreateInterpreterToQuickEntry() { + UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips))); + + __ LoadFromOffset(kLoadWord, T9, A0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry)); + __ Jr(T9); + __ Break(); + + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} +} // namespace mips + +namespace x86 { +const std::vector<uint8_t>* CreateQuickResolutionTrampoline() { + UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86))); + // Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs) + // return address + __ pushl(EDI); + __ pushl(ESI); + __ pushl(EBP); + __ pushl(EBX); + __ pushl(EDX); + __ pushl(ECX); + __ pushl(EAX); // <-- callee save Method* to go here + __ movl(EDX, ESP); // save ESP + __ fs()->pushl(Address::Absolute(Thread::SelfOffset())); // pass Thread* + __ pushl(EDX); // pass ESP for Method* + __ pushl(ECX); // pass receiver + __ pushl(EAX); // pass Method* + + // Call to resolve method. + __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode)), + X86ManagedRegister::FromCpuRegister(ECX)); + + __ movl(EDI, EAX); // save code pointer in EDI + __ addl(ESP, Immediate(16)); // Pop arguments + __ popl(EAX); // Restore args. + __ popl(ECX); + __ popl(EDX); + __ popl(EBX); + __ popl(EBP); // Restore callee saves. + __ popl(ESI); + // Swap EDI callee save with code pointer + __ xchgl(EDI, Address(ESP, 0)); + // Tail call to intended method. + __ ret(); + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} + +const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() { + UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86))); + + __ fs()->jmp(Address::Absolute(ThreadOffset(ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry)))); + + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} + +const std::vector<uint8_t>* CreateInterpreterToQuickEntry() { + UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86))); + + __ fs()->jmp(Address::Absolute(ThreadOffset(ENTRYPOINT_OFFSET(pInterpreterToQuickEntry)))); + + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} +} // namespace x86 + +} // namespace art diff --git a/src/compiler/stubs/stubs.h b/src/compiler/stubs/stubs.h new file mode 100644 index 0000000000..ebe761df35 --- /dev/null +++ b/src/compiler/stubs/stubs.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_COMPILER_STUBS_STUBS_H_ +#define ART_SRC_COMPILER_STUBS_STUBS_H_ + +#include "runtime.h" + +namespace art { + +namespace arm { +const std::vector<uint8_t>* CreatePortableResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector<uint8_t>* CreateQuickResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector<uint8_t>* CreateInterpreterToQuickEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +} + +namespace mips { +const std::vector<uint8_t>* CreatePortableResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector<uint8_t>* CreateQuickResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector<uint8_t>* CreateInterpreterToQuickEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +} + +namespace x86 { +const std::vector<uint8_t>* CreatePortableResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector<uint8_t>* CreateQuickResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector<uint8_t>* CreateInterpreterToQuickEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +} + +} // namespace art + +#endif // ART_SRC_COMPILER_STUBS_STUBS_H_ diff --git a/src/debugger.cc b/src/debugger.cc index d7fac4300b..f2a10f02b6 100644 --- a/src/debugger.cc +++ b/src/debugger.cc @@ -24,9 +24,9 @@ #include "class_linker-inl.h" #include "dex_file-inl.h" #include "dex_instruction.h" -#include "gc/card_table-inl.h" -#include "gc/large_object_space.h" -#include "gc/space.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" #include "invoke_arg_array_builder.h" #include "jdwp/object_registry.h" #include "mirror/abstract_method-inl.h" @@ -1691,6 +1691,7 @@ JDWP::JdwpError Dbg::GetThreadStatus(JDWP::ObjectId thread_id, JDWP::JdwpThreadS case kWaitingForDebuggerSuspension: *pThreadStatus = JDWP::TS_WAIT; break; case kWaitingForDebuggerToAttach: *pThreadStatus = JDWP::TS_WAIT; break; case kWaitingForGcToComplete: *pThreadStatus = JDWP::TS_WAIT; break; + case kWaitingForCheckPointsToRun: *pThreadStatus = JDWP::TS_WAIT; break; case kWaitingForJniOnLoad: *pThreadStatus = JDWP::TS_WAIT; break; case kWaitingForSignalCatcherOutput: *pThreadStatus = JDWP::TS_WAIT; break; case kWaitingInMainDebuggerLoop: *pThreadStatus = JDWP::TS_WAIT; break; @@ -3137,7 +3138,7 @@ void Dbg::DdmSendHeapInfo(HpifWhen reason) { * [u4]: current number of objects allocated */ uint8_t heap_count = 1; - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); std::vector<uint8_t> bytes; JDWP::Append4BE(bytes, heap_count); JDWP::Append4BE(bytes, 1); // Heap id (bogus; we only have one heap). @@ -3416,17 +3417,16 @@ void Dbg::DdmSendHeapSegments(bool native) { // Send a series of heap segment chunks. HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native); if (native) { - // TODO: enable when bionic has moved to dlmalloc 2.8.5 - // dlmalloc_inspect_all(HeapChunkContext::HeapChunkCallback, &context); - UNIMPLEMENTED(WARNING) << "Native heap send heap segments"; + dlmalloc_inspect_all(HeapChunkContext::HeapChunkCallback, &context); } else { - Heap* heap = Runtime::Current()->GetHeap(); - const Spaces& spaces = heap->GetSpaces(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); Thread* self = Thread::Current(); ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - for (Spaces::const_iterator cur = spaces.begin(); cur != spaces.end(); ++cur) { - if ((*cur)->IsAllocSpace()) { - (*cur)->AsAllocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context); + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It cur = spaces.begin(), end = spaces.end(); cur != end; ++cur) { + if ((*cur)->IsDlMallocSpace()) { + (*cur)->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context); } } // Walk the large objects, these are not in the AllocSpace. diff --git a/src/dex2oat.cc b/src/dex2oat.cc index ad751d0b7f..7cf54b4eee 100644 --- a/src/dex2oat.cc +++ b/src/dex2oat.cc @@ -30,6 +30,8 @@ #include "class_linker.h" #include "compiler/driver/compiler_driver.h" #include "dex_file-inl.h" +#include "gc/space/image_space.h" +#include "gc/space/space-inl.h" #include "image_writer.h" #include "leb128.h" #include "mirror/abstract_method-inl.h" @@ -161,18 +163,15 @@ class Dex2Oat { } - // Make a list of descriptors for classes to include in the image - std::set<std::string>* GetImageClassDescriptors(const char* image_classes_filename) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Reads the class names (java.lang.Object) and returns as set of class descriptors (Ljava/lang/Object;) + CompilerDriver::DescriptorSet* ReadImageClasses(const char* image_classes_filename) { UniquePtr<std::ifstream> image_classes_file(new std::ifstream(image_classes_filename, std::ifstream::in)); if (image_classes_file.get() == NULL) { LOG(ERROR) << "Failed to open image classes file " << image_classes_filename; return NULL; } - // Load all the classes specified in the file - ClassLinker* class_linker = runtime_->GetClassLinker(); - Thread* self = Thread::Current(); + UniquePtr<CompilerDriver::DescriptorSet> image_classes(new CompilerDriver::DescriptorSet); while (image_classes_file->good()) { std::string dot; std::getline(*image_classes_file.get(), dot); @@ -180,51 +179,9 @@ class Dex2Oat { continue; } std::string descriptor(DotToDescriptor(dot.c_str())); - SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(descriptor.c_str())); - if (klass.get() == NULL) { - LOG(WARNING) << "Failed to find class " << descriptor; - Thread::Current()->ClearException(); - } + image_classes->insert(descriptor); } image_classes_file->close(); - - // Resolve exception classes referenced by the loaded classes. The catch logic assumes - // exceptions are resolved by the verifier when there is a catch block in an interested method. - // Do this here so that exception classes appear to have been specified image classes. - std::set<std::pair<uint16_t, const DexFile*> > unresolved_exception_types; - SirtRef<mirror::Class> java_lang_Throwable(self, - class_linker->FindSystemClass("Ljava/lang/Throwable;")); - do { - unresolved_exception_types.clear(); - class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor, - &unresolved_exception_types); - typedef std::set<std::pair<uint16_t, const DexFile*> >::const_iterator It; // TODO: C++0x auto - for (It it = unresolved_exception_types.begin(), - end = unresolved_exception_types.end(); - it != end; ++it) { - uint16_t exception_type_idx = it->first; - const DexFile* dex_file = it->second; - mirror::DexCache* dex_cache = class_linker->FindDexCache(*dex_file); - mirror:: ClassLoader* class_loader = NULL; - SirtRef<mirror::Class> klass(self, class_linker->ResolveType(*dex_file, exception_type_idx, - dex_cache, class_loader)); - if (klass.get() == NULL) { - const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); - const char* descriptor = dex_file->GetTypeDescriptor(type_id); - LOG(FATAL) << "Failed to resolve class " << descriptor; - } - DCHECK(java_lang_Throwable->IsAssignableFrom(klass.get())); - } - // Resolving exceptions may load classes that reference more exceptions, iterate until no - // more are found - } while (!unresolved_exception_types.empty()); - - // We walk the roots looking for classes so that we'll pick up the - // above classes plus any classes them depend on such super - // classes, interfaces, and the required ClassLinker roots. - UniquePtr<ImageWriter::DescriptorSet> image_classes(new ImageWriter::DescriptorSet); - class_linker->VisitClasses(RecordImageClassesVisitor, image_classes.get()); - CHECK_NE(image_classes->size(), 0U); return image_classes.release(); } @@ -236,7 +193,7 @@ class Dex2Oat { File* oat_file, const std::string& bitcode_filename, bool image, - const ImageWriter::DescriptorSet* image_classes, + UniquePtr<CompilerDriver::DescriptorSet>& image_classes, bool dump_stats, bool dump_timings) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -260,9 +217,9 @@ class Dex2Oat { UniquePtr<CompilerDriver> driver(new CompilerDriver(compiler_backend_, instruction_set_, image, + image_classes.release(), thread_count_, support_debugging_, - image_classes, dump_stats, dump_timings)); @@ -280,11 +237,11 @@ class Dex2Oat { std::string image_file_location; uint32_t image_file_location_oat_checksum = 0; uint32_t image_file_location_oat_data_begin = 0; - Heap* heap = Runtime::Current()->GetHeap(); - if (heap->GetSpaces().size() > 1) { - ImageSpace* image_space = heap->GetImageSpace(); + if (!driver->IsImage()) { + gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum(); - image_file_location_oat_data_begin = reinterpret_cast<uint32_t>(image_space->GetImageHeader().GetOatDataBegin()); + image_file_location_oat_data_begin = + reinterpret_cast<uint32_t>(image_space->GetImageHeader().GetOatDataBegin()); image_file_location = image_space->GetImageFilename(); if (host_prefix != NULL && StartsWith(image_file_location, host_prefix->c_str())) { image_file_location = image_file_location.substr(host_prefix->size()); @@ -292,6 +249,13 @@ class Dex2Oat { } std::vector<uint8_t> oat_contents; + // TODO: change ElfWriterQuick to not require the creation of oat_contents. The old pre-mclinker + // OatWriter streamed directly to disk. The new could can be adapted to do it as follows: + // 1.) use first pass of OatWriter to calculate size of oat structure, + // 2.) call ElfWriterQuick with pointer to OatWriter instead of contents, + // 3.) have ElfWriterQuick call back to OatWriter to stream generate the output directly in + // place in the elf file. + oat_contents.reserve(5 * MB); VectorOutputStream vector_output_stream(oat_file->GetPath(), oat_contents); if (!OatWriter::Create(vector_output_stream, dex_files, @@ -313,7 +277,6 @@ class Dex2Oat { bool CreateImageFile(const std::string& image_filename, uintptr_t image_base, - ImageWriter::DescriptorSet* image_classes, const std::string& oat_filename, const std::string& oat_location, const CompilerDriver& compiler) @@ -321,8 +284,8 @@ class Dex2Oat { uintptr_t oat_data_begin; { // ImageWriter is scoped so it can free memory before doing FixupElf - ImageWriter image_writer(image_classes); - if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location, compiler)) { + ImageWriter image_writer(compiler); + if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) { LOG(ERROR) << "Failed to create image file " << image_filename; return false; } @@ -373,72 +336,6 @@ class Dex2Oat { return true; } - static void ResolveExceptionsForMethod(MethodHelper* mh, - std::set<std::pair<uint16_t, const DexFile*> >& exceptions_to_resolve) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = mh->GetCodeItem(); - if (code_item == NULL) { - return; // native or abstract method - } - if (code_item->tries_size_ == 0) { - return; // nothing to process - } - const byte* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0); - size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list); - for (size_t i = 0; i < num_encoded_catch_handlers; i++) { - int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list); - bool has_catch_all = false; - if (encoded_catch_handler_size <= 0) { - encoded_catch_handler_size = -encoded_catch_handler_size; - has_catch_all = true; - } - for (int32_t j = 0; j < encoded_catch_handler_size; j++) { - uint16_t encoded_catch_handler_handlers_type_idx = - DecodeUnsignedLeb128(&encoded_catch_handler_list); - // Add to set of types to resolve if not already in the dex cache resolved types - if (!mh->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { - exceptions_to_resolve.insert( - std::pair<uint16_t, const DexFile*>(encoded_catch_handler_handlers_type_idx, - &mh->GetDexFile())); - } - // ignore address associated with catch handler - DecodeUnsignedLeb128(&encoded_catch_handler_list); - } - if (has_catch_all) { - // ignore catch all address - DecodeUnsignedLeb128(&encoded_catch_handler_list); - } - } - } - - static bool ResolveCatchBlockExceptionsClassVisitor(mirror::Class* c, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::set<std::pair<uint16_t, const DexFile*> >* exceptions_to_resolve = - reinterpret_cast<std::set<std::pair<uint16_t, const DexFile*> >*>(arg); - MethodHelper mh; - for (size_t i = 0; i < c->NumVirtualMethods(); ++i) { - mirror::AbstractMethod* m = c->GetVirtualMethod(i); - mh.ChangeMethod(m); - ResolveExceptionsForMethod(&mh, *exceptions_to_resolve); - } - for (size_t i = 0; i < c->NumDirectMethods(); ++i) { - mirror::AbstractMethod* m = c->GetDirectMethod(i); - mh.ChangeMethod(m); - ResolveExceptionsForMethod(&mh, *exceptions_to_resolve); - } - return true; - } - - static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ImageWriter::DescriptorSet* image_classes = reinterpret_cast<ImageWriter::DescriptorSet*>(arg); - if (klass->IsArrayClass() || klass->IsPrimitive()) { - return true; - } - image_classes->insert(ClassHelper(klass).GetDescriptor()); - return true; - } - // Appends to dex_files any elements of class_path that it doesn't already // contain. This will open those dex files as necessary. static void OpenClassPathFiles(const std::string& class_path, std::vector<const DexFile*>& dex_files) { @@ -561,12 +458,14 @@ class WatchDog { private: static void* CallBack(void* arg) { WatchDog* self = reinterpret_cast<WatchDog*>(arg); + ::art::SetThreadName("dex2oat watch dog"); self->Wait(); return NULL; } static void Message(char severity, const std::string& message) { - // TODO: Remove when we switch to LOG when we can guarantee it won't prevent shutdown in error cases. + // TODO: Remove when we switch to LOG when we can guarantee it won't prevent shutdown in error + // cases. fprintf(stderr, "dex2oat%s %c %d %d %s\n", kIsDebugBuild ? "d" : "", severity, @@ -587,10 +486,13 @@ class WatchDog { void Wait() { bool warning = true; CHECK_GT(kWatchDogTimeoutSeconds, kWatchDogWarningSeconds); + // TODO: tune the multiplier for GC verification, the following is just to make the timeout + // large. + int64_t multiplier = gc::kDesiredHeapVerification > gc::kVerifyAllFast ? 100 : 1; timespec warning_ts; - InitTimeSpec(true, CLOCK_REALTIME, kWatchDogWarningSeconds * 1000, 0, &warning_ts); + InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogWarningSeconds * 1000, 0, &warning_ts); timespec timeout_ts; - InitTimeSpec(true, CLOCK_REALTIME, kWatchDogTimeoutSeconds * 1000, 0, &timeout_ts); + InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogTimeoutSeconds * 1000, 0, &timeout_ts); const char* reason = "dex2oat watch dog thread waiting"; CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason); while (!shutting_down_) { @@ -627,12 +529,14 @@ class WatchDog { bool is_watch_dog_enabled_; bool shutting_down_; - // TODO: Switch to Mutex when we can guarantee it won't prevent shutdown in error cases + // TODO: Switch to Mutex when we can guarantee it won't prevent shutdown in error cases. pthread_mutex_t mutex_; pthread_cond_t cond_; pthread_attr_t attr_; pthread_t pthread_; }; +const unsigned int WatchDog::kWatchDogWarningSeconds; +const unsigned int WatchDog::kWatchDogTimeoutSeconds; static int dex2oat(int argc, char** argv) { InitLogging(argv); @@ -919,8 +823,14 @@ static int dex2oat(int argc, char** argv) { options.push_back(std::make_pair("-small", reinterpret_cast<void*>(NULL))); #endif // ART_SMALL_MODE + +#ifdef ART_SEA_IR_MODE + options.push_back(std::make_pair("-sea_ir", reinterpret_cast<void*>(NULL))); +#endif + + Dex2Oat* p_dex2oat; - if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set, thread_count, + if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set, thread_count, support_debugging)) { LOG(ERROR) << "Failed to create dex2oat"; return EXIT_FAILURE; @@ -934,9 +844,9 @@ static int dex2oat(int argc, char** argv) { ScopedObjectAccess soa(Thread::Current()); // If --image-classes was specified, calculate the full list of classes to include in the image - UniquePtr<ImageWriter::DescriptorSet> image_classes(NULL); + UniquePtr<CompilerDriver::DescriptorSet> image_classes(NULL); if (image_classes_filename != NULL) { - image_classes.reset(dex2oat->GetImageClassDescriptors(image_classes_filename)); + image_classes.reset(dex2oat->ReadImageClasses(image_classes_filename)); if (image_classes.get() == NULL) { LOG(ERROR) << "Failed to create list of image classes from " << image_classes_filename; return EXIT_FAILURE; @@ -992,7 +902,7 @@ static int dex2oat(int argc, char** argv) { oat_file.get(), bitcode_filename, image, - image_classes.get(), + image_classes, dump_stats, dump_timings)); @@ -1057,7 +967,6 @@ static int dex2oat(int argc, char** argv) { Thread::Current()->TransitionFromRunnableToSuspended(kNative); bool image_creation_success = dex2oat->CreateImageFile(image_filename, image_base, - image_classes.get(), oat_unstripped, oat_location, *compiler.get()); diff --git a/src/dex_file.cc b/src/dex_file.cc index 0f0bed4db9..80465f2fed 100644 --- a/src/dex_file.cc +++ b/src/dex_file.cc @@ -108,6 +108,28 @@ int DexFile::GetPermissions() const { } } +bool DexFile::IsReadOnly() const { + return GetPermissions() == PROT_READ; +} + +bool DexFile::EnableWrite(uint8_t* addr, size_t length) const { + CHECK(IsReadOnly()); + if (mem_map_.get() == NULL) { + return false; + } else { + return mem_map_->ProtectRegion(addr, length, PROT_READ | PROT_WRITE); + } +} + +bool DexFile::DisableWrite(uint8_t* addr, size_t length) const { + CHECK(!IsReadOnly()); + if (mem_map_.get() == NULL) { + return false; + } else { + return mem_map_->ProtectRegion(addr, length, PROT_READ); + } +} + const DexFile* DexFile::OpenFile(const std::string& filename, const std::string& location, bool verify) { @@ -373,10 +395,10 @@ const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_kl const uint16_t class_idx = GetIndexForTypeId(declaring_klass); const uint32_t name_idx = GetIndexForStringId(name); const uint16_t type_idx = GetIndexForTypeId(type); - uint32_t lo = 0; - uint32_t hi = NumFieldIds() - 1; + int32_t lo = 0; + int32_t hi = NumFieldIds() - 1; while (hi >= lo) { - uint32_t mid = (hi + lo) / 2; + int32_t mid = (hi + lo) / 2; const DexFile::FieldId& field = GetFieldId(mid); if (class_idx > field.class_idx_) { lo = mid + 1; @@ -408,10 +430,10 @@ const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_ const uint16_t class_idx = GetIndexForTypeId(declaring_klass); const uint32_t name_idx = GetIndexForStringId(name); const uint16_t proto_idx = GetIndexForProtoId(signature); - uint32_t lo = 0; - uint32_t hi = NumMethodIds() - 1; + int32_t lo = 0; + int32_t hi = NumMethodIds() - 1; while (hi >= lo) { - uint32_t mid = (hi + lo) / 2; + int32_t mid = (hi + lo) / 2; const DexFile::MethodId& method = GetMethodId(mid); if (class_idx > method.class_idx_) { lo = mid + 1; @@ -436,15 +458,35 @@ const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_ return NULL; } -const DexFile::StringId* DexFile::FindStringId(const std::string& string) const { - uint32_t lo = 0; - uint32_t hi = NumStringIds() - 1; +const DexFile::StringId* DexFile::FindStringId(const char* string) const { + int32_t lo = 0; + int32_t hi = NumStringIds() - 1; + while (hi >= lo) { + int32_t mid = (hi + lo) / 2; + uint32_t length; + const DexFile::StringId& str_id = GetStringId(mid); + const char* str = GetStringDataAndLength(str_id, &length); + int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); + if (compare > 0) { + lo = mid + 1; + } else if (compare < 0) { + hi = mid - 1; + } else { + return &str_id; + } + } + return NULL; +} + +const DexFile::StringId* DexFile::FindStringId(const uint16_t* string) const { + int32_t lo = 0; + int32_t hi = NumStringIds() - 1; while (hi >= lo) { - uint32_t mid = (hi + lo) / 2; + int32_t mid = (hi + lo) / 2; uint32_t length; const DexFile::StringId& str_id = GetStringId(mid); const char* str = GetStringDataAndLength(str_id, &length); - int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string.c_str(), str); + int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string); if (compare > 0) { lo = mid + 1; } else if (compare < 0) { @@ -457,10 +499,10 @@ const DexFile::StringId* DexFile::FindStringId(const std::string& string) const } const DexFile::TypeId* DexFile::FindTypeId(uint32_t string_idx) const { - uint32_t lo = 0; - uint32_t hi = NumTypeIds() - 1; + int32_t lo = 0; + int32_t hi = NumTypeIds() - 1; while (hi >= lo) { - uint32_t mid = (hi + lo) / 2; + int32_t mid = (hi + lo) / 2; const TypeId& type_id = GetTypeId(mid); if (string_idx > type_id.descriptor_idx_) { lo = mid + 1; @@ -475,10 +517,10 @@ const DexFile::TypeId* DexFile::FindTypeId(uint32_t string_idx) const { const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx, const std::vector<uint16_t>& signature_type_idxs) const { - uint32_t lo = 0; - uint32_t hi = NumProtoIds() - 1; + int32_t lo = 0; + int32_t hi = NumProtoIds() - 1; while (hi >= lo) { - uint32_t mid = (hi + lo) / 2; + int32_t mid = (hi + lo) / 2; const DexFile::ProtoId& proto = GetProtoId(mid); int compare = return_type_idx - proto.return_type_idx_; if (compare == 0) { @@ -544,7 +586,7 @@ bool DexFile::CreateTypeList(uint16_t* return_type_idx, std::vector<uint16_t>* p descriptor += c; } while (c != ';'); } - const DexFile::StringId* string_id = FindStringId(descriptor); + const DexFile::StringId* string_id = FindStringId(descriptor.c_str()); if (string_id == NULL) { return false; } diff --git a/src/dex_file.h b/src/dex_file.h index 6e34b5737f..e09270e018 100644 --- a/src/dex_file.h +++ b/src/dex_file.h @@ -21,6 +21,7 @@ #include <vector> #include "base/logging.h" +#include "base/mutex.h" #include "base/stringpiece.h" #include "globals.h" #include "invoke_type.h" @@ -384,6 +385,10 @@ class DexFile { return *header_; } + Mutex& GetModificationLock() { + return modification_lock; + } + // Decode the dex magic version uint32_t GetVersion() const; @@ -436,8 +441,11 @@ class DexFile { return StringDataAndLengthByIdx(idx, &unicode_length); } - // Looks up a string id for a given string - const StringId* FindStringId(const std::string& string) const; + // Looks up a string id for a given modified utf8 string. + const StringId* FindStringId(const char* string) const; + + // Looks up a string id for a given utf16 string. + const StringId* FindStringId(const uint16_t* string) const; // Returns the number of type identifiers in the .dex file. size_t NumTypeIds() const { @@ -795,6 +803,12 @@ class DexFile { int GetPermissions() const; + bool IsReadOnly() const; + + bool EnableWrite(uint8_t* addr, size_t size) const; + + bool DisableWrite(uint8_t* addr, size_t size) const; + private: // Opens a .dex file static const DexFile* OpenFile(const std::string& filename, @@ -827,6 +841,7 @@ class DexFile { location_checksum_(location_checksum), mem_map_(mem_map), dex_object_(NULL), + modification_lock("DEX modification lock"), header_(0), string_ids_(0), type_ids_(0), @@ -887,6 +902,11 @@ class DexFile { // TODO: this is mutable as it shouldn't be here. We should move it to the dex cache or similar. mutable jobject dex_object_; + // The DEX-to-DEX compiler uses this lock to ensure thread safety when + // enabling write access to a read-only DEX file. + // TODO: move to Locks::dex_file_modification_lock. + Mutex modification_lock; + // Points to the header section. const Header* header_; @@ -974,7 +994,7 @@ class ClassDataItemIterator { bool HasNext() const { return pos_ < EndOfVirtualMethodsPos(); } - void Next() { + inline void Next() { pos_++; if (pos_ < EndOfStaticFieldsPos()) { last_idx_ = GetMemberIndex(); diff --git a/src/dex_file_verifier.cc b/src/dex_file_verifier.cc index b1efcaadbd..6df4411565 100644 --- a/src/dex_file_verifier.cc +++ b/src/dex_file_verifier.cc @@ -369,10 +369,12 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_fla } if (expect_code && code_offset == 0) { - LOG(ERROR) << StringPrintf("Unexpected zero value for class_data_item method code_off with access flags %x", access_flags); + LOG(ERROR)<< StringPrintf("Unexpected zero value for class_data_item method code_off" + " with access flags %x", access_flags); return false; } else if (!expect_code && code_offset != 0) { - LOG(ERROR) << StringPrintf("Unexpected non-zero value %x for class_data_item method code_off with access flags %x", code_offset, access_flags); + LOG(ERROR) << StringPrintf("Unexpected non-zero value %x for class_data_item method code_off" + " with access flags %x", code_offset, access_flags); return false; } @@ -544,7 +546,8 @@ bool DexFileVerifier::CheckEncodedAnnotation() { } if (last_idx >= idx && i != 0) { - LOG(ERROR) << StringPrintf("Out-of-order annotation_element name_idx: %x then %x", last_idx, idx); + LOG(ERROR) << StringPrintf("Out-of-order annotation_element name_idx: %x then %x", + last_idx, idx); return false; } @@ -651,7 +654,8 @@ bool DexFileVerifier::CheckIntraCodeItem() { uint32_t last_addr = 0; while (try_items_size--) { if (try_items->start_addr_ < last_addr) { - LOG(ERROR) << StringPrintf("Out-of_order try_item with start_addr: %x", try_items->start_addr_); + LOG(ERROR) << StringPrintf("Out-of_order try_item with start_addr: %x", + try_items->start_addr_); return false; } @@ -933,7 +937,8 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { last_idx = 0; for (uint32_t i = 0; i < method_count; i++) { if (last_idx >= method_item->method_idx_ && i != 0) { - LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x", last_idx, method_item->method_idx_); + LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x", + last_idx, method_item->method_idx_); return false; } last_idx = method_item->method_idx_; @@ -944,14 +949,16 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { const DexFile::ParameterAnnotationsItem* parameter_item = reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item); uint32_t parameter_count = item->parameters_size_; - if (!CheckListSize(parameter_item, parameter_count, sizeof(DexFile::ParameterAnnotationsItem), "parameter_annotations list")) { + if (!CheckListSize(parameter_item, parameter_count, sizeof(DexFile::ParameterAnnotationsItem), + "parameter_annotations list")) { return false; } last_idx = 0; for (uint32_t i = 0; i < parameter_count; i++) { if (last_idx >= parameter_item->method_idx_ && i != 0) { - LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x", last_idx, parameter_item->method_idx_); + LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x", + last_idx, parameter_item->method_idx_); return false; } last_idx = parameter_item->method_idx_; @@ -1051,7 +1058,8 @@ bool DexFileVerifier::CheckIntraSectionIterate(uint32_t offset, uint32_t count, uint32_t count = list->size_; if (!CheckPointerRange(list, list + 1, "annotation_set_ref_list") || - !CheckListSize(item, count, sizeof(DexFile::AnnotationSetRefItem), "annotation_set_ref_list size")) { + !CheckListSize(item, count, sizeof(DexFile::AnnotationSetRefItem), + "annotation_set_ref_list size")) { return false; } ptr_ = reinterpret_cast<const byte*>(item + count); @@ -1257,7 +1265,8 @@ bool DexFileVerifier::CheckIntraSection() { return false; } if (section_offset != header_->map_off_) { - LOG(ERROR) << StringPrintf("Map not at header-defined offset: %x, expected %x", section_offset, header_->map_off_); + LOG(ERROR) << StringPrintf("Map not at header-defined offset: %x, expected %x", + section_offset, header_->map_off_); return false; } ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); @@ -1297,7 +1306,8 @@ bool DexFileVerifier::CheckOffsetToTypeMap(uint32_t offset, uint16_t type) { return false; } if (it->second != type) { - LOG(ERROR) << StringPrintf("Unexpected data map entry @ %x; expected %x, found %x", offset, type, it->second); + LOG(ERROR) << StringPrintf("Unexpected data map entry @ %x; expected %x, found %x", + offset, type, it->second); return false; } return true; @@ -1380,7 +1390,8 @@ bool DexFileVerifier::CheckInterTypeIdItem() { if (previous_item_ != NULL) { const DexFile::TypeId* prev_item = reinterpret_cast<const DexFile::TypeId*>(previous_item_); if (prev_item->descriptor_idx_ >= item->descriptor_idx_) { - LOG(ERROR) << StringPrintf("Out-of-order type_ids: %x then %x", prev_item->descriptor_idx_, item->descriptor_idx_); + LOG(ERROR) << StringPrintf("Out-of-order type_ids: %x then %x", + prev_item->descriptor_idx_, item->descriptor_idx_); return false; } } @@ -1757,7 +1768,8 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() { LOG(ERROR) << "Mismatched defining class for parameter_annotation"; return false; } - if (!CheckOffsetToTypeMap(parameter_item->annotations_off_, DexFile::kDexTypeAnnotationSetRefList)) { + if (!CheckOffsetToTypeMap(parameter_item->annotations_off_, + DexFile::kDexTypeAnnotationSetRefList)) { return false; } parameter_item++; diff --git a/src/dex_instruction-inl.h b/src/dex_instruction-inl.h index 99dab121cf..b426e66a1c 100644 --- a/src/dex_instruction-inl.h +++ b/src/dex_instruction-inl.h @@ -21,13 +21,6 @@ namespace art { -inline const Instruction* Instruction::Next_51l() const { - DCHECK_EQ(FormatOf(Opcode()), k51l); - size_t current_size_in_bytes = 5 * sizeof(uint16_t); - const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this); - return reinterpret_cast<const Instruction*>(ptr + current_size_in_bytes); -} - //------------------------------------------------------------------------------ // VRegA //------------------------------------------------------------------------------ @@ -36,6 +29,11 @@ inline int8_t Instruction::VRegA_10t() const { return static_cast<int8_t>(InstAA()); } +inline uint8_t Instruction::VRegA_10x() const { + DCHECK_EQ(FormatOf(Opcode()), k10x); + return InstAA(); +} + inline uint4_t Instruction::VRegA_11n() const { DCHECK_EQ(FormatOf(Opcode()), k11n); return InstA(); diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc index 1b7d3bb3c4..c5901aa34b 100644 --- a/src/dex_instruction.cc +++ b/src/dex_instruction.cc @@ -82,6 +82,68 @@ static inline uint32_t fetch_uint32_impl(uint32_t offset, const uint16_t* insns) return insns[offset] | ((uint32_t) insns[offset+1] << 16); } +int32_t Instruction::VRegC() const { + switch (FormatOf(Opcode())) { + case k22b: return VRegC_22b(); + case k22c: return VRegC_22c(); + case k22s: return VRegC_22s(); + case k22t: return VRegC_22t(); + case k23x: return VRegC_23x(); + case k35c: return VRegC_35c(); + case k3rc: return VRegC_3rc(); + default: LOG(FATAL) << "Tried to access vC of instruction " << Name() << + " which has no C operand."; + } + return 0; +} + +int32_t Instruction::VRegB() const { + switch (FormatOf(Opcode())) { + case k11n: return VRegB_11n(); + case k12x: return VRegB_12x(); + case k21c: return VRegB_21c(); + case k21h: return VRegB_21h(); + case k21t: return VRegB_21t(); + case k22b: return VRegB_22b(); + case k22c: return VRegB_22c(); + case k22s: return VRegB_22s(); + case k22t: return VRegB_22t(); + case k22x: return VRegB_22x(); + case k31c: return VRegB_31c(); + case k31i: return VRegB_31i(); + case k31t: return VRegB_31t(); + case k32x: return VRegB_32x(); + case k35c: return VRegB_35c(); + case k3rc: return VRegB_3rc(); + case k51l: return VRegB_51l(); + default: LOG(FATAL) << "Tried to access vB of instruction " << Name() << + " which has no B operand."; + } + return 0; +} + +int32_t Instruction::GetTargetOffset() const { + switch (FormatOf(Opcode())) { + // Cases for conditional branches follow. + case k22t: return VRegC_22t(); + case k21t: return VRegB_21t(); + // Cases for unconditional branches follow. + case k10t: return VRegA_10t(); + case k20t: return VRegA_20t(); + case k30t: return VRegA_30t(); + default: LOG(FATAL) << "Tried to access the branch offset of an instruction " << Name() << + " which does not have a target operand."; + } + return 0; +} + +bool Instruction::CanFlowThrough() const { + const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); + uint16_t insn = *insns; + Code opcode = static_cast<Code>(insn & 0xFF); + return FlagsOf(opcode) & Instruction::kContinue; +} + void Instruction::Decode(uint32_t &vA, uint32_t &vB, uint64_t &vB_wide, uint32_t &vC, uint32_t arg[]) const { const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); uint16_t insn = *insns; @@ -299,7 +361,7 @@ std::string Instruction::DumpString(const DexFile* file) const { case NEW_INSTANCE: if (file != NULL) { uint32_t type_idx = VRegB_21c(); - os << opcode << " v" << VRegA_21c() << ", " << PrettyType(type_idx, *file) + os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << PrettyType(type_idx, *file) << " // type@" << type_idx; break; } // else fall-through @@ -312,7 +374,7 @@ std::string Instruction::DumpString(const DexFile* file) const { case SGET_SHORT: if (file != NULL) { uint32_t field_idx = VRegB_21c(); - os << opcode << " v" << VRegA_21c() << ", " << PrettyField(field_idx, *file, true) + os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << PrettyField(field_idx, *file, true) << " // field@" << field_idx; break; } // else fall-through @@ -325,7 +387,7 @@ std::string Instruction::DumpString(const DexFile* file) const { case SPUT_SHORT: if (file != NULL) { uint32_t field_idx = VRegB_21c(); - os << opcode << " v" << VRegA_21c() << ", " << PrettyField(field_idx, *file, true) + os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << PrettyField(field_idx, *file, true) << " // field@" << field_idx; break; } // else fall-through @@ -350,10 +412,18 @@ std::string Instruction::DumpString(const DexFile* file) const { case IGET_SHORT: if (file != NULL) { uint32_t field_idx = VRegC_22c(); - os << opcode << " v" << VRegA_22c() << ", v" << VRegB_22c() << ", " + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " << PrettyField(field_idx, *file, true) << " // field@" << field_idx; break; } // else fall-through + case IGET_QUICK: + case IGET_OBJECT_QUICK: + if (file != NULL) { + uint32_t field_idx = VRegC_22c(); + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " + << "// offset@" << field_idx; + break; + } // else fall-through case IPUT: case IPUT_WIDE: case IPUT_OBJECT: @@ -363,21 +433,29 @@ std::string Instruction::DumpString(const DexFile* file) const { case IPUT_SHORT: if (file != NULL) { uint32_t field_idx = VRegC_22c(); - os << opcode << " v" << VRegA_22c() << ", v" << VRegB_22c() << ", " + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " << PrettyField(field_idx, *file, true) << " // field@" << field_idx; break; } // else fall-through + case IPUT_QUICK: + case IPUT_OBJECT_QUICK: + if (file != NULL) { + uint32_t field_idx = VRegC_22c(); + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " + << "// offset@" << field_idx; + break; + } // else fall-through case INSTANCE_OF: if (file != NULL) { uint32_t type_idx = VRegC_22c(); - os << opcode << " v" << VRegA_22c() << ", v" << VRegB_22c() << ", " + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " << PrettyType(type_idx, *file) << " // type@" << type_idx; break; } case NEW_ARRAY: if (file != NULL) { uint32_t type_idx = VRegC_22c(); - os << opcode << " v" << VRegA_22c() << ", v" << VRegB_22c() << ", " + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " << PrettyType(type_idx, *file) << " // type@" << type_idx; break; } // else fall-through @@ -413,6 +491,19 @@ std::string Instruction::DumpString(const DexFile* file) const { os << "}, " << PrettyMethod(method_idx, *file) << " // method@" << method_idx; break; } // else fall-through + case INVOKE_VIRTUAL_QUICK: + if (file != NULL) { + os << opcode << " {"; + uint32_t method_idx = VRegB_35c(); + for (size_t i = 0; i < VRegA_35c(); ++i) { + if (i != 0) { + os << ", "; + } + os << "v" << arg[i]; + } + os << "}, // vtable@" << method_idx; + break; + } // else fall-through default: os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2] << ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c(); @@ -433,6 +524,13 @@ std::string Instruction::DumpString(const DexFile* file) const { << PrettyMethod(method_idx, *file) << " // method@" << method_idx; break; } // else fall-through + case INVOKE_VIRTUAL_RANGE_QUICK: + if (file != NULL) { + uint32_t method_idx = VRegB_3rc(); + os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1)) + << "// vtable@" << method_idx; + break; + } // else fall-through default: os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc()); diff --git a/src/dex_instruction.h b/src/dex_instruction.h index 218acb6fe1..602667aa4e 100644 --- a/src/dex_instruction.h +++ b/src/dex_instruction.h @@ -46,6 +46,7 @@ class Instruction { const uint16_t case_count; const int32_t first_key; const int32_t targets[]; + private: DISALLOW_COPY_AND_ASSIGN(PackedSwitchPayload); }; @@ -73,6 +74,7 @@ class Instruction { const uint16_t element_width; const uint32_t element_count; const uint8_t data[]; + private: DISALLOW_COPY_AND_ASSIGN(ArrayDataPayload); }; @@ -162,39 +164,45 @@ class Instruction { } } + // Reads an instruction out of the stream at the specified address. + static const Instruction* At(const uint16_t* code) { + DCHECK(code != NULL); + return reinterpret_cast<const Instruction*>(code); + } + + // Reads an instruction out of the stream from the current address plus an offset. + const Instruction* RelativeAt(int32_t offset) const { + return At(reinterpret_cast<const uint16_t*>(this) + offset); + } + // Returns a pointer to the next instruction in the stream. const Instruction* Next() const { - size_t current_size_in_bytes = SizeInCodeUnits() * sizeof(uint16_t); - const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this); - return reinterpret_cast<const Instruction*>(ptr + current_size_in_bytes); + return RelativeAt(SizeInCodeUnits()); } // Returns a pointer to the instruction after this 1xx instruction in the stream. const Instruction* Next_1xx() const { DCHECK(FormatOf(Opcode()) >= k10x && FormatOf(Opcode()) <= k10t); - size_t current_size_in_bytes = 1 * sizeof(uint16_t); - const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this); - return reinterpret_cast<const Instruction*>(ptr + current_size_in_bytes); + return RelativeAt(1); } // Returns a pointer to the instruction after this 2xx instruction in the stream. const Instruction* Next_2xx() const { DCHECK(FormatOf(Opcode()) >= k20t && FormatOf(Opcode()) <= k22c); - size_t current_size_in_bytes = 2 * sizeof(uint16_t); - const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this); - return reinterpret_cast<const Instruction*>(ptr + current_size_in_bytes); + return RelativeAt(2); } // Returns a pointer to the instruction after this 3xx instruction in the stream. const Instruction* Next_3xx() const { DCHECK(FormatOf(Opcode()) >= k32x && FormatOf(Opcode()) <= k3rc); - size_t current_size_in_bytes = 3 * sizeof(uint16_t); - const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this); - return reinterpret_cast<const Instruction*>(ptr + current_size_in_bytes); + return RelativeAt(3); } // Returns a pointer to the instruction after this 51l instruction in the stream. - const Instruction* Next_51l() const; + const Instruction* Next_51l() const { + DCHECK(FormatOf(Opcode()) == k51l); + return RelativeAt(5); + } // Returns the name of this instruction's opcode. const char* Name() const { @@ -208,6 +216,7 @@ class Instruction { // VRegA int8_t VRegA_10t() const; + uint8_t VRegA_10x() const; uint4_t VRegA_11n() const; uint8_t VRegA_11x() const; uint4_t VRegA_12x() const; @@ -232,6 +241,7 @@ class Instruction { uint8_t VRegA_51l() const; // VRegB + int32_t VRegB() const; int4_t VRegB_11n() const; uint4_t VRegB_12x() const; uint16_t VRegB_21c() const; @@ -250,9 +260,10 @@ class Instruction { uint16_t VRegB_32x() const; uint16_t VRegB_35c() const; uint16_t VRegB_3rc() const; - uint64_t VRegB_51l() const; // vB_wide + uint64_t VRegB_51l() const; // vB_wide // VRegC + int32_t VRegC() const; int8_t VRegC_22b() const; uint16_t VRegC_22c() const; int16_t VRegC_22s() const; @@ -271,10 +282,28 @@ class Instruction { return static_cast<Code>(opcode); } - // Reads an instruction out of the stream at the specified address. - static const Instruction* At(const uint16_t* code) { - CHECK(code != NULL); - return reinterpret_cast<const Instruction*>(code); + void SetOpcode(Code opcode) { + DCHECK_LT(static_cast<uint16_t>(opcode), 256u); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode); + } + + void SetVRegB_3rc(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k3rc); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[1] = val; + } + + void SetVRegB_35c(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k35c); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[1] = val; + } + + void SetVRegC_22c(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k22c); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[1] = val; } // Returns the format of the given opcode. @@ -297,6 +326,12 @@ class Instruction { return (kInstructionFlags[Opcode()] & kUnconditional) != 0; } + // Returns the branch offset if this instruction is a branch. + int32_t GetTargetOffset() const; + + // Returns true if the instruction allows control flow to go to the following instruction. + bool CanFlowThrough() const; + // Returns true if this instruction is a switch. bool IsSwitch() const { return (kInstructionFlags[Opcode()] & kSwitch) != 0; diff --git a/src/dex_instruction_list.h b/src/dex_instruction_list.h index 3083d224e9..9daec61ba5 100644 --- a/src/dex_instruction_list.h +++ b/src/dex_instruction_list.h @@ -242,14 +242,14 @@ V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ - V(0xE3, UNUSED_E3, "unused-e3", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE4, UNUSED_E4, "unused-e4", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE5, UNUSED_E5, "unused-e5", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE6, UNUSED_E6, "unused-e6", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE7, UNUSED_E7, "unused-e7", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE8, UNUSED_E8, "unused-e8", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE9, UNUSED_E9, "unused-e9", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xEA, UNUSED_EA, "unused-ea", k10x, false, kUnknown, 0, kVerifyError) \ + V(0xE3, IGET_QUICK, "iget-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE4, IGET_WIDE_QUICK, "iget-wide-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \ + V(0xE5, IGET_OBJECT_QUICK, "iget-object-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE6, IPUT_QUICK, "iput-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \ + V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArg) \ + V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRange) \ V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \ V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \ V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \ diff --git a/src/disassembler_arm.cc b/src/disassembler_arm.cc index 7e75600b19..172bef84d6 100644 --- a/src/disassembler_arm.cc +++ b/src/disassembler_arm.cc @@ -1223,7 +1223,7 @@ size_t DisassemblerArm::DumpThumb16(std::ostream& os, const uint8_t* instr_ptr) args << Rt << ", [" << Rn << ", #" << imm5 << "]"; } } else if (opcode1 >= 0x34 && opcode1 <= 0x37) { // 1101xx - uint32_t imm8 = instr & 0xFF; + int8_t imm8 = instr & 0xFF; uint32_t cond = (instr >> 8) & 0xF; opcode << "b"; DumpCond(opcode, cond); @@ -1260,7 +1260,7 @@ size_t DisassemblerArm::DumpThumb16(std::ostream& os, const uint8_t* instr_ptr) uint16_t imm5 = (instr >> 3) & 0x1F; ThumbRegister Rn(instr, 0); opcode << (op != 0 ? "cbnz" : "cbz"); - uint32_t imm32 = (i << 7) | (imm5 << 1); + uint32_t imm32 = (i << 6) | (imm5 << 1); args << Rn << ", "; DumpBranchTarget(args, instr_ptr + 4, imm32); break; diff --git a/src/gc/atomic_stack.h b/src/gc/accounting/atomic_stack.h index 0197bce992..4e1c253bdf 100644 --- a/src/gc/atomic_stack.h +++ b/src/gc/accounting/atomic_stack.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_SRC_ATOMIC_STACK_H_ -#define ART_SRC_ATOMIC_STACK_H_ +#ifndef ART_SRC_GC_ACCOUNTING_ATOMIC_STACK_H_ +#define ART_SRC_GC_ACCOUNTING_ATOMIC_STACK_H_ #include <string> @@ -27,6 +27,8 @@ #include "utils.h" namespace art { +namespace gc { +namespace accounting { template <typename T> class AtomicStack { @@ -38,15 +40,14 @@ class AtomicStack { return mark_stack.release(); } - ~AtomicStack(){ - - } + ~AtomicStack() {} void Reset() { DCHECK(mem_map_.get() != NULL); DCHECK(begin_ != NULL); front_index_ = 0; back_index_ = 0; + is_sorted_ = true; int result = madvise(begin_, sizeof(T) * capacity_, MADV_DONTNEED); if (result == -1) { PLOG(WARNING) << "madvise failed"; @@ -58,6 +59,7 @@ class AtomicStack { // Returns false if we overflowed the stack. bool AtomicPushBack(const T& value) { int32_t index; + is_sorted_ = false; do { index = back_index_; if (UNLIKELY(static_cast<size_t>(index) >= capacity_)) { @@ -70,6 +72,7 @@ class AtomicStack { } void PushBack(const T& value) { + is_sorted_ = false; int32_t index = back_index_; DCHECK_LT(static_cast<size_t>(index), capacity_); back_index_ = index + 1; @@ -100,11 +103,11 @@ class AtomicStack { return back_index_ - front_index_; } - T* Begin() { + T* Begin() const { return const_cast<mirror::Object**>(begin_ + front_index_); } - T* End() { + T* End() const { return const_cast<mirror::Object**>(begin_ + back_index_); } @@ -118,14 +121,33 @@ class AtomicStack { Init(); } + void Sort() { + if (!is_sorted_) { + int32_t start_back_index = back_index_.get(); + int32_t start_front_index = front_index_.get(); + is_sorted_ = true; + std::sort(Begin(), End()); + CHECK_EQ(start_back_index, back_index_.get()); + CHECK_EQ(start_front_index, front_index_.get()); + } + } + + bool Contains(const T& value) const { + if (is_sorted_) { + return std::binary_search(Begin(), End(), value); + } else { + return std::find(Begin(), End(), value) != End(); + } + } + private: AtomicStack(const std::string& name, const size_t capacity) : name_(name), back_index_(0), front_index_(0), begin_(NULL), - capacity_(capacity) { - + capacity_(capacity), + is_sorted_(true) { } // Size in number of elements. @@ -156,11 +178,15 @@ class AtomicStack { // Maximum number of elements. size_t capacity_; + bool is_sorted_; + DISALLOW_COPY_AND_ASSIGN(AtomicStack); }; typedef AtomicStack<mirror::Object*> ObjectStack; +} // namespace accounting +} // namespace gc } // namespace art -#endif // ART_SRC_MARK_STACK_H_ +#endif // ART_SRC_GC_ACCOUNTING_ATOMIC_STACK_H_ diff --git a/src/gc/card_table-inl.h b/src/gc/accounting/card_table-inl.h index 13590b70a9..1e7529084a 100644 --- a/src/gc/card_table-inl.h +++ b/src/gc/accounting/card_table-inl.h @@ -24,6 +24,8 @@ #include "utils.h" namespace art { +namespace gc { +namespace accounting { static inline bool byte_cas(byte old_value, byte new_value, byte* address) { // Little endian means most significant byte is on the left. @@ -204,6 +206,8 @@ inline void CardTable::CheckCardValid(byte* card) const { << " end: " << reinterpret_cast<void*>(mem_map_->End()); } +} // namespace accounting +} // namespace gc } // namespace art #endif // ART_SRC_GC_CARDTABLE_INL_H_ diff --git a/src/gc/card_table.cc b/src/gc/accounting/card_table.cc index 57824e90db..4f2ae26c37 100644 --- a/src/gc/card_table.cc +++ b/src/gc/accounting/card_table.cc @@ -17,14 +17,17 @@ #include "card_table.h" #include "base/logging.h" -#include "gc/card_table-inl.h" -#include "heap.h" +#include "card_table-inl.h" +#include "gc/heap.h" +#include "gc/space/space.h" #include "heap_bitmap.h" #include "runtime.h" -#include "space.h" #include "utils.h" namespace art { +namespace gc { +namespace accounting { + /* * Maintain a card table from the write barrier. All writes of * non-NULL values to heap addresses should go through an entry in @@ -82,7 +85,7 @@ CardTable::CardTable(MemMap* mem_map, byte* biased_begin, size_t offset) byte* __attribute__((unused)) end = mem_map_->End(); } -void CardTable::ClearSpaceCards(ContinuousSpace* space) { +void CardTable::ClearSpaceCards(space::ContinuousSpace* space) { // TODO: clear just the range of the table that has been modified byte* card_start = CardFromAddr(space->Begin()); byte* card_end = CardFromAddr(space->End()); // Make sure to round up. @@ -116,4 +119,6 @@ void CardTable::VerifyCardTable() { UNIMPLEMENTED(WARNING) << "Card table verification"; } +} // namespace accounting +} // namespace gc } // namespace art diff --git a/src/gc/card_table.h b/src/gc/accounting/card_table.h index 842fcc3aa2..cf85d15448 100644 --- a/src/gc/card_table.h +++ b/src/gc/accounting/card_table.h @@ -23,11 +23,21 @@ #include "UniquePtr.h" namespace art { + namespace mirror { -class Object; + class Object; } // namespace mirror + +namespace gc { + +namespace space { + class ContinuousSpace; +} // namespace space + class Heap; -class ContinuousSpace; + +namespace accounting { + class SpaceBitmap; // Maintain a card table from the the write barrier. All writes of @@ -105,7 +115,7 @@ class CardTable { void ClearCardTable(); // Resets all of the bytes in the card table which do not map to the image space. - void ClearSpaceCards(ContinuousSpace* space); + void ClearSpaceCards(space::ContinuousSpace* space); // Returns the first address in the heap which maps to this card. void* AddrFromCard(const byte *card_addr) const; @@ -139,5 +149,8 @@ class CardTable { const size_t offset_; }; +} // namespace accounting +} // namespace gc } // namespace art + #endif // ART_SRC_GC_CARDTABLE_H_ diff --git a/src/gc/heap_bitmap-inl.h b/src/gc/accounting/heap_bitmap-inl.h index 281118359b..8e3123b974 100644 --- a/src/gc/heap_bitmap-inl.h +++ b/src/gc/accounting/heap_bitmap-inl.h @@ -14,23 +14,37 @@ * limitations under the License. */ -#ifndef ART_SRC_GC_HEAP_BITMAP_INL_H_ -#define ART_SRC_GC_HEAP_BITMAP_INL_H_ +#ifndef ART_SRC_GC_ACCOUNTING_HEAP_BITMAP_INL_H_ +#define ART_SRC_GC_ACCOUNTING_HEAP_BITMAP_INL_H_ #include "heap_bitmap.h" namespace art { +namespace gc { +namespace accounting { template <typename Visitor> inline void HeapBitmap::Visit(const Visitor& visitor) { // TODO: C++0x auto - for (Bitmaps::iterator it = bitmaps_.begin(); it != bitmaps_.end(); ++it) { + typedef std::vector<SpaceBitmap*>::iterator It; + for (It it = continuous_space_bitmaps_.begin(), end = continuous_space_bitmaps_.end(); + it != end; ++it) { SpaceBitmap* bitmap = *it; bitmap->VisitMarkedRange(bitmap->HeapBegin(), bitmap->HeapLimit(), visitor, VoidFunctor()); } - large_objects_->Visit(visitor); + // TODO: C++0x auto + typedef std::vector<SpaceSetMap*>::iterator It2; + DCHECK(discontinuous_space_sets_.begin() != discontinuous_space_sets_.end()); + for (It2 it = discontinuous_space_sets_.begin(), end = discontinuous_space_sets_.end(); + it != end; ++it) { + SpaceSetMap* set = *it; + set->Visit(visitor); + } + } +} // namespace accounting +} // namespace gc } // namespace art -#endif // ART_SRC_GC_HEAP_BITMAP_INL_H_ +#endif // ART_SRC_GC_ACCOUNTING_HEAP_BITMAP_INL_H_ diff --git a/src/gc/accounting/heap_bitmap.cc b/src/gc/accounting/heap_bitmap.cc new file mode 100644 index 0000000000..1bdc9783fa --- /dev/null +++ b/src/gc/accounting/heap_bitmap.cc @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "heap_bitmap.h" + +#include "gc/space/space.h" + +namespace art { +namespace gc { +namespace accounting { + +void HeapBitmap::ReplaceBitmap(SpaceBitmap* old_bitmap, SpaceBitmap* new_bitmap) { + // TODO: C++0x auto + typedef std::vector<SpaceBitmap*>::iterator It; + for (It it = continuous_space_bitmaps_.begin(), end = continuous_space_bitmaps_.end(); + it != end; ++it) { + if (*it == old_bitmap) { + *it = new_bitmap; + return; + } + } + LOG(FATAL) << "bitmap " << static_cast<const void*>(old_bitmap) << " not found"; +} + +void HeapBitmap::ReplaceObjectSet(SpaceSetMap* old_set, SpaceSetMap* new_set) { + // TODO: C++0x auto + typedef std::vector<SpaceSetMap*>::iterator It; + for (It it = discontinuous_space_sets_.begin(), end = discontinuous_space_sets_.end(); + it != end; ++it) { + if (*it == old_set) { + *it = new_set; + return; + } + } + LOG(FATAL) << "object set " << static_cast<const void*>(old_set) << " not found"; +} + +void HeapBitmap::AddContinuousSpaceBitmap(accounting::SpaceBitmap* bitmap) { + DCHECK(bitmap != NULL); + + // Check for interval overlap. + typedef std::vector<SpaceBitmap*>::iterator It; + for (It it = continuous_space_bitmaps_.begin(), end = continuous_space_bitmaps_.end(); + it != end; ++it) { + SpaceBitmap* bitmap = *it; + SpaceBitmap* cur_bitmap = *it; + CHECK(bitmap->HeapBegin() < cur_bitmap->HeapLimit() && + bitmap->HeapLimit() > cur_bitmap->HeapBegin()) + << "Bitmap " << bitmap->Dump() << " overlaps with existing bitmap " << cur_bitmap->Dump(); + } + continuous_space_bitmaps_.push_back(bitmap); +} + +void HeapBitmap::AddDiscontinuousObjectSet(SpaceSetMap* set) { + DCHECK(set != NULL); + discontinuous_space_sets_.push_back(set); +} + +void HeapBitmap::Walk(SpaceBitmap::Callback* callback, void* arg) { + // TODO: C++0x auto + typedef std::vector<SpaceBitmap*>::iterator It; + for (It it = continuous_space_bitmaps_.begin(), end = continuous_space_bitmaps_.end(); + it != end; ++it) { + SpaceBitmap* bitmap = *it; + bitmap->Walk(callback, arg); + } + // TODO: C++0x auto + typedef std::vector<SpaceSetMap*>::iterator It2; + DCHECK(discontinuous_space_sets_.begin() != discontinuous_space_sets_.end()); + for (It2 it = discontinuous_space_sets_.begin(), end = discontinuous_space_sets_.end(); + it != end; ++it) { + SpaceSetMap* set = *it; + set->Walk(callback, arg); + } +} + +} // namespace accounting +} // namespace gc +} // namespace art diff --git a/src/gc/accounting/heap_bitmap.h b/src/gc/accounting/heap_bitmap.h new file mode 100644 index 0000000000..5ff40c6426 --- /dev/null +++ b/src/gc/accounting/heap_bitmap.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_GC_ACCOUNTING_HEAP_BITMAP_H_ +#define ART_SRC_GC_ACCOUNTING_HEAP_BITMAP_H_ + +#include "base/logging.h" +#include "locks.h" +#include "space_bitmap.h" + +namespace art { +namespace gc { + +class Heap; + +namespace accounting { + +class HeapBitmap { + public: + bool Test(const mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + SpaceBitmap* bitmap = GetContinuousSpaceBitmap(obj); + if (LIKELY(bitmap != NULL)) { + return bitmap->Test(obj); + } else { + return GetDiscontinuousSpaceObjectSet(obj) != NULL; + } + } + + void Clear(const mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + SpaceBitmap* bitmap = GetContinuousSpaceBitmap(obj); + if (LIKELY(bitmap != NULL)) { + bitmap->Clear(obj); + } else { + SpaceSetMap* set = GetDiscontinuousSpaceObjectSet(obj); + DCHECK(set != NULL); + set->Clear(obj); + } + } + + void Set(const mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + SpaceBitmap* bitmap = GetContinuousSpaceBitmap(obj); + if (LIKELY(bitmap != NULL)) { + bitmap->Set(obj); + } else { + SpaceSetMap* set = GetDiscontinuousSpaceObjectSet(obj); + DCHECK(set != NULL); + set->Set(obj); + } + } + + SpaceBitmap* GetContinuousSpaceBitmap(const mirror::Object* obj) { + // TODO: C++0x auto + typedef std::vector<SpaceBitmap*>::iterator It; + for (It it = continuous_space_bitmaps_.begin(), end = continuous_space_bitmaps_.end(); + it != end; ++it) { + SpaceBitmap* bitmap = *it; + if (bitmap->HasAddress(obj)) { + return bitmap; + } + } + return NULL; + } + + SpaceSetMap* GetDiscontinuousSpaceObjectSet(const mirror::Object* obj) { + // TODO: C++0x auto + typedef std::vector<SpaceSetMap*>::iterator It; + for (It it = discontinuous_space_sets_.begin(), end = discontinuous_space_sets_.end(); + it != end; ++it) { + SpaceSetMap* set = *it; + if (set->Test(obj)) { + return set; + } + } + return NULL; + } + + void Walk(SpaceBitmap::Callback* callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + template <typename Visitor> + void Visit(const Visitor& visitor) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Find and replace a bitmap pointer, this is used by for the bitmap swapping in the GC. + void ReplaceBitmap(SpaceBitmap* old_bitmap, SpaceBitmap* new_bitmap) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Find and replace a object set pointer, this is used by for the bitmap swapping in the GC. + void ReplaceObjectSet(SpaceSetMap* old_set, SpaceSetMap* new_set) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + HeapBitmap(Heap* heap) : heap_(heap) { + } + + private: + + const Heap* const heap_; + + void AddContinuousSpaceBitmap(SpaceBitmap* bitmap); + void AddDiscontinuousObjectSet(SpaceSetMap* set); + + // Bitmaps covering continuous spaces. + std::vector<SpaceBitmap*> continuous_space_bitmaps_; + + // Sets covering discontinuous spaces. + std::vector<SpaceSetMap*> discontinuous_space_sets_; + + friend class art::gc::Heap; +}; + +} // namespace accounting +} // namespace gc +} // namespace art + +#endif // ART_SRC_GC_ACCOUNTING_HEAP_BITMAP_H_ diff --git a/src/gc/accounting/mod_union_table-inl.h b/src/gc/accounting/mod_union_table-inl.h new file mode 100644 index 0000000000..656af94853 --- /dev/null +++ b/src/gc/accounting/mod_union_table-inl.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_GC_MOD_UNION_TABLE_INL_H_ +#define ART_SRC_GC_MOD_UNION_TABLE_INL_H_ + +#include "mod_union_table.h" + +#include "gc/space/space.h" + +namespace art { +namespace gc { +namespace accounting { + +// A mod-union table to record image references to the Zygote and alloc space. +class ModUnionTableToZygoteAllocspace : public ModUnionTableReferenceCache { +public: + ModUnionTableToZygoteAllocspace(Heap* heap) : ModUnionTableReferenceCache(heap) { + } + + bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) { + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(); it != spaces.end(); ++it) { + if ((*it)->Contains(ref)) { + return (*it)->IsDlMallocSpace(); + } + } + // Assume it points to a large object. + // TODO: Check. + return true; + } +}; + +// A mod-union table to record Zygote references to the alloc space. +class ModUnionTableToAllocspace : public ModUnionTableReferenceCache { + public: + ModUnionTableToAllocspace(Heap* heap) : ModUnionTableReferenceCache(heap) { + } + + bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) { + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(); it != spaces.end(); ++it) { + space::ContinuousSpace* space = *it; + if (space->Contains(ref)) { + // The allocation space is always considered for collection whereas the Zygote space is + // + return space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect; + } + } + // Assume it points to a large object. + // TODO: Check. + return true; + } +}; + +} // namespace accounting +} // namespace gc +} // namespace art + +#endif // ART_SRC_GC_MOD_UNION_TABLE_INL_H_ diff --git a/src/gc/mod_union_table.cc b/src/gc/accounting/mod_union_table.cc index da950bb9b8..05b68c4922 100644 --- a/src/gc/mod_union_table.cc +++ b/src/gc/accounting/mod_union_table.cc @@ -18,15 +18,14 @@ #include "base/stl_util.h" #include "card_table-inl.h" -#include "heap.h" #include "heap_bitmap.h" -#include "mark_sweep.h" -#include "mark_sweep-inl.h" +#include "gc/collector/mark_sweep-inl.h" +#include "gc/heap.h" +#include "gc/space/space.h" #include "mirror/object-inl.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" #include "mirror/object_array-inl.h" -#include "space.h" #include "space_bitmap-inl.h" #include "thread.h" #include "UniquePtr.h" @@ -34,21 +33,25 @@ using namespace art::mirror; namespace art { +namespace gc { +namespace accounting { class MarkIfReachesAllocspaceVisitor { public: - explicit MarkIfReachesAllocspaceVisitor(Heap* const heap, SpaceBitmap* bitmap) + explicit MarkIfReachesAllocspaceVisitor(Heap* const heap, accounting::SpaceBitmap* bitmap) : heap_(heap), bitmap_(bitmap) { } // Extra parameters are required since we use this same visitor signature for checking objects. - void operator ()(const Object* obj, const Object* ref, const MemberOffset& /* offset */, bool /* is_static */) const { + void operator ()(const Object* obj, const Object* ref, const MemberOffset& /* offset */, + bool /* is_static */) const { // TODO: Optimize? // TODO: C++0x auto - const Spaces& spaces = heap_->GetSpaces(); - for (Spaces::const_iterator cur = spaces.begin(); cur != spaces.end(); ++cur) { - if ((*cur)->IsAllocSpace() && (*cur)->Contains(ref)) { + const std::vector<space::ContinuousSpace*>& spaces = heap_->GetContinuousSpaces(); + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It cur = spaces.begin(); cur != spaces.end(); ++cur) { + if ((*cur)->IsDlMallocSpace() && (*cur)->Contains(ref)) { bitmap_->Set(obj); break; } @@ -57,12 +60,12 @@ class MarkIfReachesAllocspaceVisitor { private: Heap* const heap_; - SpaceBitmap* bitmap_; + accounting::SpaceBitmap* const bitmap_; }; class ModUnionVisitor { public: - explicit ModUnionVisitor(Heap* const heap, SpaceBitmap* bitmap) + explicit ModUnionVisitor(Heap* const heap, accounting::SpaceBitmap* bitmap) : heap_(heap), bitmap_(bitmap) { } @@ -74,11 +77,11 @@ class ModUnionVisitor { // We don't have an early exit since we use the visitor pattern, an early exit should // significantly speed this up. MarkIfReachesAllocspaceVisitor visitor(heap_, bitmap_); - MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(obj, visitor); } private: Heap* const heap_; - SpaceBitmap* bitmap_; + accounting::SpaceBitmap* const bitmap_; }; class ModUnionClearCardSetVisitor { @@ -109,65 +112,12 @@ class ModUnionClearCardVisitor { } } private: - std::vector<byte*>* cleared_cards_; + std::vector<byte*>* const cleared_cards_; }; -ModUnionTableBitmap::ModUnionTableBitmap(Heap* heap) : ModUnionTable(heap) { - // Prevent fragmentation of the heap which is caused by resizing of the vector. - // TODO: Make a new vector which uses madvise (basically same as a mark stack). - cleared_cards_.reserve(32); - const Spaces& spaces = heap->GetSpaces(); - // Create one heap bitmap per image space. - // TODO: C++0x auto - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - ContinuousSpace* space = *it; - if (space->IsImageSpace()) { - // The mod-union table is only needed when we have an image space since it's purpose is to - // cache image roots. - UniquePtr<SpaceBitmap> bitmap(SpaceBitmap::Create("mod-union table bitmap", space->Begin(), - space->Size())); - CHECK(bitmap.get() != NULL) << "Failed to create mod-union bitmap"; - bitmaps_.Put(space, bitmap.release()); - } - } -} - -ModUnionTableBitmap::~ModUnionTableBitmap() { - STLDeleteValues(&bitmaps_); -} - -void ModUnionTableBitmap::ClearCards(ContinuousSpace* space) { - CardTable* card_table = heap_->GetCardTable(); - ModUnionClearCardVisitor visitor(&cleared_cards_); - // Clear dirty cards in the this image space and update the corresponding mod-union bits. - card_table->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), visitor); -} - -void ModUnionTableBitmap::Update() { - CardTable* card_table = heap_->GetCardTable(); - while (!cleared_cards_.empty()) { - byte* card = cleared_cards_.back(); - cleared_cards_.pop_back(); - - uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card)); - uintptr_t end = start + CardTable::kCardSize; - ContinuousSpace* space = heap_->FindSpaceFromObject(reinterpret_cast<Object*>(start)); - SpaceBitmap* bitmap = space->GetLiveBitmap(); - - // Clear the mod-union bitmap range corresponding to this card so that we don't have any - // objects marked which do not reach the alloc space. - bitmap->VisitRange(start, end, SpaceBitmap::ClearVisitor(bitmap)); - - // At this point we need to update the mod-union bitmap to contain all the objects which reach - // the alloc space. - ModUnionVisitor visitor(heap_, bitmap); - space->GetLiveBitmap()->VisitMarkedRange(start, end, visitor, VoidFunctor()); - } -} - class ModUnionScanImageRootVisitor { public: - ModUnionScanImageRootVisitor(MarkSweep* const mark_sweep) : mark_sweep_(mark_sweep) { + ModUnionScanImageRootVisitor(collector::MarkSweep* const mark_sweep) : mark_sweep_(mark_sweep) { } void operator ()(const Object* root) const @@ -178,30 +128,10 @@ class ModUnionScanImageRootVisitor { } private: - MarkSweep* const mark_sweep_; + collector::MarkSweep* const mark_sweep_; }; -void ModUnionTableBitmap::MarkReferences(MarkSweep* mark_sweep) { - // Some tests have no image space, and therefore no mod-union bitmap. - ModUnionScanImageRootVisitor image_root_scanner(mark_sweep); - for (BitmapMap::iterator it = bitmaps_.begin(); it != bitmaps_.end(); ++it) { - const ContinuousSpace* space = it->first; - uintptr_t begin = reinterpret_cast<uintptr_t>(space->Begin()); - uintptr_t end = reinterpret_cast<uintptr_t>(space->End()); - it->second->VisitMarkedRange(begin, end, image_root_scanner, VoidFunctor()); - } -} - - -ModUnionTableReferenceCache::ModUnionTableReferenceCache(Heap* heap) : ModUnionTable(heap) { - -} - -ModUnionTableReferenceCache::~ModUnionTableReferenceCache() { - -} - -void ModUnionTableReferenceCache::ClearCards(ContinuousSpace* space) { +void ModUnionTableReferenceCache::ClearCards(space::ContinuousSpace* space) { CardTable* card_table = GetHeap()->GetCardTable(); ModUnionClearCardSetVisitor visitor(&cleared_cards_); // Clear dirty cards in the this space and update the corresponding mod-union bits. @@ -210,9 +140,8 @@ void ModUnionTableReferenceCache::ClearCards(ContinuousSpace* space) { class AddToReferenceArrayVisitor { public: - explicit AddToReferenceArrayVisitor( - ModUnionTableReferenceCache* const mod_union_table, - ModUnionTableReferenceCache::ReferenceArray* references) + explicit AddToReferenceArrayVisitor(ModUnionTableReferenceCache* mod_union_table, + std::vector<const mirror::Object*>* references) : mod_union_table_(mod_union_table), references_(references) { } @@ -227,41 +156,35 @@ class AddToReferenceArrayVisitor { } private: - ModUnionTableReferenceCache* mod_union_table_; - ModUnionTable::ReferenceArray* references_; + ModUnionTableReferenceCache* const mod_union_table_; + std::vector<const mirror::Object*>* const references_; }; class ModUnionReferenceVisitor { public: - explicit ModUnionReferenceVisitor( - ModUnionTableReferenceCache* const mod_union_table, - ModUnionTableReferenceCache::ReferenceArray* references) + explicit ModUnionReferenceVisitor(ModUnionTableReferenceCache* const mod_union_table, + std::vector<const mirror::Object*>* references) : mod_union_table_(mod_union_table), references_(references) { } void operator ()(const Object* obj) const - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, - Locks::mutator_lock_) { + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { DCHECK(obj != NULL); // We don't have an early exit since we use the visitor pattern, an early // exit should significantly speed this up. AddToReferenceArrayVisitor visitor(mod_union_table_, references_); - MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(obj, visitor); } private: ModUnionTableReferenceCache* const mod_union_table_; - ModUnionTable::ReferenceArray* references_; + std::vector<const mirror::Object*>* const references_; }; - class CheckReferenceVisitor { public: - typedef std::set<const Object*> ReferenceSet; - - explicit CheckReferenceVisitor( - ModUnionTableReferenceCache* const mod_union_table, - const ReferenceSet& references) + explicit CheckReferenceVisitor(ModUnionTableReferenceCache* mod_union_table, + const std::set<const Object*>& references) : mod_union_table_(mod_union_table), references_(references) { } @@ -269,13 +192,13 @@ class CheckReferenceVisitor { // Extra parameters are required since we use this same visitor signature for checking objects. // TODO: Fixme when anotatalysis works with visitors. void operator ()(const Object* obj, const Object* ref, const MemberOffset& /* offset */, - bool /* is_static */) const + bool /* is_static */) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { Heap* heap = mod_union_table_->GetHeap(); if (ref != NULL && mod_union_table_->AddReference(obj, ref) && references_.find(ref) == references_.end()) { - ContinuousSpace* from_space = heap->FindSpaceFromObject(obj); - ContinuousSpace* to_space = heap->FindSpaceFromObject(ref); + space::ContinuousSpace* from_space = heap->FindContinuousSpaceFromObject(obj, false); + space::ContinuousSpace* to_space = heap->FindContinuousSpaceFromObject(ref, false); LOG(INFO) << "Object " << reinterpret_cast<const void*>(obj) << "(" << PrettyTypeOf(obj) << ")" << "References " << reinterpret_cast<const void*>(ref) << "(" << PrettyTypeOf(ref) << ") without being in mod-union table"; @@ -288,83 +211,108 @@ class CheckReferenceVisitor { private: ModUnionTableReferenceCache* const mod_union_table_; - const ReferenceSet& references_; + const std::set<const Object*>& references_; }; class ModUnionCheckReferences { public: - typedef std::set<const Object*> ReferenceSet; - - explicit ModUnionCheckReferences ( - ModUnionTableReferenceCache* const mod_union_table, - const ReferenceSet& references) - : mod_union_table_(mod_union_table), - references_(references) { + explicit ModUnionCheckReferences (ModUnionTableReferenceCache* mod_union_table, + const std::set<const Object*>& references) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + : mod_union_table_(mod_union_table), references_(references) { } void operator ()(const Object* obj) const NO_THREAD_SAFETY_ANALYSIS { + Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current()); DCHECK(obj != NULL); - if (kDebugLocking) { - Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current()); - } CheckReferenceVisitor visitor(mod_union_table_, references_); - MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(obj, visitor); } private: ModUnionTableReferenceCache* const mod_union_table_; - const ReferenceSet& references_; + const std::set<const Object*>& references_; }; void ModUnionTableReferenceCache::Verify() { // Start by checking that everything in the mod union table is marked. Heap* heap = GetHeap(); - for (ReferenceMap::const_iterator it = references_.begin(); it != references_.end(); ++it) { - for (ReferenceArray::const_iterator it_ref = it->second.begin(); it_ref != it->second.end(); + typedef SafeMap<const byte*, std::vector<const mirror::Object*> >::const_iterator It; + typedef std::vector<const mirror::Object*>::const_iterator It2; + for (It it = references_.begin(), end = references_.end(); it != end; ++it) { + for (It2 it_ref = it->second.begin(), end_ref = it->second.end(); it_ref != end_ref; ++it_ref ) { - DCHECK(heap->GetLiveBitmap()->Test(*it_ref)); + CHECK(heap->IsLiveObjectLocked(*it_ref)); } } // Check the references of each clean card which is also in the mod union table. - for (ReferenceMap::const_iterator it = references_.begin(); it != references_.end(); ++it) { + CardTable* card_table = heap->GetCardTable(); + for (It it = references_.begin(); it != references_.end(); ++it) { const byte* card = &*it->first; if (*card == CardTable::kCardClean) { std::set<const Object*> reference_set; - for (ReferenceArray::const_iterator itr = it->second.begin(); itr != it->second.end();++itr) { + for (It2 itr = it->second.begin(); itr != it->second.end();++itr) { reference_set.insert(*itr); } ModUnionCheckReferences visitor(this, reference_set); - CardTable* card_table = heap->GetCardTable(); uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card)); uintptr_t end = start + CardTable::kCardSize; - SpaceBitmap* live_bitmap = - heap->FindSpaceFromObject(reinterpret_cast<Object*>(start))->GetLiveBitmap(); + space::ContinuousSpace* space = + heap->FindContinuousSpaceFromObject(reinterpret_cast<Object*>(start), false); + SpaceBitmap* live_bitmap = space->GetLiveBitmap(); live_bitmap->VisitMarkedRange(start, end, visitor, VoidFunctor()); } } } +void ModUnionTableReferenceCache::Dump(std::ostream& os) { + CardTable* card_table = heap_->GetCardTable(); + typedef std::set<byte*>::const_iterator It; + os << "ModUnionTable cleared cards: ["; + for (It it = cleared_cards_.begin(); it != cleared_cards_.end(); ++it) { + byte* card = *it; + uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card)); + uintptr_t end = start + CardTable::kCardSize; + os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << ","; + } + os << "]\nModUnionTable references: ["; + typedef SafeMap<const byte*, std::vector<const mirror::Object*> >::const_iterator It2; + for (It2 it = references_.begin(); it != references_.end(); ++it) { + const byte* card = &*it->first; + uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card)); + uintptr_t end = start + CardTable::kCardSize; + os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << "->{"; + typedef std::vector<const mirror::Object*>::const_iterator It3; + for (It3 itr = it->second.begin(); itr != it->second.end();++itr) { + os << reinterpret_cast<const void*>(*itr) << ","; + } + os << "},"; + } +} + void ModUnionTableReferenceCache::Update() { Heap* heap = GetHeap(); CardTable* card_table = heap->GetCardTable(); - ReferenceArray cards_references; + std::vector<const mirror::Object*> cards_references; ModUnionReferenceVisitor visitor(this, &cards_references); - for (ClearedCards::iterator it = cleared_cards_.begin(); it != cleared_cards_.end(); ++it) { + typedef std::set<byte*>::iterator It; + for (It it = cleared_cards_.begin(), cc_end = cleared_cards_.end(); it != cc_end; ++it) { byte* card = *it; // Clear and re-compute alloc space references associated with this card. cards_references.clear(); uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card)); uintptr_t end = start + CardTable::kCardSize; SpaceBitmap* live_bitmap = - heap->FindSpaceFromObject(reinterpret_cast<Object*>(start))->GetLiveBitmap(); + heap->FindContinuousSpaceFromObject(reinterpret_cast<Object*>(start), false)->GetLiveBitmap(); live_bitmap->VisitMarkedRange(start, end, visitor, VoidFunctor()); // Update the corresponding references for the card. // TODO: C++0x auto - ReferenceMap::iterator found = references_.find(card); + SafeMap<const byte*, std::vector<const mirror::Object*> >::iterator + found = references_.find(card); if (found == references_.end()) { if (cards_references.empty()) { // No reason to add empty array. @@ -378,11 +326,14 @@ void ModUnionTableReferenceCache::Update() { cleared_cards_.clear(); } -void ModUnionTableReferenceCache::MarkReferences(MarkSweep* mark_sweep) { +void ModUnionTableReferenceCache::MarkReferences(collector::MarkSweep* mark_sweep) { // TODO: C++0x auto size_t count = 0; - for (ReferenceMap::const_iterator it = references_.begin(); it != references_.end(); ++it) { - for (ReferenceArray::const_iterator it_ref = it->second.begin(); it_ref != it->second.end(); ++it_ref ) { + + typedef SafeMap<const byte*, std::vector<const mirror::Object*> >::const_iterator It; + for (It it = references_.begin(); it != references_.end(); ++it) { + typedef std::vector<const mirror::Object*>::const_iterator It2; + for (It2 it_ref = it->second.begin(); it_ref != it->second.end(); ++it_ref ) { mark_sweep->MarkRoot(*it_ref); ++count; } @@ -392,15 +343,7 @@ void ModUnionTableReferenceCache::MarkReferences(MarkSweep* mark_sweep) { } } -ModUnionTableCardCache::ModUnionTableCardCache(Heap* heap) : ModUnionTable(heap) { - -} - -ModUnionTableCardCache::~ModUnionTableCardCache() { - -} - -void ModUnionTableCardCache::ClearCards(ContinuousSpace* space) { +void ModUnionTableCardCache::ClearCards(space::ContinuousSpace* space) { CardTable* card_table = GetHeap()->GetCardTable(); ModUnionClearCardSetVisitor visitor(&cleared_cards_); // Clear dirty cards in the this space and update the corresponding mod-union bits. @@ -408,17 +351,46 @@ void ModUnionTableCardCache::ClearCards(ContinuousSpace* space) { } // Mark all references to the alloc space(s). -void ModUnionTableCardCache::MarkReferences(MarkSweep* mark_sweep) { +void ModUnionTableCardCache::MarkReferences(collector::MarkSweep* mark_sweep) { CardTable* card_table = heap_->GetCardTable(); ModUnionScanImageRootVisitor visitor(mark_sweep); - for (ClearedCards::const_iterator it = cleared_cards_.begin(); it != cleared_cards_.end(); ++it) { + typedef std::set<byte*>::const_iterator It; + It it = cleared_cards_.begin(); + It cc_end = cleared_cards_.end(); + if (it != cc_end) { byte* card = *it; uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card)); uintptr_t end = start + CardTable::kCardSize; - SpaceBitmap* live_bitmap = - heap_->FindSpaceFromObject(reinterpret_cast<Object*>(start))->GetLiveBitmap(); - live_bitmap->VisitMarkedRange(start, end, visitor, VoidFunctor()); + space::ContinuousSpace* cur_space = + heap_->FindContinuousSpaceFromObject(reinterpret_cast<Object*>(start), false); + accounting::SpaceBitmap* cur_live_bitmap = cur_space->GetLiveBitmap(); + cur_live_bitmap->VisitMarkedRange(start, end, visitor, VoidFunctor()); + for (++it; it != cc_end; ++it) { + card = *it; + start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card)); + end = start + CardTable::kCardSize; + if (UNLIKELY(!cur_space->Contains(reinterpret_cast<Object*>(start)))) { + cur_space = heap_->FindContinuousSpaceFromObject(reinterpret_cast<Object*>(start), false); + cur_live_bitmap = cur_space->GetLiveBitmap(); + } + cur_live_bitmap->VisitMarkedRange(start, end, visitor, VoidFunctor()); + } + } +} + +void ModUnionTableCardCache::Dump(std::ostream& os) { + CardTable* card_table = heap_->GetCardTable(); + typedef std::set<byte*>::const_iterator It; + os << "ModUnionTable dirty cards: ["; + for (It it = cleared_cards_.begin(); it != cleared_cards_.end(); ++it) { + byte* card = *it; + uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card)); + uintptr_t end = start + CardTable::kCardSize; + os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << ","; } + os << "]"; } +} // namespace accounting +} // namespace gc } // namespace art diff --git a/src/gc/mod_union_table.h b/src/gc/accounting/mod_union_table.h index c0b95357e6..5d25e05658 100644 --- a/src/gc/mod_union_table.h +++ b/src/gc/accounting/mod_union_table.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_SRC_GC_MOD_UNION_TABLE_H_ -#define ART_SRC_GC_MOD_UNION_TABLE_H_ +#ifndef ART_SRC_GC_ACCOUNTING_MOD_UNION_TABLE_H_ +#define ART_SRC_GC_ACCOUNTING_MOD_UNION_TABLE_H_ #include "globals.h" #include "safe_map.h" @@ -25,37 +25,47 @@ namespace art { namespace mirror { -class Object; -} -class ContinuousSpace; + class Object; +} // namespace mirror + +namespace gc { + +namespace collector { + class MarkSweep; +} // namespace collector +namespace space { + class ContinuousSpace; + class Space; +} // namespace space + class Heap; -class HeapBitmap; -class MarkSweep; -class Space; + +namespace accounting { + class SpaceBitmap; +class HeapBitmap; -// Base class +// The mod-union table is the union of modified cards. It is used to allow the card table to be +// cleared between GC phases, reducing the number of dirty cards that need to be scanned. class ModUnionTable { public: - typedef std::vector<const mirror::Object*> ReferenceArray; - typedef std::set<byte*> ClearedCards; - ModUnionTable(Heap* heap) : heap_(heap) { - } virtual ~ModUnionTable() { - } - // Clear cards which map to a memory range of a space. - virtual void ClearCards(ContinuousSpace* space) = 0; + // Clear cards which map to a memory range of a space. This doesn't immediately update the + // mod-union table, as updating the mod-union table may have an associated cost, such as + // determining references to track. + virtual void ClearCards(space::ContinuousSpace* space) = 0; - // Update the mod-union table. + // Update the mod-union table using data stored by ClearCards. There may be multiple ClearCards + // before a call to update, for example, back-to-back sticky GCs. virtual void Update() = 0; - // Mark all references which are stored in the mod union table. - virtual void MarkReferences(MarkSweep* mark_sweep) = 0; + // Mark the bitmaps for all references which are stored in the mod-union table. + virtual void MarkReferences(collector::MarkSweep* mark_sweep) = 0; // Verification, sanity checks that we don't have clean cards which conflict with out cached data // for said cards. Exclusive lock is required since verify sometimes uses @@ -63,6 +73,8 @@ class ModUnionTable { // bitmap or not. virtual void Verify() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) = 0; + virtual void Dump(std::ostream& os) = 0; + Heap* GetHeap() const { return heap_; } @@ -71,44 +83,14 @@ class ModUnionTable { Heap* const heap_; }; -// Bitmap implementation. -// DEPRECATED, performs strictly less well than merely caching which cards were dirty. -class ModUnionTableBitmap : public ModUnionTable { - public: - ModUnionTableBitmap(Heap* heap); - virtual ~ModUnionTableBitmap(); - - // Clear space cards. - void ClearCards(ContinuousSpace* space); - - // Update table based on cleared cards. - void Update() - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Mark all references to the alloc space(s). - void MarkReferences(MarkSweep* mark_sweep) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - - protected: - // Cleared card array, used to update the mod-union table. - std::vector<byte*> cleared_cards_; - - // One bitmap per image space. - // TODO: Add support for Zygote spaces? - typedef SafeMap<ContinuousSpace*, SpaceBitmap*> BitmapMap; - BitmapMap bitmaps_; -}; - // Reference caching implementation. Caches references pointing to alloc space(s) for each card. class ModUnionTableReferenceCache : public ModUnionTable { public: - typedef SafeMap<const byte*, ReferenceArray > ReferenceMap; - - ModUnionTableReferenceCache(Heap* heap); - virtual ~ModUnionTableReferenceCache(); + ModUnionTableReferenceCache(Heap* heap) : ModUnionTable(heap) {} + virtual ~ModUnionTableReferenceCache() {} // Clear and store cards for a space. - void ClearCards(ContinuousSpace* space); + void ClearCards(space::ContinuousSpace* space); // Update table based on cleared cards. void Update() @@ -116,7 +98,7 @@ class ModUnionTableReferenceCache : public ModUnionTable { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Mark all references to the alloc space(s). - void MarkReferences(MarkSweep* mark_sweep) + void MarkReferences(collector::MarkSweep* mark_sweep) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); @@ -127,41 +109,45 @@ class ModUnionTableReferenceCache : public ModUnionTable { // Function that tells whether or not to add a reference to the table. virtual bool AddReference(const mirror::Object* obj, const mirror::Object* ref) = 0; + void Dump(std::ostream& os); + protected: // Cleared card array, used to update the mod-union table. - ClearedCards cleared_cards_; + std::set<byte*> cleared_cards_; // Maps from dirty cards to their corresponding alloc space references. - ReferenceMap references_; + SafeMap<const byte*, std::vector<const mirror::Object*> > references_; }; // Card caching implementation. Keeps track of which cards we cleared and only this information. class ModUnionTableCardCache : public ModUnionTable { public: - typedef SafeMap<const byte*, ReferenceArray > ReferenceMap; - - ModUnionTableCardCache(Heap* heap); - virtual ~ModUnionTableCardCache(); + ModUnionTableCardCache(Heap* heap) : ModUnionTable(heap) {} + virtual ~ModUnionTableCardCache() {} // Clear and store cards for a space. - void ClearCards(ContinuousSpace* space); + void ClearCards(space::ContinuousSpace* space); - // Nothing to update. + // Nothing to update as all dirty cards were placed into cleared cards during clearing. void Update() {} // Mark all references to the alloc space(s). - void MarkReferences(MarkSweep* mark_sweep) + void MarkReferences(collector::MarkSweep* mark_sweep) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Nothing to verify. void Verify() {} + void Dump(std::ostream& os); + protected: // Cleared card array, used to update the mod-union table. - ClearedCards cleared_cards_; + std::set<byte*> cleared_cards_; }; +} // namespace accounting +} // namespace gc } // namespace art -#endif // ART_SRC_GC_MOD_UNION_TABLE_H_ +#endif // ART_SRC_GC_ACCOUNTING_MOD_UNION_TABLE_H_ diff --git a/src/gc/space_bitmap-inl.h b/src/gc/accounting/space_bitmap-inl.h index dd91403756..a4fd330c8f 100644 --- a/src/gc/space_bitmap-inl.h +++ b/src/gc/accounting/space_bitmap-inl.h @@ -14,13 +14,16 @@ * limitations under the License. */ -#ifndef ART_SRC_GC_SPACE_BITMAP_INL_H_ -#define ART_SRC_GC_SPACE_BITMAP_INL_H_ +#ifndef ART_SRC_GC_ACCOUNTING_SPACE_BITMAP_INL_H_ +#define ART_SRC_GC_ACCOUNTING_SPACE_BITMAP_INL_H_ #include "base/logging.h" #include "cutils/atomic-inline.h" +#include "utils.h" namespace art { +namespace gc { +namespace accounting { inline bool SpaceBitmap::AtomicTestAndSet(const mirror::Object* obj) { uintptr_t addr = reinterpret_cast<uintptr_t>(obj); @@ -136,6 +139,9 @@ inline bool SpaceBitmap::Modify(const mirror::Object* obj, bool do_set) { } return (old_word & mask) != 0; } + +} // namespace accounting +} // namespace gc } // namespace art -#endif // ART_SRC_GC_SPACE_BITMAP_INL_H_ +#endif // ART_SRC_GC_ACCOUNTING_SPACE_BITMAP_INL_H_ diff --git a/src/gc/space_bitmap.cc b/src/gc/accounting/space_bitmap.cc index 773aa1e707..19f1128963 100644 --- a/src/gc/space_bitmap.cc +++ b/src/gc/accounting/space_bitmap.cc @@ -14,19 +14,21 @@ * limitations under the License. */ -#include "heap_bitmap.h" - #include "base/logging.h" #include "dex_file-inl.h" +#include "heap_bitmap.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "object_utils.h" #include "space_bitmap-inl.h" #include "UniquePtr.h" #include "utils.h" namespace art { +namespace gc { +namespace accounting { std::string SpaceBitmap::GetName() const { return name_; @@ -36,6 +38,12 @@ void SpaceBitmap::SetName(const std::string& name) { name_ = name; } +std::string SpaceBitmap::Dump() const { + return StringPrintf("%s: %p-%p", name_.c_str(), + reinterpret_cast<void*>(HeapBegin()), + reinterpret_cast<void*>(HeapLimit())); +} + void SpaceSetMap::Walk(SpaceBitmap::Callback* callback, void* arg) { for (Objects::iterator it = contained_.begin(); it != contained_.end(); ++it) { callback(const_cast<mirror::Object*>(*it), arg); @@ -72,8 +80,6 @@ void SpaceBitmap::SetHeapLimit(uintptr_t new_end) { // mem_map_->Trim(reinterpret_cast<byte*>(heap_begin_ + bitmap_size_)); } -// Fill the bitmap with zeroes. Returns the bitmap's memory to the -// system as a side-effect. void SpaceBitmap::Clear() { if (bitmap_begin_ != NULL) { // This returns the memory to the system. Successive page faults @@ -164,14 +170,6 @@ void SpaceBitmap::SweepWalk(const SpaceBitmap& live_bitmap, } } -} // namespace art - -// Support needed for in order traversal -#include "mirror/object.h" -#include "object_utils.h" - -namespace art { - static void WalkFieldsInOrder(SpaceBitmap* visited, SpaceBitmap::Callback* callback, mirror::Object* obj, void* arg); @@ -273,10 +271,6 @@ void SpaceSetMap::SetName(const std::string& name) { name_ = name; } -SpaceSetMap::SpaceSetMap(const std::string& name) : name_(name) { - -} - void SpaceSetMap::CopyFrom(const SpaceSetMap& space_set) { contained_ = space_set.contained_; } @@ -287,6 +281,8 @@ std::ostream& operator << (std::ostream& stream, const SpaceBitmap& bitmap) { << "begin=" << reinterpret_cast<const void*>(bitmap.HeapBegin()) << ",end=" << reinterpret_cast<const void*>(bitmap.HeapLimit()) << "]"; - } +} +} // namespace accounting +} // namespace gc } // namespace art diff --git a/src/gc/space_bitmap.h b/src/gc/accounting/space_bitmap.h index 6bc06d600d..bb487d88d0 100644 --- a/src/gc/space_bitmap.h +++ b/src/gc/accounting/space_bitmap.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_SRC_GC_SPACE_BITMAP_H_ -#define ART_SRC_GC_SPACE_BITMAP_H_ +#ifndef ART_SRC_GC_ACCOUNTING_SPACE_BITMAP_H_ +#define ART_SRC_GC_ACCOUNTING_SPACE_BITMAP_H_ #include "locks.h" #include "globals.h" @@ -28,12 +28,17 @@ #include <vector> namespace art { + namespace mirror { -class Object; + class Object; } // namespace mirror +namespace gc { +namespace accounting { + class SpaceBitmap { public: + // Alignment of objects within spaces. static const size_t kAlignment = 8; typedef void Callback(mirror::Object* obj, void* arg); @@ -52,7 +57,7 @@ class SpaceBitmap { // <index> is the index of .bits that contains the bit representing // <offset>. static size_t OffsetToIndex(size_t offset) { - return offset / kAlignment / kBitsPerWord; + return offset / kAlignment / kBitsPerWord; } static uintptr_t IndexToOffset(size_t index) { @@ -75,6 +80,7 @@ class SpaceBitmap { // Returns true if the object was previously marked. bool AtomicTestAndSet(const mirror::Object* obj); + // Fill the bitmap with zeroes. Returns the bitmap's memory to the system as a side-effect. void Clear(); bool Test(const mirror::Object* obj) const; @@ -160,6 +166,8 @@ class SpaceBitmap { std::string GetName() const; void SetName(const std::string& name); + std::string Dump() const; + const void* GetObjectWordAddress(const mirror::Object* obj) const { uintptr_t addr = reinterpret_cast<uintptr_t>(obj); const uintptr_t offset = addr - heap_begin_; @@ -236,7 +244,8 @@ class SpaceSetMap { } } - SpaceSetMap(const std::string& name); + SpaceSetMap(const std::string& name) : name_(name) {} + ~SpaceSetMap() {} Objects& GetObjects() { return contained_; @@ -249,6 +258,8 @@ class SpaceSetMap { std::ostream& operator << (std::ostream& stream, const SpaceBitmap& bitmap); +} // namespace accounting +} // namespace gc } // namespace art -#endif // ART_SRC_GC_SPACE_BITMAP_H_ +#endif // ART_SRC_GC_ACCOUNTING_SPACE_BITMAP_H_ diff --git a/src/gc/space_bitmap_test.cc b/src/gc/accounting/space_bitmap_test.cc index 4645659470..d00d7c2739 100644 --- a/src/gc/space_bitmap_test.cc +++ b/src/gc/accounting/space_bitmap_test.cc @@ -17,7 +17,6 @@ #include "space_bitmap.h" #include "common_test.h" -#include "dlmalloc.h" #include "globals.h" #include "space_bitmap-inl.h" #include "UniquePtr.h" @@ -25,6 +24,8 @@ #include <stdint.h> namespace art { +namespace gc { +namespace accounting { class SpaceBitmapTest : public CommonTest { public: @@ -87,4 +88,6 @@ TEST_F(SpaceBitmapTest, ScanRange) { } } +} // namespace accounting +} // namespace gc } // namespace art diff --git a/src/dlmalloc.cc b/src/gc/allocator/dlmalloc.cc index 1d62d2011e..7584b6e6b9 100644 --- a/src/dlmalloc.cc +++ b/src/gc/allocator/dlmalloc.cc @@ -45,3 +45,28 @@ static void art_heap_corruption(const char* function) { static void art_heap_usage_error(const char* function, void* p) { LOG(FATAL) << "Incorrect use of function '" << function << "' argument " << p << " not expected"; } + +#include "globals.h" +#include "utils.h" +#include <sys/mman.h> + +using namespace art; +extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) { + // Is this chunk in use? + if (used_bytes != 0) { + return; + } + // Do we have any whole pages to give back? + start = reinterpret_cast<void*>(RoundUp(reinterpret_cast<uintptr_t>(start), kPageSize)); + end = reinterpret_cast<void*>(RoundDown(reinterpret_cast<uintptr_t>(end), kPageSize)); + if (end > start) { + size_t length = reinterpret_cast<byte*>(end) - reinterpret_cast<byte*>(start); + int rc = madvise(start, length, MADV_DONTNEED); + if (UNLIKELY(rc != 0)) { + errno = rc; + PLOG(FATAL) << "madvise failed during heap trimming"; + } + size_t* reclaimed = reinterpret_cast<size_t*>(arg); + *reclaimed += length; + } +} diff --git a/src/dlmalloc.h b/src/gc/allocator/dlmalloc.h index b6759a098c..6b02a44ffe 100644 --- a/src/dlmalloc.h +++ b/src/gc/allocator/dlmalloc.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_SRC_DLMALLOC_H_ -#define ART_SRC_DLMALLOC_H_ +#ifndef ART_SRC_GC_ALLOCATOR_DLMALLOC_H_ +#define ART_SRC_GC_ALLOCATOR_DLMALLOC_H_ // Configure dlmalloc for mspaces. #define HAVE_MMAP 0 @@ -33,4 +33,8 @@ extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), void* arg); extern "C" int dlmalloc_trim(size_t); -#endif // ART_SRC_DLMALLOC_H_ +// Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused +// pages back to the kernel. +extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/); + +#endif // ART_SRC_GC_ALLOCATOR_DLMALLOC_H_ diff --git a/src/gc/collector/garbage_collector.cc b/src/gc/collector/garbage_collector.cc new file mode 100644 index 0000000000..378a971250 --- /dev/null +++ b/src/gc/collector/garbage_collector.cc @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012 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. + */ + +#define ATRACE_TAG ATRACE_TAG_DALVIK + +#include <stdio.h> +#include <cutils/trace.h> + +#include "garbage_collector.h" + +#include "base/logging.h" +#include "base/mutex-inl.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" +#include "thread.h" +#include "thread_list.h" + +namespace art { +namespace gc { +namespace collector { + +GarbageCollector::GarbageCollector(Heap* heap, const std::string& name) + : heap_(heap), + name_(name), + verbose_(VLOG_IS_ON(heap)), + duration_ns_(0), + timings_(name_.c_str(), true, verbose_), + cumulative_timings_(name) { + ResetCumulativeStatistics(); +} + +bool GarbageCollector::HandleDirtyObjectsPhase() { + DCHECK(IsConcurrent()); + return true; +} + +void GarbageCollector::RegisterPause(uint64_t nano_length) { + pause_times_.push_back(nano_length); +} + +void GarbageCollector::ResetCumulativeStatistics() { + cumulative_timings_.Reset(); + total_time_ns_ = 0; + total_paused_time_ns_ = 0; + total_freed_objects_ = 0; + total_freed_bytes_ = 0; +} + +void GarbageCollector::Run() { + Thread* self = Thread::Current(); + ThreadList* thread_list = Runtime::Current()->GetThreadList(); + + uint64_t start_time = NanoTime(); + pause_times_.clear(); + duration_ns_ = 0; + + InitializePhase(); + + if (!IsConcurrent()) { + // Pause is the entire length of the GC. + uint64_t pause_start = NanoTime(); + ATRACE_BEGIN("Application threads suspended"); + thread_list->SuspendAll(); + MarkingPhase(); + ReclaimPhase(); + thread_list->ResumeAll(); + ATRACE_END(); + uint64_t pause_end = NanoTime(); + pause_times_.push_back(pause_end - pause_start); + } else { + { + ReaderMutexLock mu(self, *Locks::mutator_lock_); + MarkingPhase(); + } + bool done = false; + while (!done) { + uint64_t pause_start = NanoTime(); + ATRACE_BEGIN("Application threads suspended"); + thread_list->SuspendAll(); + done = HandleDirtyObjectsPhase(); + thread_list->ResumeAll(); + ATRACE_END(); + uint64_t pause_end = NanoTime(); + pause_times_.push_back(pause_end - pause_start); + } + { + ReaderMutexLock mu(self, *Locks::mutator_lock_); + ReclaimPhase(); + } + } + + uint64_t end_time = NanoTime(); + duration_ns_ = end_time - start_time; + + FinishPhase(); +} + +void GarbageCollector::SwapBitmaps() { + // Swap the live and mark bitmaps for each alloc space. This is needed since sweep re-swaps + // these bitmaps. The bitmap swapping is an optimization so that we do not need to clear the live + // bits of dead objects in the live bitmap. + const GcType gc_type = GetGcType(); + const std::vector<space::ContinuousSpace*>& cont_spaces = GetHeap()->GetContinuousSpaces(); + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = cont_spaces.begin(), end = cont_spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + // We never allocate into zygote spaces. + if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect || + (gc_type == kGcTypeFull && + space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect)) { + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); + if (live_bitmap != mark_bitmap) { + heap_->GetLiveBitmap()->ReplaceBitmap(live_bitmap, mark_bitmap); + heap_->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); + space->AsDlMallocSpace()->SwapBitmaps(); + } + } + } + const std::vector<space::DiscontinuousSpace*>& disc_spaces = GetHeap()->GetDiscontinuousSpaces(); + // TODO: C++0x + typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2; + for (It2 it = disc_spaces.begin(), end = disc_spaces.end(); it != end; ++it) { + space::LargeObjectSpace* space = down_cast<space::LargeObjectSpace*>(*it); + accounting::SpaceSetMap* live_set = space->GetLiveObjects(); + accounting::SpaceSetMap* mark_set = space->GetMarkObjects(); + heap_->GetLiveBitmap()->ReplaceObjectSet(live_set, mark_set); + heap_->GetMarkBitmap()->ReplaceObjectSet(mark_set, live_set); + space->SwapBitmaps(); + } +} + +} // namespace collector +} // namespace gc +} // namespace art diff --git a/src/gc/garbage_collector.h b/src/gc/collector/garbage_collector.h index a1014c2c7f..1ab395775b 100644 --- a/src/gc/garbage_collector.h +++ b/src/gc/collector/garbage_collector.h @@ -17,28 +17,38 @@ #ifndef ART_SRC_GC_GARBAGE_COLLECTOR_H_ #define ART_SRC_GC_GARBAGE_COLLECTOR_H_ +#include "gc_type.h" #include "locks.h" +#include "base/timing_logger.h" #include <stdint.h> #include <vector> namespace art { +namespace gc { class Heap; +namespace collector { + class GarbageCollector { public: // Returns true iff the garbage collector is concurrent. virtual bool IsConcurrent() const = 0; - GarbageCollector(Heap* heap); + GarbageCollector(Heap* heap, const std::string& name); + virtual ~GarbageCollector() { } + + const char* GetName() const { + return name_.c_str(); + } - virtual ~GarbageCollector(); + virtual GcType GetGcType() const = 0; // Run the garbage collector. void Run(); - Heap* GetHeap() { + Heap* GetHeap() const { return heap_; } @@ -48,16 +58,28 @@ class GarbageCollector { } // Returns how long the GC took to complete in nanoseconds. - uint64_t GetDuration() const { - return duration_; + uint64_t GetDurationNs() const { + return duration_ns_; } + void RegisterPause(uint64_t nano_length); + + base::NewTimingLogger& GetTimings() { + return timings_; + } - virtual std::string GetName() const = 0; + CumulativeLogger& GetCumulativeTimings() { + return cumulative_timings_; + } - void RegisterPause(uint64_t nano_length); + void ResetCumulativeStatistics(); + + // Swap the live and mark bitmaps of spaces that are active for the collector. For partial GC, + // this is the allocation space, for full GC then we swap the zygote bitmaps too. + void SwapBitmaps() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); protected: + // The initial phase. Done without mutators paused. virtual void InitializePhase() = 0; @@ -73,11 +95,28 @@ class GarbageCollector { // Called after the GC is finished. Done without mutators paused. virtual void FinishPhase() = 0; - Heap* heap_; + Heap* const heap_; + + std::string name_; + + const bool verbose_; + + uint64_t duration_ns_; + base::NewTimingLogger timings_; + + // Cumulative statistics. + uint64_t total_time_ns_; + uint64_t total_paused_time_ns_; + uint64_t total_freed_objects_; + uint64_t total_freed_bytes_; + + CumulativeLogger cumulative_timings_; + std::vector<uint64_t> pause_times_; - uint64_t duration_; }; +} // namespace collector +} // namespace gc } // namespace art #endif // ART_SRC_GC_GARBAGE_COLLECTOR_H_ diff --git a/src/gc/collector/gc_type.cc b/src/gc/collector/gc_type.cc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/gc/collector/gc_type.cc diff --git a/src/gc/gc_type.h b/src/gc/collector/gc_type.h index 908f038ff2..bb25bb93f9 100644 --- a/src/gc/gc_type.h +++ b/src/gc/collector/gc_type.h @@ -14,26 +14,33 @@ * limitations under the License. */ -#ifndef ART_SRC_GC_GC_TYPE_H_ -#define ART_SRC_GC_GC_TYPE_H_ +#ifndef ART_SRC_GC_COLLECTOR_GC_TYPE_H_ +#define ART_SRC_GC_COLLECTOR_GC_TYPE_H_ + +#include <ostream> namespace art { +namespace gc { +namespace collector { -// The ordering of the enum matters, it is used to determine which GCs are run first. +// The type of collection to be performed. The ordering of the enum matters, it is used to +// determine which GCs are run first. enum GcType { - // No Gc + // Placeholder for when no GC has been performed. kGcTypeNone, - // Sticky mark bits "generational" GC. + // Sticky mark bits GC that attempts to only free objects allocated since the last GC. kGcTypeSticky, - // Partial GC, over only the alloc space. + // Partial GC that marks the application heap but not the Zygote. kGcTypePartial, - // Full GC + // Full GC that marks and frees in both the application and Zygote heap. kGcTypeFull, - // Number of different Gc types. + // Number of different GC types. kGcTypeMax, }; std::ostream& operator<<(std::ostream& os, const GcType& policy); +} // namespace collector +} // namespace gc } // namespace art -#endif // ART_SRC_GC_GC_TYPE_H_ +#endif // ART_SRC_GC_COLLECTOR_GC_TYPE_H_ diff --git a/src/gc/mark_sweep-inl.h b/src/gc/collector/mark_sweep-inl.h index 726502330b..ea9fced84a 100644 --- a/src/gc/mark_sweep-inl.h +++ b/src/gc/collector/mark_sweep-inl.h @@ -17,12 +17,16 @@ #ifndef ART_SRC_GC_MARK_SWEEP_INL_H_ #define ART_SRC_GC_MARK_SWEEP_INL_H_ -#include "heap.h" +#include "gc/collector/mark_sweep.h" + +#include "gc/heap.h" #include "mirror/class.h" #include "mirror/field.h" #include "mirror/object_array.h" namespace art { +namespace gc { +namespace collector { template <typename MarkVisitor> inline void MarkSweep::ScanObjectVisit(const mirror::Object* obj, const MarkVisitor& visitor) { @@ -154,6 +158,8 @@ inline void MarkSweep::VisitObjectArrayReferences(const mirror::ObjectArray<mirr } } +} // namespace collector +} // namespace gc } // namespace art #endif // ART_SRC_GC_MARK_SWEEP_INL_H_ diff --git a/src/gc/mark_sweep.cc b/src/gc/collector/mark_sweep.cc index 25b4b78356..d54fec6d9f 100644 --- a/src/gc/mark_sweep.cc +++ b/src/gc/collector/mark_sweep.cc @@ -25,13 +25,16 @@ #include "base/macros.h" #include "base/mutex-inl.h" #include "base/timing_logger.h" -#include "card_table.h" -#include "card_table-inl.h" -#include "heap.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/accounting/space_bitmap-inl.h" +#include "gc/heap.h" +#include "gc/space/image_space.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" #include "indirect_reference_table.h" #include "intern_table.h" #include "jni_internal.h" -#include "large_object_space.h" #include "monitor.h" #include "mark_sweep-inl.h" #include "mirror/class-inl.h" @@ -43,15 +46,15 @@ #include "mirror/object_array.h" #include "mirror/object_array-inl.h" #include "runtime.h" -#include "space.h" -#include "space_bitmap-inl.h" -#include "thread.h" +#include "thread-inl.h" #include "thread_list.h" #include "verifier/method_verifier.h" using namespace art::mirror; namespace art { +namespace gc { +namespace collector { // Performance options. static const bool kParallelMarkStack = true; @@ -68,7 +71,6 @@ static const bool kCountJavaLangRefs = false; class SetFingerVisitor { public: SetFingerVisitor(MarkSweep* const mark_sweep) : mark_sweep_(mark_sweep) { - } void operator ()(void* finger) const { @@ -79,13 +81,7 @@ class SetFingerVisitor { MarkSweep* const mark_sweep_; }; -std::string MarkSweep::GetName() const { - std::ostringstream ss; - ss << (IsConcurrent() ? "Concurrent" : "") << GetGcType(); - return ss.str(); -} - -void MarkSweep::ImmuneSpace(ContinuousSpace* space) { +void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) { // Bind live to mark bitmap if necessary. if (space->GetLiveBitmap() != space->GetMarkBitmap()) { BindLiveToMarkBitmap(space); @@ -97,54 +93,68 @@ void MarkSweep::ImmuneSpace(ContinuousSpace* space) { SetImmuneRange(reinterpret_cast<Object*>(space->Begin()), reinterpret_cast<Object*>(space->End())); } else { - const Spaces& spaces = GetHeap()->GetSpaces(); - const ContinuousSpace* prev_space = NULL; - // Find out if the previous space is immune. - // TODO: C++0x - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - if (*it == space) { - break; - } - prev_space = *it; + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + const space::ContinuousSpace* prev_space = NULL; + // Find out if the previous space is immune. + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + if (*it == space) { + break; } + prev_space = *it; + } - // If previous space was immune, then extend the immune region. - if (prev_space != NULL && - immune_begin_ <= reinterpret_cast<Object*>(prev_space->Begin()) && - immune_end_ >= reinterpret_cast<Object*>(prev_space->End())) { + // If previous space was immune, then extend the immune region. Relies on continuous spaces + // being sorted by Heap::AddContinuousSpace. + if (prev_space != NULL && + immune_begin_ <= reinterpret_cast<Object*>(prev_space->Begin()) && + immune_end_ >= reinterpret_cast<Object*>(prev_space->End())) { immune_begin_ = std::min(reinterpret_cast<Object*>(space->Begin()), immune_begin_); immune_end_ = std::max(reinterpret_cast<Object*>(space->End()), immune_end_); } } } -// Bind the live bits to the mark bits of bitmaps based on the gc type. void MarkSweep::BindBitmaps() { - Spaces& spaces = GetHeap()->GetSpaces(); + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); // Mark all of the spaces we never collect as immune. - for (Spaces::iterator it = spaces.begin(); it != spaces.end(); ++it) { - ContinuousSpace* space = *it; - if (space->GetGcRetentionPolicy() == kGcRetentionPolicyNeverCollect) { + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect) { ImmuneSpace(space); } } } -MarkSweep::MarkSweep(Heap* heap, bool is_concurrent) - : GarbageCollector(heap), +MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) + : GarbageCollector(heap, + name_prefix + (name_prefix.empty() ? "" : " ") + + (is_concurrent ? "concurrent mark sweep": "mark sweep")), + current_mark_bitmap_(NULL), + java_lang_Class_(NULL), + mark_stack_(NULL), + finger_(NULL), + immune_begin_(NULL), + immune_end_(NULL), + soft_reference_list_(NULL), + weak_reference_list_(NULL), + finalizer_reference_list_(NULL), + phantom_reference_list_(NULL), + cleared_reference_list_(NULL), gc_barrier_(new Barrier(0)), large_object_lock_("mark sweep large object lock", kMarkSweepLargeObjectLock), mark_stack_expand_lock_("mark sweep mark stack expand lock"), is_concurrent_(is_concurrent), - timings_(GetName(), true), - cumulative_timings_(GetName()) { - cumulative_timings_.SetName(GetName()); - ResetCumulativeStatistics(); + clear_soft_references_(false) { } void MarkSweep::InitializePhase() { + timings_.Reset(); + timings_.StartSplit("InitializePhase"); mark_stack_ = GetHeap()->mark_stack_.get(); DCHECK(mark_stack_ != NULL); finger_ = NULL; @@ -169,34 +179,31 @@ void MarkSweep::InitializePhase() { java_lang_Class_ = Class::GetJavaLangClass(); CHECK(java_lang_Class_ != NULL); FindDefaultMarkBitmap(); - // Mark any concurrent roots as dirty since we need to scan them at least once during this GC. - Runtime::Current()->DirtyRoots(); - timings_.Reset(); // Do any pre GC verification. heap_->PreGcVerification(this); } void MarkSweep::ProcessReferences(Thread* self) { + timings_.NewSplit("ProcessReferences"); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); ProcessReferences(&soft_reference_list_, clear_soft_references_, &weak_reference_list_, &finalizer_reference_list_, &phantom_reference_list_); - timings_.AddSplit("ProcessReferences"); } bool MarkSweep::HandleDirtyObjectsPhase() { Thread* self = Thread::Current(); - ObjectStack* allocation_stack = GetHeap()->allocation_stack_.get(); + accounting::ObjectStack* allocation_stack = GetHeap()->allocation_stack_.get(); Locks::mutator_lock_->AssertExclusiveHeld(self); { + timings_.NewSplit("ReMarkRoots"); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); // Re-mark root set. ReMarkRoots(); - timings_.AddSplit("ReMarkRoots"); // Scan dirty objects, this is only required if we are not doing concurrent GC. - RecursiveMarkDirtyObjects(CardTable::kCardDirty); + RecursiveMarkDirtyObjects(accounting::CardTable::kCardDirty); } ProcessReferences(self); @@ -206,15 +213,17 @@ bool MarkSweep::HandleDirtyObjectsPhase() { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); // This second sweep makes sure that we don't have any objects in the live stack which point to // freed objects. These cause problems since their references may be previously freed objects. - SweepArray(timings_, allocation_stack, false); + SweepArray(allocation_stack, false); } else { + timings_.NewSplit("UnMarkAllocStack"); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - // We only sweep over the live stack, and the live stack should not intersect with the - // allocation stack, so it should be safe to UnMark anything in the allocation stack as live. + // The allocation stack contains things allocated since the start of the GC. These may have been + // marked during this GC meaning they won't be eligible for reclaiming in the next sticky GC. + // Remove these objects from the mark bitmaps so that they will be eligible for sticky + // collection. heap_->UnMarkAllocStack(GetHeap()->alloc_space_->GetMarkBitmap(), - GetHeap()->large_object_space_->GetMarkObjects(), - allocation_stack); - timings_.AddSplit("UnMarkAllocStack"); + GetHeap()->large_object_space_->GetMarkObjects(), + allocation_stack); } return true; } @@ -227,31 +236,30 @@ void MarkSweep::MarkingPhase() { Heap* heap = GetHeap(); Thread* self = Thread::Current(); + timings_.NewSplit("BindBitmaps"); BindBitmaps(); FindDefaultMarkBitmap(); - timings_.AddSplit("BindBitmaps"); - // Process dirty cards and add dirty cards to mod union tables. heap->ProcessCards(timings_); // Need to do this before the checkpoint since we don't want any threads to add references to // the live stack during the recursive mark. + timings_.NewSplit("SwapStacks"); heap->SwapStacks(); - timings_.AddSplit("SwapStacks"); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); if (Locks::mutator_lock_->IsExclusiveHeld(self)) { // If we exclusively hold the mutator lock, all threads must be suspended. + timings_.NewSplit("MarkRoots"); MarkRoots(); - timings_.AddSplit("MarkConcurrentRoots"); } else { - MarkRootsCheckpoint(); - timings_.AddSplit("MarkRootsCheckpoint"); + timings_.NewSplit("MarkRootsCheckpoint"); + MarkRootsCheckpoint(self); + timings_.NewSplit("MarkNonThreadRoots"); MarkNonThreadRoots(); - timings_.AddSplit("MarkNonThreadRoots"); } + timings_.NewSplit("MarkConcurrentRoots"); MarkConcurrentRoots(); - timings_.AddSplit("MarkConcurrentRoots"); heap->UpdateAndMarkModUnion(this, timings_, GetGcType()); MarkReachableObjects(); @@ -260,12 +268,12 @@ void MarkSweep::MarkingPhase() { void MarkSweep::MarkReachableObjects() { // Mark everything allocated since the last as GC live so that we can sweep concurrently, // knowing that new allocations won't be marked as live. - ObjectStack* live_stack = heap_->GetLiveStack(); + timings_.NewSplit("MarkStackAsLive"); + accounting::ObjectStack* live_stack = heap_->GetLiveStack(); heap_->MarkAllocStack(heap_->alloc_space_->GetLiveBitmap(), heap_->large_object_space_->GetLiveObjects(), live_stack); live_stack->Reset(); - timings_.AddSplit("MarkStackAsLive"); // Recursively mark all the non-image bits set in the mark bitmap. RecursiveMark(); DisableFinger(); @@ -289,60 +297,31 @@ void MarkSweep::ReclaimPhase() { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); // Reclaim unmarked objects. - Sweep(timings_, false); + Sweep(false); // Swap the live and mark bitmaps for each space which we modified space. This is an // optimization that enables us to not clear live bits inside of the sweep. Only swaps unbound // bitmaps. + timings_.NewSplit("SwapBitmaps"); SwapBitmaps(); - timings_.AddSplit("SwapBitmaps"); // Unbind the live and mark bitmaps. UnBindBitmaps(); } } -void MarkSweep::SwapBitmaps() { - // Swap the live and mark bitmaps for each alloc space. This is needed since sweep re-swaps - // these bitmaps. The bitmap swapping is an optimization so that we do not need to clear the live - // bits of dead objects in the live bitmap. - const GcType gc_type = GetGcType(); - // TODO: C++0x - Spaces& spaces = heap_->GetSpaces(); - for (Spaces::iterator it = spaces.begin(); it != spaces.end(); ++it) { - ContinuousSpace* space = *it; - // We never allocate into zygote spaces. - if (space->GetGcRetentionPolicy() == kGcRetentionPolicyAlwaysCollect || - (gc_type == kGcTypeFull && - space->GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect)) { - SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); - if (live_bitmap != mark_bitmap) { - heap_->GetLiveBitmap()->ReplaceBitmap(live_bitmap, mark_bitmap); - heap_->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); - space->AsAllocSpace()->SwapBitmaps(); - } - } - } - SwapLargeObjects(); -} - -void MarkSweep::SwapLargeObjects() { - LargeObjectSpace* large_object_space = heap_->GetLargeObjectsSpace(); - large_object_space->SwapBitmaps(); - heap_->GetLiveBitmap()->SetLargeObjects(large_object_space->GetLiveObjects()); - heap_->GetMarkBitmap()->SetLargeObjects(large_object_space->GetMarkObjects()); -} - void MarkSweep::SetImmuneRange(Object* begin, Object* end) { immune_begin_ = begin; immune_end_ = end; } void MarkSweep::FindDefaultMarkBitmap() { - const Spaces& spaces = heap_->GetSpaces(); - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - if ((*it)->GetGcRetentionPolicy() == kGcRetentionPolicyAlwaysCollect) { + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { current_mark_bitmap_ = (*it)->GetMarkBitmap(); CHECK(current_mark_bitmap_ != NULL); return; @@ -389,10 +368,10 @@ inline void MarkSweep::MarkObjectNonNull(const Object* obj, bool check_finger) { // Try to take advantage of locality of references within a space, failing this find the space // the hard way. - SpaceBitmap* object_bitmap = current_mark_bitmap_; + accounting::SpaceBitmap* object_bitmap = current_mark_bitmap_; if (UNLIKELY(!object_bitmap->HasAddress(obj))) { - SpaceBitmap* new_bitmap = heap_->GetMarkBitmap()->GetSpaceBitmap(obj); - if (new_bitmap != NULL) { + accounting::SpaceBitmap* new_bitmap = heap_->GetMarkBitmap()->GetContinuousSpaceBitmap(obj); + if (LIKELY(new_bitmap != NULL)) { object_bitmap = new_bitmap; } else { MarkLargeObject(obj); @@ -416,13 +395,16 @@ inline void MarkSweep::MarkObjectNonNull(const Object* obj, bool check_finger) { // Rare case, probably not worth inlining since it will increase instruction cache miss rate. bool MarkSweep::MarkLargeObject(const Object* obj) { - LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); - SpaceSetMap* large_objects = large_object_space->GetMarkObjects(); + // TODO: support >1 discontinuous space. + space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); + accounting::SpaceSetMap* large_objects = large_object_space->GetMarkObjects(); if (kProfileLargeObjects) { ++large_object_test_; } if (UNLIKELY(!large_objects->Test(obj))) { - if (!large_object_space->Contains(obj)) { + // TODO: mark may be called holding the JNI global references lock, Contains will hold the + // large object space lock causing a lock level violation. Bug: 9414652; + if (!kDebugLocking && !large_object_space->Contains(obj)) { LOG(ERROR) << "Tried to mark " << obj << " not contained by any spaces"; LOG(ERROR) << "Attempting see if it's a bad root"; VerifyRoots(); @@ -448,9 +430,9 @@ inline bool MarkSweep::MarkObjectParallel(const Object* obj) { // Try to take advantage of locality of references within a space, failing this find the space // the hard way. - SpaceBitmap* object_bitmap = current_mark_bitmap_; + accounting::SpaceBitmap* object_bitmap = current_mark_bitmap_; if (UNLIKELY(!object_bitmap->HasAddress(obj))) { - SpaceBitmap* new_bitmap = heap_->GetMarkBitmap()->GetSpaceBitmap(obj); + accounting::SpaceBitmap* new_bitmap = heap_->GetMarkBitmap()->GetContinuousSpaceBitmap(obj); if (new_bitmap != NULL) { object_bitmap = new_bitmap; } else { @@ -510,8 +492,8 @@ void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg, void MarkSweep::VerifyRoot(const Object* root, size_t vreg, const StackVisitor* visitor) { // See if the root is on any space bitmap. - if (GetHeap()->GetLiveBitmap()->GetSpaceBitmap(root) == NULL) { - LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); + if (GetHeap()->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == NULL) { + space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); if (!large_object_space->Contains(root)) { LOG(ERROR) << "Found invalid root: " << root; if (visitor != NULL) { @@ -535,7 +517,8 @@ void MarkSweep::MarkNonThreadRoots() { } void MarkSweep::MarkConcurrentRoots() { - Runtime::Current()->VisitConcurrentRoots(MarkObjectCallback, this); + // Visit all runtime roots and clear dirty flags. + Runtime::Current()->VisitConcurrentRoots(MarkObjectCallback, this, false, true); } class CheckObjectVisitor { @@ -571,11 +554,11 @@ void MarkSweep::VerifyImageRootVisitor(Object* root, void* arg) { mark_sweep->CheckObject(root); } -void MarkSweep::BindLiveToMarkBitmap(ContinuousSpace* space) { - CHECK(space->IsAllocSpace()); - DlMallocSpace* alloc_space = space->AsAllocSpace(); - SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - SpaceBitmap* mark_bitmap = alloc_space->mark_bitmap_.release(); +void MarkSweep::BindLiveToMarkBitmap(space::ContinuousSpace* space) { + CHECK(space->IsDlMallocSpace()); + space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = alloc_space->mark_bitmap_.release(); GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); alloc_space->temp_bitmap_.reset(mark_bitmap); alloc_space->mark_bitmap_.reset(live_bitmap); @@ -584,7 +567,6 @@ void MarkSweep::BindLiveToMarkBitmap(ContinuousSpace* space) { class ScanObjectVisitor { public: ScanObjectVisitor(MarkSweep* const mark_sweep) : mark_sweep_(mark_sweep) { - } // TODO: Fixme when anotatalysis works with visitors. @@ -601,29 +583,39 @@ class ScanObjectVisitor { }; void MarkSweep::ScanGrayObjects(byte minimum_age) { - const Spaces& spaces = heap_->GetSpaces(); - CardTable* card_table = heap_->GetCardTable(); + accounting::CardTable* card_table = GetHeap()->GetCardTable(); + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); ScanObjectVisitor visitor(this); SetFingerVisitor finger_visitor(this); - // TODO: C++ 0x auto - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - ContinuousSpace* space = *it; + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), space_end = spaces.end(); it != space_end; ++it) { + space::ContinuousSpace* space = *it; + switch (space->GetGcRetentionPolicy()) { + case space::kGcRetentionPolicyNeverCollect: + timings_.NewSplit("ScanGrayImageSpaceObjects"); + break; + case space::kGcRetentionPolicyFullCollect: + timings_.NewSplit("ScanGrayZygoteSpaceObjects"); + break; + case space::kGcRetentionPolicyAlwaysCollect: + timings_.NewSplit("ScanGrayAllocSpaceObjects"); + break; + } byte* begin = space->Begin(); byte* end = space->End(); // Image spaces are handled properly since live == marked for them. - SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); - card_table->Scan(mark_bitmap, begin, end, visitor, VoidFunctor(), minimum_age); + accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); + card_table->Scan(mark_bitmap, begin, end, visitor, finger_visitor, minimum_age); } } class CheckBitmapVisitor { public: CheckBitmapVisitor(MarkSweep* mark_sweep) : mark_sweep_(mark_sweep) { - } - void operator ()(const Object* obj) const - NO_THREAD_SAFETY_ANALYSIS { + void operator ()(const Object* obj) const NO_THREAD_SAFETY_ANALYSIS { if (kDebugLocking) { Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current()); } @@ -640,13 +632,15 @@ void MarkSweep::VerifyImageRoots() { // objects which are either in the image space or marked objects in the alloc // space CheckBitmapVisitor visitor(this); - const Spaces& spaces = heap_->GetSpaces(); - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { if ((*it)->IsImageSpace()) { - ImageSpace* space = (*it)->AsImageSpace(); + space::ImageSpace* space = (*it)->AsImageSpace(); uintptr_t begin = reinterpret_cast<uintptr_t>(space->Begin()); uintptr_t end = reinterpret_cast<uintptr_t>(space->End()); - SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); DCHECK(live_bitmap != NULL); live_bitmap->VisitMarkedRange(begin, end, visitor, VoidFunctor()); } @@ -656,6 +650,7 @@ void MarkSweep::VerifyImageRoots() { // Populates the mark stack based on the set of marked objects and // recursively marks until the mark stack is emptied. void MarkSweep::RecursiveMark() { + timings_.NewSplit("RecursiveMark"); // RecursiveMark will build the lists of known instances of the Reference classes. // See DelayReferenceReferent for details. CHECK(soft_reference_list_ == NULL); @@ -665,16 +660,17 @@ void MarkSweep::RecursiveMark() { CHECK(cleared_reference_list_ == NULL); const bool partial = GetGcType() == kGcTypePartial; - const Spaces& spaces = heap_->GetSpaces(); SetFingerVisitor set_finger_visitor(this); ScanObjectVisitor scan_visitor(this); if (!kDisableFinger) { finger_ = NULL; - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - ContinuousSpace* space = *it; - if ((space->GetGcRetentionPolicy() == kGcRetentionPolicyAlwaysCollect) || - (!partial && space->GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) - ) { + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if ((space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) || + (!partial && space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect)) { current_mark_bitmap_ = space->GetMarkBitmap(); if (current_mark_bitmap_ == NULL) { GetHeap()->DumpSpaces(); @@ -688,9 +684,8 @@ void MarkSweep::RecursiveMark() { } } DisableFinger(); - timings_.AddSplit("RecursiveMark"); + timings_.NewSplit("ProcessMarkStack"); ProcessMarkStack(); - timings_.AddSplit("ProcessMarkStack"); } bool MarkSweep::IsMarkedCallback(const Object* object, void* arg) { @@ -701,13 +696,12 @@ bool MarkSweep::IsMarkedCallback(const Object* object, void* arg) { void MarkSweep::RecursiveMarkDirtyObjects(byte minimum_age) { ScanGrayObjects(minimum_age); - timings_.AddSplit("ScanGrayObjects"); + timings_.NewSplit("ProcessMarkStack"); ProcessMarkStack(); - timings_.AddSplit("ProcessMarkStack"); } void MarkSweep::ReMarkRoots() { - Runtime::Current()->VisitRoots(ReMarkObjectVisitor, this); + Runtime::Current()->VisitRoots(ReMarkObjectVisitor, this, true, true); } void MarkSweep::SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg) { @@ -724,7 +718,7 @@ void MarkSweep::SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg) { } struct ArrayMarkedCheck { - ObjectStack* live_stack; + accounting::ObjectStack* live_stack; MarkSweep* mark_sweep; }; @@ -734,11 +728,11 @@ bool MarkSweep::IsMarkedArrayCallback(const Object* object, void* arg) { if (array_check->mark_sweep->IsMarked(object)) { return true; } - ObjectStack* live_stack = array_check->live_stack; + accounting::ObjectStack* live_stack = array_check->live_stack; return std::find(live_stack->Begin(), live_stack->End(), object) == live_stack->End(); } -void MarkSweep::SweepSystemWeaksArray(ObjectStack* allocations) { +void MarkSweep::SweepSystemWeaksArray(accounting::ObjectStack* allocations) { Runtime* runtime = Runtime::Current(); // The callbacks check // !is_marked where is_marked is the callback but we want @@ -775,7 +769,7 @@ bool MarkSweep::VerifyIsLiveCallback(const Object* obj, void* arg) { void MarkSweep::VerifyIsLive(const Object* obj) { Heap* heap = GetHeap(); if (!heap->GetLiveBitmap()->Test(obj)) { - LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); + space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); if (!large_object_space->GetLiveObjects()->Test(obj)) { if (std::find(heap->allocation_stack_->Begin(), heap->allocation_stack_->End(), obj) == heap->allocation_stack_->End()) { @@ -805,7 +799,7 @@ void MarkSweep::VerifySystemWeaks() { struct SweepCallbackContext { MarkSweep* mark_sweep; - AllocSpace* space; + space::AllocSpace* space; Thread* self; }; @@ -828,28 +822,29 @@ class CheckpointMarkThreadRoots : public Closure { MarkSweep* mark_sweep_; }; -void MarkSweep::ResetCumulativeStatistics() { - cumulative_timings_.Reset(); - total_time_ = 0; - total_paused_time_ = 0; - total_freed_objects_ = 0; - total_freed_bytes_ = 0; -} - -void MarkSweep::MarkRootsCheckpoint() { +void MarkSweep::MarkRootsCheckpoint(Thread* self) { CheckpointMarkThreadRoots check_point(this); ThreadList* thread_list = Runtime::Current()->GetThreadList(); - // Increment the count of the barrier. If all of the checkpoints have already been finished then - // will hit 0 and continue. Otherwise we are still waiting for some checkpoints, so the counter - // will go positive and we will unblock when it hits zero. - gc_barrier_->Increment(Thread::Current(), thread_list->RunCheckpoint(&check_point)); + // Request the check point is run on all threads returning a count of the threads that must + // run through the barrier including self. + size_t barrier_count = thread_list->RunCheckpoint(&check_point); + // Release locks then wait for all mutator threads to pass the barrier. + // TODO: optimize to not release locks when there are no threads to wait for. + Locks::heap_bitmap_lock_->ExclusiveUnlock(self); + Locks::mutator_lock_->SharedUnlock(self); + ThreadState old_state = self->SetState(kWaitingForCheckPointsToRun); + CHECK_EQ(old_state, kWaitingPerformingGc); + gc_barrier_->Increment(self, barrier_count); + self->SetState(kWaitingPerformingGc); + Locks::mutator_lock_->SharedLock(self); + Locks::heap_bitmap_lock_->ExclusiveLock(self); } void MarkSweep::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { SweepCallbackContext* context = static_cast<SweepCallbackContext*>(arg); MarkSweep* mark_sweep = context->mark_sweep; Heap* heap = mark_sweep->GetHeap(); - AllocSpace* space = context->space; + space::AllocSpace* space = context->space; Thread* self = context->self; Locks::heap_bitmap_lock_->AssertExclusiveHeld(self); // Use a bulk free, that merges consecutive objects before freeing or free per object? @@ -875,22 +870,23 @@ void MarkSweep::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { } } -void MarkSweep::SweepArray(TimingLogger& logger, ObjectStack* allocations, bool swap_bitmaps) { +void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps) { size_t freed_bytes = 0; - DlMallocSpace* space = heap_->GetAllocSpace(); + space::DlMallocSpace* space = heap_->GetAllocSpace(); // If we don't swap bitmaps then newly allocated Weaks go into the live bitmap but not mark // bitmap, resulting in occasional frees of Weaks which are still in use. + timings_.NewSplit("SweepSystemWeaks"); SweepSystemWeaksArray(allocations); - logger.AddSplit("SweepSystemWeaks"); + timings_.NewSplit("Process allocation stack"); // Newly allocated objects MUST be in the alloc space and those are the only objects which we are // going to free. - SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); - LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); - SpaceSetMap* large_live_objects = large_object_space->GetLiveObjects(); - SpaceSetMap* large_mark_objects = large_object_space->GetMarkObjects(); + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); + space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); + accounting::SpaceSetMap* large_live_objects = large_object_space->GetLiveObjects(); + accounting::SpaceSetMap* large_mark_objects = large_object_space->GetMarkObjects(); if (swap_bitmaps) { std::swap(live_bitmap, mark_bitmap); std::swap(large_live_objects, large_mark_objects); @@ -916,7 +912,8 @@ void MarkSweep::SweepArray(TimingLogger& logger, ObjectStack* allocations, bool freed_bytes += large_object_space->Free(self, obj); } } - logger.AddSplit("Process allocation stack"); + CHECK_EQ(count, allocations->Size()); + timings_.NewSplit("FreeList"); size_t freed_objects = out - objects; freed_bytes += space->FreeList(self, freed_objects, objects); @@ -925,71 +922,78 @@ void MarkSweep::SweepArray(TimingLogger& logger, ObjectStack* allocations, bool heap_->RecordFree(freed_objects + freed_large_objects, freed_bytes); freed_objects_ += freed_objects; freed_bytes_ += freed_bytes; - logger.AddSplit("FreeList"); + + timings_.NewSplit("ResetStack"); allocations->Reset(); - logger.AddSplit("ResetStack"); } -void MarkSweep::Sweep(TimingLogger& timings, bool swap_bitmaps) { +void MarkSweep::Sweep(bool swap_bitmaps) { DCHECK(mark_stack_->IsEmpty()); // If we don't swap bitmaps then newly allocated Weaks go into the live bitmap but not mark // bitmap, resulting in occasional frees of Weaks which are still in use. + timings_.NewSplit("SweepSystemWeaks"); SweepSystemWeaks(); - timings.AddSplit("SweepSystemWeaks"); - const bool partial = GetGcType() == kGcTypePartial; - const Spaces& spaces = heap_->GetSpaces(); + const bool partial = (GetGcType() == kGcTypePartial); SweepCallbackContext scc; scc.mark_sweep = this; scc.self = Thread::Current(); - // TODO: C++0x auto - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - ContinuousSpace* space = *it; - if ( - space->GetGcRetentionPolicy() == kGcRetentionPolicyAlwaysCollect || - (!partial && space->GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) - ) { + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + // We always sweep always collect spaces. + bool sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect); + if (!partial && !sweep_space) { + // We sweep full collect spaces when the GC isn't a partial GC (ie its full). + sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect); + } + if (sweep_space) { uintptr_t begin = reinterpret_cast<uintptr_t>(space->Begin()); uintptr_t end = reinterpret_cast<uintptr_t>(space->End()); - scc.space = space->AsAllocSpace(); - SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); + scc.space = space->AsDlMallocSpace(); + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); if (swap_bitmaps) { std::swap(live_bitmap, mark_bitmap); } - if (space->GetGcRetentionPolicy() == kGcRetentionPolicyAlwaysCollect) { + if (!space->IsZygoteSpace()) { + timings_.NewSplit("SweepAllocSpace"); // Bitmaps are pre-swapped for optimization which enables sweeping with the heap unlocked. - SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, - &SweepCallback, reinterpret_cast<void*>(&scc)); + accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, + &SweepCallback, reinterpret_cast<void*>(&scc)); } else { - // Zygote sweep takes care of dirtying cards and clearing live bits, does not free actual memory. - SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, - &ZygoteSweepCallback, reinterpret_cast<void*>(&scc)); + timings_.NewSplit("SweepZygote"); + // Zygote sweep takes care of dirtying cards and clearing live bits, does not free actual + // memory. + accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, + &ZygoteSweepCallback, reinterpret_cast<void*>(&scc)); } } } - timings.AddSplit("Sweep"); + timings_.NewSplit("SweepLargeObjects"); SweepLargeObjects(swap_bitmaps); - timings.AddSplit("SweepLargeObjects"); } void MarkSweep::SweepLargeObjects(bool swap_bitmaps) { // Sweep large objects - LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); - SpaceSetMap* large_live_objects = large_object_space->GetLiveObjects(); - SpaceSetMap* large_mark_objects = large_object_space->GetMarkObjects(); + space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); + accounting::SpaceSetMap* large_live_objects = large_object_space->GetLiveObjects(); + accounting::SpaceSetMap* large_mark_objects = large_object_space->GetMarkObjects(); if (swap_bitmaps) { std::swap(large_live_objects, large_mark_objects); } - SpaceSetMap::Objects& live_objects = large_live_objects->GetObjects(); + accounting::SpaceSetMap::Objects& live_objects = large_live_objects->GetObjects(); // O(n*log(n)) but hopefully there are not too many large objects. size_t freed_objects = 0; size_t freed_bytes = 0; - // TODO: C++0x Thread* self = Thread::Current(); - for (SpaceSetMap::Objects::iterator it = live_objects.begin(); it != live_objects.end(); ++it) { + // TODO: C++0x + typedef accounting::SpaceSetMap::Objects::iterator It; + for (It it = live_objects.begin(), end = live_objects.end(); it != end; ++it) { if (!large_mark_objects->Test(*it)) { freed_bytes += large_object_space->Free(self, const_cast<Object*>(*it)); ++freed_objects; @@ -997,20 +1001,21 @@ void MarkSweep::SweepLargeObjects(bool swap_bitmaps) { } freed_objects_ += freed_objects; freed_bytes_ += freed_bytes; - // Large objects don't count towards bytes_allocated. GetHeap()->RecordFree(freed_objects, freed_bytes); } void MarkSweep::CheckReference(const Object* obj, const Object* ref, MemberOffset offset, bool is_static) { - const Spaces& spaces = heap_->GetSpaces(); - // TODO: C++0x auto - for (Spaces::const_iterator cur = spaces.begin(); cur != spaces.end(); ++cur) { - if ((*cur)->IsAllocSpace() && (*cur)->Contains(ref)) { + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace() && space->Contains(ref)) { DCHECK(IsMarked(obj)); bool is_marked = IsMarked(ref); if (!is_marked) { - LOG(INFO) << **cur; + LOG(INFO) << *space; LOG(WARNING) << (is_static ? "Static ref'" : "Instance ref'") << PrettyTypeOf(ref) << "' (" << reinterpret_cast<const void*>(ref) << ") in '" << PrettyTypeOf(obj) << "' (" << reinterpret_cast<const void*>(obj) << ") at offset " @@ -1107,7 +1112,7 @@ void MarkSweep::ScanObject(const Object* obj) { } class MarkStackChunk : public Task { -public: + public: MarkStackChunk(ThreadPool* thread_pool, MarkSweep* mark_sweep, Object** begin, Object** end) : mark_sweep_(mark_sweep), thread_pool_(thread_pool), @@ -1169,6 +1174,7 @@ public: // Don't need to use atomic ++ since we only one thread is writing to an output block at any // given time. void Push(Object* obj) { + CHECK(obj != NULL); data_[length_++] = obj; } @@ -1176,7 +1182,7 @@ public: if (static_cast<size_t>(length_) < max_size) { Push(const_cast<Object*>(obj)); } else { - // Internal buffer is full, push to a new buffer instead. + // Internal (thread-local) buffer is full, push to a new buffer instead. if (UNLIKELY(output_ == NULL)) { AllocateOutputChunk(); } else if (UNLIKELY(static_cast<size_t>(output_->length_) == max_size)) { @@ -1255,8 +1261,8 @@ void MarkSweep::ProcessMarkStackParallel() { thread_pool->AddTask(self, new MarkStackChunk(thread_pool, this, begin, end)); } thread_pool->StartWorkers(self); + thread_pool->Wait(self, true, true); mark_stack_->Reset(); - thread_pool->Wait(self, true); //LOG(INFO) << "Idle wait time " << PrettyDuration(thread_pool->GetWaitTime()); CHECK_EQ(work_chunks_created_, work_chunks_deleted_) << " some of the work chunks were leaked"; } @@ -1443,15 +1449,16 @@ void MarkSweep::ProcessReferences(Object** soft_references, bool clear_soft, } void MarkSweep::UnBindBitmaps() { - const Spaces& spaces = heap_->GetSpaces(); - // TODO: C++0x auto - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - Space* space = *it; - if (space->IsAllocSpace()) { - DlMallocSpace* alloc_space = space->AsAllocSpace(); + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); if (alloc_space->temp_bitmap_.get() != NULL) { // At this point, the temp_bitmap holds our old mark bitmap. - SpaceBitmap* new_bitmap = alloc_space->temp_bitmap_.release(); + accounting::SpaceBitmap* new_bitmap = alloc_space->temp_bitmap_.release(); GetHeap()->GetMarkBitmap()->ReplaceBitmap(alloc_space->mark_bitmap_.get(), new_bitmap); CHECK_EQ(alloc_space->mark_bitmap_.release(), alloc_space->live_bitmap_.get()); alloc_space->mark_bitmap_.reset(new_bitmap); @@ -1464,20 +1471,21 @@ void MarkSweep::UnBindBitmaps() { void MarkSweep::FinishPhase() { // Can't enqueue referneces if we hold the mutator lock. Object* cleared_references = GetClearedReferences(); - heap_->EnqueueClearedReferences(&cleared_references); + Heap* heap = GetHeap(); + heap->EnqueueClearedReferences(&cleared_references); - heap_->PostGcVerification(this); + heap->PostGcVerification(this); - heap_->GrowForUtilization(GetDuration()); - timings_.AddSplit("GrowForUtilization"); + timings_.NewSplit("GrowForUtilization"); + heap->GrowForUtilization(GetDurationNs()); - heap_->RequestHeapTrim(); - timings_.AddSplit("RequestHeapTrim"); + timings_.NewSplit("RequestHeapTrim"); + heap->RequestHeapTrim(); // Update the cumulative statistics - total_time_ += GetDuration(); - total_paused_time_ += std::accumulate(GetPauseTimes().begin(), GetPauseTimes().end(), 0, - std::plus<uint64_t>()); + total_time_ns_ += GetDurationNs(); + total_paused_time_ns_ += std::accumulate(GetPauseTimes().begin(), GetPauseTimes().end(), 0, + std::plus<uint64_t>()); total_freed_objects_ += GetFreedObjects(); total_freed_bytes_ += GetFreedBytes(); @@ -1511,27 +1519,26 @@ void MarkSweep::FinishPhase() { // Update the cumulative loggers. cumulative_timings_.Start(); - cumulative_timings_.AddLogger(timings_); + cumulative_timings_.AddNewLogger(timings_); cumulative_timings_.End(); // Clear all of the spaces' mark bitmaps. - const Spaces& spaces = heap_->GetSpaces(); - // TODO: C++0x auto - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - ContinuousSpace* space = *it; - if (space->GetGcRetentionPolicy() != kGcRetentionPolicyNeverCollect) { + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->GetGcRetentionPolicy() != space::kGcRetentionPolicyNeverCollect) { space->GetMarkBitmap()->Clear(); } } mark_stack_->Reset(); // Reset the marked large objects. - LargeObjectSpace* large_objects = GetHeap()->GetLargeObjectsSpace(); + space::LargeObjectSpace* large_objects = GetHeap()->GetLargeObjectsSpace(); large_objects->GetMarkObjects()->Clear(); } -MarkSweep::~MarkSweep() { - -} - +} // namespace collector +} // namespace gc } // namespace art diff --git a/src/gc/mark_sweep.h b/src/gc/collector/mark_sweep.h index 11ce32f59a..9df3c19939 100644 --- a/src/gc/mark_sweep.h +++ b/src/gc/collector/mark_sweep.h @@ -21,40 +21,50 @@ #include "barrier.h" #include "base/macros.h" #include "base/mutex.h" -#include "base/timing_logger.h" #include "garbage_collector.h" -#include "gc_type.h" #include "offsets.h" #include "root_visitor.h" #include "UniquePtr.h" namespace art { + namespace mirror { -class Class; -class Object; -template<class T> class ObjectArray; -} -template <typename T> class AtomicStack; -class CheckObjectVisitor; -class ContinuousSpace; -class Heap; -class MarkIfReachesAllocspaceVisitor; -class ModUnionClearCardVisitor; -class ModUnionVisitor; -class ModUnionTableBitmap; -typedef AtomicStack<mirror::Object*> ObjectStack; -class SpaceBitmap; + class Class; + class Object; + template<class T> class ObjectArray; +} // namespace mirror + class StackVisitor; class Thread; -class MarkStackChunk; + +namespace gc { + +namespace accounting { + template <typename T> class AtomicStack; + class MarkIfReachesAllocspaceVisitor; + class ModUnionClearCardVisitor; + class ModUnionVisitor; + class ModUnionTableBitmap; + class MarkStackChunk; + typedef AtomicStack<mirror::Object*> ObjectStack; + class SpaceBitmap; +} // namespace accounting + +namespace space { + class ContinuousSpace; +} // namespace space + +class CheckObjectVisitor; +class Heap; + +namespace collector { class MarkSweep : public GarbageCollector { public: - explicit MarkSweep(Heap* heap, bool is_concurrent); + explicit MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix = ""); - ~MarkSweep(); + ~MarkSweep() {} - virtual std::string GetName() const; virtual void InitializePhase(); virtual bool IsConcurrent() const; virtual bool HandleDirtyObjectsPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -85,8 +95,9 @@ class MarkSweep : public GarbageCollector { void MarkConcurrentRoots(); EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - void MarkRootsCheckpoint(); - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + void MarkRootsCheckpoint(Thread* self) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Verify that image roots point to only marked objects within the alloc space. void VerifyImageRoots() @@ -98,16 +109,17 @@ class MarkSweep : public GarbageCollector { EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Make a space immune, immune spaces are assumed to have all live objects marked. - void ImmuneSpace(ContinuousSpace* space) + // Make a space immune, immune spaces have all live objects marked - that is the mark and + // live bitmaps are bound together. + void ImmuneSpace(space::ContinuousSpace* space) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Bind the live bits to the mark bits of bitmaps based on the gc type. - virtual void BindBitmaps() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie + // the image. Mark that portion of the heap as immune. + virtual void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void BindLiveToMarkBitmap(ContinuousSpace* space) + void BindLiveToMarkBitmap(space::ContinuousSpace* space) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); void UnBindBitmaps() @@ -127,21 +139,15 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Sweeps unmarked objects to complete the garbage collection. - virtual void Sweep(TimingLogger& timings, bool swap_bitmaps) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // Sweeps unmarked objects to complete the garbage collection. - void SweepLargeObjects(bool swap_bitmaps) - EXCLUSIVE_LOCKS_REQUIRED(GlobalSynchronization::heap_bitmap_lock_); + void SweepLargeObjects(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // Sweep only pointers within an array. WARNING: Trashes objects. - void SweepArray(TimingLogger& logger, ObjectStack* allocation_stack_, bool swap_bitmaps) + void SweepArray(accounting::ObjectStack* allocation_stack_, bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - // Swap bitmaps (if we are a full Gc then we swap the zygote bitmap too). - virtual void SwapBitmaps() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - void SwapLargeObjects() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - mirror::Object* GetClearedReferences() { return cleared_reference_list_; } @@ -177,12 +183,12 @@ class MarkSweep : public GarbageCollector { return freed_objects_; } - uint64_t GetTotalTime() const { - return total_time_; + uint64_t GetTotalTimeNs() const { + return total_time_ns_; } - uint64_t GetTotalPausedTime() const { - return total_paused_time_; + uint64_t GetTotalPausedTimeNs() const { + return total_paused_time_ns_; } uint64_t GetTotalFreedObjects() const { @@ -200,7 +206,7 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // Only sweep the weaks which are inside of an allocation stack. - void SweepSystemWeaksArray(ObjectStack* allocations) + void SweepSystemWeaksArray(accounting::ObjectStack* allocations) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); static bool VerifyIsLiveCallback(const mirror::Object* obj, void* arg) @@ -237,16 +243,6 @@ class MarkSweep : public GarbageCollector { return *gc_barrier_; } - TimingLogger& GetTimings() { - return timings_; - } - - CumulativeLogger& GetCumulativeTimings() { - return cumulative_timings_; - } - - void ResetCumulativeStatistics(); - protected: // Returns true if the object has its bit set in the mark bitmap. bool IsMarked(const mirror::Object* object) const; @@ -381,13 +377,14 @@ class MarkSweep : public GarbageCollector { // Whether or not we count how many of each type of object were scanned. static const bool kCountScannedTypes = false; - // Current space, we check this space first to avoid searching for the appropriate space for an object. - SpaceBitmap* current_mark_bitmap_; + // Current space, we check this space first to avoid searching for the appropriate space for an + // object. + accounting::SpaceBitmap* current_mark_bitmap_; // Cache java.lang.Class for optimization. mirror::Class* java_lang_Class_; - ObjectStack* mark_stack_; + accounting::ObjectStack* mark_stack_; mirror::Object* finger_; @@ -401,10 +398,15 @@ class MarkSweep : public GarbageCollector { mirror::Object* phantom_reference_list_; mirror::Object* cleared_reference_list_; + // Number of bytes freed in this collection. AtomicInteger freed_bytes_; + // Number of objects freed in this collection. AtomicInteger freed_objects_; + // Number of classes scanned, if kCountScannedTypes. AtomicInteger class_count_; + // Number of arrays scanned, if kCountScannedTypes. AtomicInteger array_count_; + // Number of non-class/arrays scanned, if kCountScannedTypes. AtomicInteger other_count_; AtomicInteger large_object_test_; AtomicInteger large_object_mark_; @@ -414,28 +416,19 @@ class MarkSweep : public GarbageCollector { AtomicInteger work_chunks_deleted_; AtomicInteger reference_count_; - // Cumulative statistics. - uint64_t total_time_; - uint64_t total_paused_time_; - uint64_t total_freed_objects_; - uint64_t total_freed_bytes_; - UniquePtr<Barrier> gc_barrier_; Mutex large_object_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; Mutex mark_stack_expand_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; const bool is_concurrent_; - TimingLogger timings_; - CumulativeLogger cumulative_timings_; - bool clear_soft_references_; friend class AddIfReachesAllocSpaceVisitor; // Used by mod-union table. friend class CheckBitmapVisitor; friend class CheckObjectVisitor; friend class CheckReferenceVisitor; - friend class Heap; + friend class art::gc::Heap; friend class InternTableEntryIsUnmarked; friend class MarkIfReachesAllocspaceVisitor; friend class ModUnionCheckReferences; @@ -453,6 +446,8 @@ class MarkSweep : public GarbageCollector { DISALLOW_COPY_AND_ASSIGN(MarkSweep); }; +} // namespace collector +} // namespace gc } // namespace art #endif // ART_SRC_GC_MARK_SWEEP_H_ diff --git a/src/gc/partial_mark_sweep.cc b/src/gc/collector/partial_mark_sweep.cc index f9c1787045..ef893c50b0 100644 --- a/src/gc/partial_mark_sweep.cc +++ b/src/gc/collector/partial_mark_sweep.cc @@ -16,36 +16,38 @@ #include "partial_mark_sweep.h" -#include "heap.h" -#include "large_object_space.h" +#include "gc/heap.h" +#include "gc/space/space.h" #include "partial_mark_sweep.h" -#include "space.h" #include "thread.h" namespace art { +namespace gc { +namespace collector { -PartialMarkSweep::PartialMarkSweep(Heap* heap, bool is_concurrent) - : MarkSweep(heap, is_concurrent) { +PartialMarkSweep::PartialMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) + : MarkSweep(heap, is_concurrent, name_prefix + (name_prefix.empty() ? "" : " ") + "partial") { cumulative_timings_.SetName(GetName()); } -PartialMarkSweep::~PartialMarkSweep() { - -} - void PartialMarkSweep::BindBitmaps() { MarkSweep::BindBitmaps(); - Spaces& spaces = GetHeap()->GetSpaces(); + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); // For partial GCs we need to bind the bitmap of the zygote space so that all objects in the // zygote space are viewed as marked. - for (Spaces::iterator it = spaces.begin(); it != spaces.end(); ++it) { - ContinuousSpace* space = *it; - if (space->GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) { + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) { + CHECK(space->IsZygoteSpace()); ImmuneSpace(space); } } } +} // namespace collector +} // namespace gc } // namespace art diff --git a/src/gc/partial_mark_sweep.h b/src/gc/collector/partial_mark_sweep.h index 64c0bcd8f8..bd4a580450 100644 --- a/src/gc/partial_mark_sweep.h +++ b/src/gc/collector/partial_mark_sweep.h @@ -14,13 +14,15 @@ * limitations under the License. */ -#ifndef ART_SRC_GC_PARTIAL_MARK_SWEEP_H_ -#define ART_SRC_GC_PARTIAL_MARK_SWEEP_H_ +#ifndef ART_SRC_GC_COLLECTOR_PARTIAL_MARK_SWEEP_H_ +#define ART_SRC_GC_COLLECTOR_PARTIAL_MARK_SWEEP_H_ #include "locks.h" #include "mark_sweep.h" namespace art { +namespace gc { +namespace collector { class PartialMarkSweep : public MarkSweep { public: @@ -28,16 +30,19 @@ class PartialMarkSweep : public MarkSweep { return kGcTypePartial; } - explicit PartialMarkSweep(Heap* heap, bool is_concurrent); - ~PartialMarkSweep(); + explicit PartialMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix = ""); + ~PartialMarkSweep() {} protected: - virtual void BindBitmaps() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Bind the live bits to the mark bits of bitmaps for spaces that aren't collected for partial + // collections, ie the Zygote space. Also mark this space is immune. + virtual void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); DISALLOW_COPY_AND_ASSIGN(PartialMarkSweep); }; +} // namespace collector +} // namespace gc } // namespace art -#endif // ART_SRC_GC_PARTIAL_MARK_SWEEP_H_ +#endif // ART_SRC_GC_COLLECTOR_PARTIAL_MARK_SWEEP_H_ diff --git a/src/gc/collector/sticky_mark_sweep.cc b/src/gc/collector/sticky_mark_sweep.cc new file mode 100644 index 0000000000..71e580d748 --- /dev/null +++ b/src/gc/collector/sticky_mark_sweep.cc @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gc/heap.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space.h" +#include "sticky_mark_sweep.h" +#include "thread.h" + +namespace art { +namespace gc { +namespace collector { + +StickyMarkSweep::StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) + : PartialMarkSweep(heap, is_concurrent, + name_prefix + (name_prefix.empty() ? "" : " ") + "sticky") { + cumulative_timings_.SetName(GetName()); +} + +void StickyMarkSweep::BindBitmaps() { + PartialMarkSweep::BindBitmaps(); + + const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces(); + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + // For sticky GC, we want to bind the bitmaps of all spaces as the allocation stack lets us + // know what was allocated since the last GC. A side-effect of binding the allocation space mark + // and live bitmap is that marking the objects will place them in the live bitmap. + // TODO: C++0x + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { + BindLiveToMarkBitmap(space); + } + } + + GetHeap()->GetLargeObjectsSpace()->CopyLiveToMarked(); +} + +void StickyMarkSweep::MarkReachableObjects() { + DisableFinger(); + RecursiveMarkDirtyObjects(accounting::CardTable::kCardDirty - 1); +} + +void StickyMarkSweep::Sweep(bool swap_bitmaps) { + timings_.NewSplit("SweepArray"); + accounting::ObjectStack* live_stack = GetHeap()->GetLiveStack(); + SweepArray(live_stack, false); +} + +} // namespace collector +} // namespace gc +} // namespace art diff --git a/src/gc/sticky_mark_sweep.h b/src/gc/collector/sticky_mark_sweep.h index 41ab0cc807..b16cfc1b49 100644 --- a/src/gc/sticky_mark_sweep.h +++ b/src/gc/collector/sticky_mark_sweep.h @@ -22,29 +22,34 @@ #include "partial_mark_sweep.h" namespace art { +namespace gc { +namespace collector { class StickyMarkSweep : public PartialMarkSweep { public: - virtual GcType GetGcType() const { + GcType GetGcType() const { return kGcTypeSticky; } - explicit StickyMarkSweep(Heap* heap, bool is_concurrent); - ~StickyMarkSweep(); + explicit StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix = ""); + ~StickyMarkSweep() {} + protected: - virtual void BindBitmaps() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Bind the live bits to the mark bits of bitmaps for all spaces, all spaces other than the + // alloc space will be marked as immune. + void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - virtual void MarkReachableObjects() + void MarkReachableObjects() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - virtual void Sweep(TimingLogger& timings, bool swap_bitmaps) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); DISALLOW_COPY_AND_ASSIGN(StickyMarkSweep); }; +} // namespace collector +} // namespace gc } // namespace art #endif // ART_SRC_GC_STICKY_MARK_SWEEP_H_ diff --git a/src/gc/garbage_collector.cc b/src/gc/garbage_collector.cc deleted file mode 100644 index 94daec7c31..0000000000 --- a/src/gc/garbage_collector.cc +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "garbage_collector.h" - -#include "base/mutex-inl.h" -#include "thread.h" -#include "thread_list.h" - -namespace art { - GarbageCollector::GarbageCollector(Heap* heap) - : heap_(heap), - duration_(0) { - - } - - bool GarbageCollector::HandleDirtyObjectsPhase() { - DCHECK(IsConcurrent()); - return true; - } - - void GarbageCollector::RegisterPause(uint64_t nano_length) { - pause_times_.push_back(nano_length); - } - - void GarbageCollector::Run() { - Thread* self = Thread::Current(); - ThreadList* thread_list = Runtime::Current()->GetThreadList(); - - uint64_t start_time = NanoTime(); - pause_times_.clear(); - duration_ = 0; - - InitializePhase(); - - if (!IsConcurrent()) { - // Pause is the entire length of the GC. - uint64_t pause_start = NanoTime(); - thread_list->SuspendAll(); - MarkingPhase(); - ReclaimPhase(); - thread_list->ResumeAll(); - uint64_t pause_end = NanoTime(); - pause_times_.push_back(pause_end - pause_start); - } else { - { - ReaderMutexLock mu(self, *Locks::mutator_lock_); - MarkingPhase(); - } - bool done = false; - while (!done) { - uint64_t pause_start = NanoTime(); - thread_list->SuspendAll(); - done = HandleDirtyObjectsPhase(); - thread_list->ResumeAll(); - uint64_t pause_end = NanoTime(); - pause_times_.push_back(pause_end - pause_start); - } - { - ReaderMutexLock mu(self, *Locks::mutator_lock_); - ReclaimPhase(); - } - } - - uint64_t end_time = NanoTime(); - duration_ = end_time - start_time; - - FinishPhase(); - } - - GarbageCollector::~GarbageCollector() { - - } -} // namespace art diff --git a/src/heap.cc b/src/gc/heap.cc index f39c26ebde..9ec1f21243 100644 --- a/src/heap.cc +++ b/src/gc/heap.cc @@ -16,6 +16,8 @@ #include "heap.h" +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include <cutils/trace.h> #include <sys/types.h> #include <sys/wait.h> @@ -25,20 +27,17 @@ #include "base/stl_util.h" #include "cutils/sched_policy.h" #include "debugger.h" -#include "gc/atomic_stack.h" -#include "gc/card_table.h" -#include "gc/card_table-inl.h" -#include "gc/heap_bitmap.h" -#include "gc/heap_bitmap-inl.h" -#include "gc/large_object_space.h" -#include "gc/mark_sweep.h" -#include "gc/mark_sweep-inl.h" -#include "gc/partial_mark_sweep.h" -#include "gc/space_bitmap-inl.h" -#include "gc/sticky_mark_sweep.h" -#include "gc/mod_union_table.h" -#include "gc/mod_union_table-inl.h" -#include "gc/space.h" +#include "gc/accounting/atomic_stack.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/heap_bitmap-inl.h" +#include "gc/accounting/mod_union_table-inl.h" +#include "gc/accounting/space_bitmap-inl.h" +#include "gc/collector/mark_sweep-inl.h" +#include "gc/collector/partial_mark_sweep.h" +#include "gc/collector/sticky_mark_sweep.h" +#include "gc/space/image_space.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" #include "image.h" #include "invoke_arg_array_builder.h" #include "mirror/class-inl.h" @@ -56,10 +55,15 @@ #include "well_known_classes.h" namespace art { +namespace gc { +// When to create a log message about a slow GC, 100ms. static const uint64_t kSlowGcThreshold = MsToNs(100); +// When to create a log message about a slow pause, 5ms. static const uint64_t kLongGcPauseThreshold = MsToNs(5); static const bool kDumpGcPerformanceOnShutdown = false; +// Minimum amount of remaining bytes before a concurrent GC is triggered. +static const size_t kMinConcurrentRemainingBytes = 128 * KB; const double Heap::kDefaultTargetUtilization = 0.5; static bool GenerateImage(const std::string& image_file_name) { @@ -156,19 +160,18 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max card_table_(NULL), concurrent_gc_(concurrent_gc), have_zygote_space_(false), + reference_queue_lock_(NULL), is_gc_running_(false), - last_gc_type_(kGcTypeNone), - enforce_heap_growth_rate_(false), + last_gc_type_(collector::kGcTypeNone), capacity_(capacity), growth_limit_(growth_limit), max_allowed_footprint_(initial_size), - concurrent_start_size_(128 * KB), - concurrent_min_free_(256 * KB), - concurrent_start_bytes_(concurrent_gc ? initial_size - concurrent_start_size_ : - std::numeric_limits<size_t>::max()), + concurrent_start_bytes_(concurrent_gc ? initial_size - (kMinConcurrentRemainingBytes) + : std::numeric_limits<size_t>::max()), sticky_gc_count_(0), - total_bytes_freed_(0), - total_objects_freed_(0), + sticky_to_partial_gc_ratio_(10), + total_bytes_freed_ever_(0), + total_objects_freed_ever_(0), large_object_threshold_(3 * kPageSize), num_bytes_allocated_(0), verify_missing_card_marks_(false), @@ -176,12 +179,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max verify_pre_gc_heap_(false), verify_post_gc_heap_(false), verify_mod_union_table_(false), - partial_gc_frequency_(10), min_alloc_space_size_for_sticky_gc_(2 * MB), min_remaining_space_for_sticky_gc_(1 * MB), - last_trim_time_(0), + last_trim_time_ms_(0), allocation_rate_(0), - max_allocation_stack_size_(MB), + max_allocation_stack_size_(kDesiredHeapVerification > kNoHeapVerification? KB : MB), reference_referent_offset_(0), reference_queue_offset_(0), reference_queueNext_offset_(0), @@ -198,34 +200,34 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max LOG(INFO) << "Heap() entering"; } - live_bitmap_.reset(new HeapBitmap(this)); - mark_bitmap_.reset(new HeapBitmap(this)); + live_bitmap_.reset(new accounting::HeapBitmap(this)); + mark_bitmap_.reset(new accounting::HeapBitmap(this)); // Requested begin for the alloc space, to follow the mapped image and oat files byte* requested_begin = NULL; std::string image_file_name(original_image_file_name); if (!image_file_name.empty()) { - ImageSpace* image_space = NULL; + space::ImageSpace* image_space = NULL; if (OS::FileExists(image_file_name.c_str())) { // If the /system file exists, it should be up-to-date, don't try to generate - image_space = ImageSpace::Create(image_file_name); + image_space = space::ImageSpace::Create(image_file_name); } else { // If the /system file didn't exist, we need to use one from the dalvik-cache. // If the cache file exists, try to open, but if it fails, regenerate. // If it does not exist, generate. image_file_name = GetDalvikCacheFilenameOrDie(image_file_name); if (OS::FileExists(image_file_name.c_str())) { - image_space = ImageSpace::Create(image_file_name); + image_space = space::ImageSpace::Create(image_file_name); } if (image_space == NULL) { CHECK(GenerateImage(image_file_name)) << "Failed to generate image: " << image_file_name; - image_space = ImageSpace::Create(image_file_name); + image_space = space::ImageSpace::Create(image_file_name); } } CHECK(image_space != NULL) << "Failed to create space from " << image_file_name; - AddSpace(image_space); + AddContinuousSpace(image_space); // Oat files referenced by image files immediately follow them in memory, ensure alloc space // isn't going to get in the middle byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd(); @@ -247,46 +249,47 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max // Allocate the large object space. const bool kUseFreeListSpaceForLOS = false; if (kUseFreeListSpaceForLOS) { - large_object_space_.reset(FreeListSpace::Create("large object space", NULL, capacity)); + large_object_space_ = space::FreeListSpace::Create("large object space", NULL, capacity); } else { - large_object_space_.reset(LargeObjectMapSpace::Create("large object space")); + large_object_space_ = space::LargeObjectMapSpace::Create("large object space"); } - live_bitmap_->SetLargeObjects(large_object_space_->GetLiveObjects()); - mark_bitmap_->SetLargeObjects(large_object_space_->GetMarkObjects()); + CHECK(large_object_space_ != NULL) << "Failed to create large object space"; + AddDiscontinuousSpace(large_object_space_); - UniquePtr<DlMallocSpace> alloc_space(DlMallocSpace::Create("alloc space", initial_size, - growth_limit, capacity, - requested_begin)); - alloc_space_ = alloc_space.release(); + alloc_space_ = space::DlMallocSpace::Create("alloc space", + initial_size, + growth_limit, capacity, + requested_begin); CHECK(alloc_space_ != NULL) << "Failed to create alloc space"; alloc_space_->SetFootprintLimit(alloc_space_->Capacity()); - AddSpace(alloc_space_); + AddContinuousSpace(alloc_space_); - // Spaces are sorted in order of Begin(). - byte* heap_begin = spaces_.front()->Begin(); - size_t heap_capacity = spaces_.back()->End() - spaces_.front()->Begin(); - if (spaces_.back()->IsAllocSpace()) { - heap_capacity += spaces_.back()->AsAllocSpace()->NonGrowthLimitCapacity(); + // Compute heap capacity. Continuous spaces are sorted in order of Begin(). + byte* heap_begin = continuous_spaces_.front()->Begin(); + size_t heap_capacity = continuous_spaces_.back()->End() - continuous_spaces_.front()->Begin(); + if (continuous_spaces_.back()->IsDlMallocSpace()) { + heap_capacity += continuous_spaces_.back()->AsDlMallocSpace()->NonGrowthLimitCapacity(); } // Mark image objects in the live bitmap // TODO: C++0x - for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - Space* space = *it; + typedef std::vector<space::ContinuousSpace*>::iterator It; + for (It it = continuous_spaces_.begin(); it != continuous_spaces_.end(); ++it) { + space::ContinuousSpace* space = *it; if (space->IsImageSpace()) { - ImageSpace* image_space = space->AsImageSpace(); + space::ImageSpace* image_space = space->AsImageSpace(); image_space->RecordImageAllocations(image_space->GetLiveBitmap()); } } // Allocate the card table. - card_table_.reset(CardTable::Create(heap_begin, heap_capacity)); + card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity)); CHECK(card_table_.get() != NULL) << "Failed to create card table"; - mod_union_table_.reset(new ModUnionTableToZygoteAllocspace<ModUnionTableReferenceCache>(this)); - CHECK(mod_union_table_.get() != NULL) << "Failed to create mod-union table"; + image_mod_union_table_.reset(new accounting::ModUnionTableToZygoteAllocspace(this)); + CHECK(image_mod_union_table_.get() != NULL) << "Failed to create image mod-union table"; - zygote_mod_union_table_.reset(new ModUnionTableCardCache(this)); + zygote_mod_union_table_.reset(new accounting::ModUnionTableCardCache(this)); CHECK(zygote_mod_union_table_.get() != NULL) << "Failed to create Zygote mod-union table"; // TODO: Count objects in the image space here. @@ -294,11 +297,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max // Default mark stack size in bytes. static const size_t default_mark_stack_size = 64 * KB; - mark_stack_.reset(ObjectStack::Create("mark stack", default_mark_stack_size)); - allocation_stack_.reset(ObjectStack::Create("allocation stack", - max_allocation_stack_size_)); - live_stack_.reset(ObjectStack::Create("live stack", - max_allocation_stack_size_)); + mark_stack_.reset(accounting::ObjectStack::Create("mark stack", default_mark_stack_size)); + allocation_stack_.reset(accounting::ObjectStack::Create("allocation stack", + max_allocation_stack_size_)); + live_stack_.reset(accounting::ObjectStack::Create("live stack", + max_allocation_stack_size_)); // It's still too early to take a lock because there are no threads yet, but we can create locks // now. We don't create it earlier to make it clear that you can't use locks during heap @@ -308,17 +311,17 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max *gc_complete_lock_)); // Create the reference queue lock, this is required so for parrallel object scanning in the GC. - reference_queue_lock_.reset(new Mutex("reference queue lock")); + reference_queue_lock_ = new Mutex("reference queue lock"); - last_gc_time_ = NanoTime(); + last_gc_time_ns_ = NanoTime(); last_gc_size_ = GetBytesAllocated(); // Create our garbage collectors. for (size_t i = 0; i < 2; ++i) { const bool concurrent = i != 0; - mark_sweep_collectors_.push_back(new MarkSweep(this, concurrent)); - mark_sweep_collectors_.push_back(new PartialMarkSweep(this, concurrent)); - mark_sweep_collectors_.push_back(new StickyMarkSweep(this, concurrent)); + mark_sweep_collectors_.push_back(new collector::MarkSweep(this, concurrent)); + mark_sweep_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); + mark_sweep_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); } CHECK(max_allowed_footprint_ != 0); @@ -331,7 +334,7 @@ void Heap::CreateThreadPool() { // TODO: Make sysconf(_SC_NPROCESSORS_CONF) be a helper function? // Use the number of processors - 1 since the thread doing the GC does work while its waiting for // workers to complete. - thread_pool_.reset(new ThreadPool(sysconf(_SC_NPROCESSORS_CONF) - 1)); + thread_pool_.reset(new ThreadPool(1)); // new ThreadPool(sysconf(_SC_NPROCESSORS_CONF) - 1)); } void Heap::DeleteThreadPool() { @@ -339,44 +342,55 @@ void Heap::DeleteThreadPool() { } // Sort spaces based on begin address -struct SpaceSorter { - bool operator ()(const ContinuousSpace* a, const ContinuousSpace* b) const { +struct ContinuousSpaceSorter { + bool operator ()(const space::ContinuousSpace* a, const space::ContinuousSpace* b) const { return a->Begin() < b->Begin(); } }; -void Heap::AddSpace(ContinuousSpace* space) { +void Heap::AddContinuousSpace(space::ContinuousSpace* space) { WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); DCHECK(space != NULL); DCHECK(space->GetLiveBitmap() != NULL); - live_bitmap_->AddSpaceBitmap(space->GetLiveBitmap()); + live_bitmap_->AddContinuousSpaceBitmap(space->GetLiveBitmap()); DCHECK(space->GetMarkBitmap() != NULL); - mark_bitmap_->AddSpaceBitmap(space->GetMarkBitmap()); - spaces_.push_back(space); - if (space->IsAllocSpace()) { - alloc_space_ = space->AsAllocSpace(); + mark_bitmap_->AddContinuousSpaceBitmap(space->GetMarkBitmap()); + continuous_spaces_.push_back(space); + if (space->IsDlMallocSpace() && !space->IsLargeObjectSpace()) { + alloc_space_ = space->AsDlMallocSpace(); } // Ensure that spaces remain sorted in increasing order of start address (required for CMS finger) - std::sort(spaces_.begin(), spaces_.end(), SpaceSorter()); + std::sort(continuous_spaces_.begin(), continuous_spaces_.end(), ContinuousSpaceSorter()); // Ensure that ImageSpaces < ZygoteSpaces < AllocSpaces so that we can do address based checks to // avoid redundant marking. bool seen_zygote = false, seen_alloc = false; - for (Spaces::const_iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - Space* space = *it; + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = continuous_spaces_.begin(); it != continuous_spaces_.end(); ++it) { + space::ContinuousSpace* space = *it; if (space->IsImageSpace()) { DCHECK(!seen_zygote); DCHECK(!seen_alloc); } else if (space->IsZygoteSpace()) { DCHECK(!seen_alloc); seen_zygote = true; - } else if (space->IsAllocSpace()) { + } else if (space->IsDlMallocSpace()) { seen_alloc = true; } } } +void Heap::AddDiscontinuousSpace(space::DiscontinuousSpace* space) { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + DCHECK(space != NULL); + DCHECK(space->GetLiveObjects() != NULL); + live_bitmap_->AddDiscontinuousObjectSet(space->GetLiveObjects()); + DCHECK(space->GetMarkObjects() != NULL); + mark_bitmap_->AddDiscontinuousObjectSet(space->GetMarkObjects()); + discontinuous_spaces_.push_back(space); +} + void Heap::DumpGcPerformanceInfo(std::ostream& os) { // Dump cumulative timings. os << "Dumping cumulative Gc timings\n"; @@ -385,14 +399,15 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { // Dump cumulative loggers for each GC type. // TODO: C++0x uint64_t total_paused_time = 0; - for (Collectors::const_iterator it = mark_sweep_collectors_.begin(); + typedef std::vector<collector::MarkSweep*>::const_iterator It; + for (It it = mark_sweep_collectors_.begin(); it != mark_sweep_collectors_.end(); ++it) { - MarkSweep* collector = *it; + collector::MarkSweep* collector = *it; CumulativeLogger& logger = collector->GetCumulativeTimings(); if (logger.GetTotalNs() != 0) { os << Dumpable<CumulativeLogger>(logger); const uint64_t total_ns = logger.GetTotalNs(); - const uint64_t total_pause_ns = (*it)->GetTotalPausedTime(); + const uint64_t total_pause_ns = (*it)->GetTotalPausedTimeNs(); double seconds = NsToMs(logger.GetTotalNs()) / 1000.0; const uint64_t freed_bytes = collector->GetTotalFreedBytes(); const uint64_t freed_objects = collector->GetTotalFreedObjects(); @@ -407,15 +422,15 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { } } uint64_t allocation_time = static_cast<uint64_t>(total_allocation_time_) * kTimeAdjust; - size_t total_objects_allocated = GetTotalObjectsAllocated(); - size_t total_bytes_allocated = GetTotalBytesAllocated(); + size_t total_objects_allocated = GetObjectsAllocatedEver(); + size_t total_bytes_allocated = GetBytesAllocatedEver(); if (total_duration != 0) { const double total_seconds = double(total_duration / 1000) / 1000000.0; os << "Total time spent in GC: " << PrettyDuration(total_duration) << "\n"; os << "Mean GC size throughput: " - << PrettySize(GetTotalBytesFreed() / total_seconds) << "/s\n"; + << PrettySize(GetBytesFreedEver() / total_seconds) << "/s\n"; os << "Mean GC object throughput: " - << (GetTotalObjectsFreed() / total_seconds) << " objects/s\n"; + << (GetObjectsFreedEver() / total_seconds) << " objects/s\n"; } os << "Total number of allocations: " << total_objects_allocated << "\n"; os << "Total bytes allocated " << PrettySize(total_bytes_allocated) << "\n"; @@ -444,33 +459,59 @@ Heap::~Heap() { // heap lock held. We know though that no non-daemon threads are executing, and we know that // all daemon threads are suspended, and we also know that the threads list have been deleted, so // those threads can't resume. We're the only running thread, and we can do whatever we like... - STLDeleteElements(&spaces_); + STLDeleteElements(&continuous_spaces_); + STLDeleteElements(&discontinuous_spaces_); delete gc_complete_lock_; + delete reference_queue_lock_; } -ContinuousSpace* Heap::FindSpaceFromObject(const mirror::Object* obj) const { +space::ContinuousSpace* Heap::FindContinuousSpaceFromObject(const mirror::Object* obj, + bool fail_ok) const { // TODO: C++0x auto - for (Spaces::const_iterator it = spaces_.begin(); it != spaces_.end(); ++it) { + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { if ((*it)->Contains(obj)) { return *it; } } - LOG(FATAL) << "object " << reinterpret_cast<const void*>(obj) << " not inside any spaces!"; + if (!fail_ok) { + LOG(FATAL) << "object " << reinterpret_cast<const void*>(obj) << " not inside any spaces!"; + } return NULL; } -ImageSpace* Heap::GetImageSpace() { +space::DiscontinuousSpace* Heap::FindDiscontinuousSpaceFromObject(const mirror::Object* obj, + bool fail_ok) const { // TODO: C++0x auto - for (Spaces::const_iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - if ((*it)->IsImageSpace()) { - return (*it)->AsImageSpace(); + typedef std::vector<space::DiscontinuousSpace*>::const_iterator It; + for (It it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { + if ((*it)->Contains(obj)) { + return *it; } } + if (!fail_ok) { + LOG(FATAL) << "object " << reinterpret_cast<const void*>(obj) << " not inside any spaces!"; + } return NULL; } -DlMallocSpace* Heap::GetAllocSpace() { - return alloc_space_; +space::Space* Heap::FindSpaceFromObject(const mirror::Object* obj, bool fail_ok) const { + space::Space* result = FindContinuousSpaceFromObject(obj, true); + if (result != NULL) { + return result; + } + return FindDiscontinuousSpaceFromObject(obj, true); +} + +space::ImageSpace* Heap::GetImageSpace() const { + // TODO: C++0x auto + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { + if ((*it)->IsImageSpace()) { + return (*it)->AsImageSpace(); + } + } + return NULL; } static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void* arg) { @@ -501,17 +542,17 @@ mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_co // range. This also means that we rely on SetClass not dirtying the object's card. if (byte_count >= large_object_threshold_ && have_zygote_space_ && c->IsPrimitiveArray()) { size = RoundUp(byte_count, kPageSize); - obj = Allocate(self, large_object_space_.get(), size); + obj = Allocate(self, large_object_space_, size); // Make sure that our large object didn't get placed anywhere within the space interval or else // it breaks the immune range. DCHECK(obj == NULL || - reinterpret_cast<byte*>(obj) < spaces_.front()->Begin() || - reinterpret_cast<byte*>(obj) >= spaces_.back()->End()); + reinterpret_cast<byte*>(obj) < continuous_spaces_.front()->Begin() || + reinterpret_cast<byte*>(obj) >= continuous_spaces_.back()->End()); } else { obj = Allocate(self, alloc_space_, byte_count); // Ensure that we did not allocate into a zygote space. - DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj)->IsZygoteSpace()); + DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); size = alloc_space_->AllocationSize(obj); } @@ -543,8 +584,8 @@ mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_co } std::ostringstream oss; int64_t total_bytes_free = GetFreeMemory(); - uint64_t alloc_space_size = alloc_space_->GetNumBytesAllocated(); - uint64_t large_object_size = large_object_space_->GetNumObjectsAllocated(); + uint64_t alloc_space_size = alloc_space_->GetBytesAllocated(); + uint64_t large_object_size = large_object_space_->GetObjectsAllocated(); oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free << " free bytes; allocation space size " << alloc_space_size << "; large object space size " << large_object_size; @@ -552,9 +593,11 @@ mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_co if (total_bytes_free >= byte_count) { size_t max_contiguous_allocation = 0; // TODO: C++0x auto - for (Spaces::const_iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - if ((*it)->IsAllocSpace()) { - (*it)->AsAllocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation); + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + space->AsDlMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation); } } oss << "; failed due to fragmentation (largest possible contiguous allocation " @@ -570,20 +613,41 @@ bool Heap::IsHeapAddress(const mirror::Object* obj) { if (obj == NULL) { return true; } - if (!IsAligned<kObjectAlignment>(obj)) { + if (UNLIKELY(!IsAligned<kObjectAlignment>(obj))) { return false; } - for (size_t i = 0; i < spaces_.size(); ++i) { - if (spaces_[i]->Contains(obj)) { - return true; - } - } - return large_object_space_->Contains(obj); + return FindSpaceFromObject(obj, true) != NULL; } bool Heap::IsLiveObjectLocked(const mirror::Object* obj) { - Locks::heap_bitmap_lock_->AssertReaderHeld(Thread::Current()); - return IsHeapAddress(obj) && GetLiveBitmap()->Test(obj); + //Locks::heap_bitmap_lock_->AssertReaderHeld(Thread::Current()); + if (obj == NULL) { + return false; + } + if (UNLIKELY(!IsAligned<kObjectAlignment>(obj))) { + return false; + } + space::ContinuousSpace* cont_space = FindContinuousSpaceFromObject(obj, true); + if (cont_space != NULL) { + if (cont_space->GetLiveBitmap()->Test(obj)) { + return true; + } + } else { + space::DiscontinuousSpace* disc_space = FindDiscontinuousSpaceFromObject(obj, true); + if (disc_space != NULL) { + if (disc_space->GetLiveObjects()->Test(obj)) { + return true; + } + } + } + for (size_t i = 0; i < 5; ++i) { + if (allocation_stack_->Contains(const_cast<mirror::Object*>(obj)) || + live_stack_->Contains(const_cast<mirror::Object*>(obj))) { + return true; + } + NanoSleep(MsToNs(10)); + } + return false; } void Heap::VerifyObjectImpl(const mirror::Object* obj) { @@ -596,16 +660,19 @@ void Heap::VerifyObjectImpl(const mirror::Object* obj) { void Heap::DumpSpaces() { // TODO: C++0x auto - for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - ContinuousSpace* space = *it; - SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); LOG(INFO) << space << " " << *space << "\n" << live_bitmap << " " << *live_bitmap << "\n" << mark_bitmap << " " << *mark_bitmap; } - if (large_object_space_.get() != NULL) { - large_object_space_->Dump(LOG(INFO)); + typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2; + for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { + space::DiscontinuousSpace* space = *it; + LOG(INFO) << space << " " << *space << "\n"; } } @@ -636,18 +703,11 @@ void Heap::VerifyObjectBody(const mirror::Object* obj) { if (verify_object_mode_ != kVerifyAllFast) { // TODO: the bitmap tests below are racy if VerifyObjectBody is called without the // heap_bitmap_lock_. - if (!GetLiveBitmap()->Test(obj)) { - // Check the allocation stack / live stack. - if (!std::binary_search(live_stack_->Begin(), live_stack_->End(), obj) && - std::find(allocation_stack_->Begin(), allocation_stack_->End(), obj) == - allocation_stack_->End()) { - if (large_object_space_->GetLiveObjects()->Test(obj)) { - DumpSpaces(); - LOG(FATAL) << "Object is dead: " << obj; - } - } + if (!IsLiveObjectLocked(obj)) { + DumpSpaces(); + LOG(FATAL) << "Object is dead: " << obj; } - if (!GetLiveBitmap()->Test(c)) { + if (!IsLiveObjectLocked(c)) { LOG(FATAL) << "Class of object is dead: " << c << " in object: " << obj; } } @@ -682,7 +742,7 @@ void Heap::RecordAllocation(size_t size, mirror::Object* obj) { // This is safe to do since the GC will never free objects which are neither in the allocation // stack or the live bitmap. while (!allocation_stack_->AtomicPushBack(obj)) { - CollectGarbageInternal(kGcTypeSticky, kGcCauseForAlloc, false); + CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false); } } @@ -702,7 +762,8 @@ void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) { } } -mirror::Object* Heap::TryToAllocate(Thread* self, AllocSpace* space, size_t alloc_size, bool grow) { +mirror::Object* Heap::TryToAllocate(Thread* self, space::AllocSpace* space, size_t alloc_size, + bool grow) { // Should we try to use a CAS here and fix up num_bytes_allocated_ later with AllocationSize? if (num_bytes_allocated_ + alloc_size > max_allowed_footprint_) { // max_allowed_footprint_ <= growth_limit_ so it is safe to check in here. @@ -710,23 +771,12 @@ mirror::Object* Heap::TryToAllocate(Thread* self, AllocSpace* space, size_t allo // Completely out of memory. return NULL; } - - if (enforce_heap_growth_rate_) { - if (grow) { - // Grow the heap by alloc_size extra bytes. - max_allowed_footprint_ = std::min(max_allowed_footprint_ + alloc_size, growth_limit_); - VLOG(gc) << "Grow heap to " << PrettySize(max_allowed_footprint_) - << " for a " << PrettySize(alloc_size) << " allocation"; - } else { - return NULL; - } - } } return space->Alloc(self, alloc_size); } -mirror::Object* Heap::Allocate(Thread* self, AllocSpace* space, size_t alloc_size) { +mirror::Object* Heap::Allocate(Thread* self, space::AllocSpace* space, size_t alloc_size) { // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are // done in the runnable state where suspension is expected. DCHECK_EQ(self->GetState(), kRunnable); @@ -739,8 +789,8 @@ mirror::Object* Heap::Allocate(Thread* self, AllocSpace* space, size_t alloc_siz // The allocation failed. If the GC is running, block until it completes, and then retry the // allocation. - GcType last_gc = WaitForConcurrentGcToComplete(self); - if (last_gc != kGcTypeNone) { + collector::GcType last_gc = WaitForConcurrentGcToComplete(self); + if (last_gc != collector::kGcTypeNone) { // A GC was in progress and we blocked, retry allocation now that memory has been freed. ptr = TryToAllocate(self, space, alloc_size, false); if (ptr != NULL) { @@ -749,20 +799,21 @@ mirror::Object* Heap::Allocate(Thread* self, AllocSpace* space, size_t alloc_siz } // Loop through our different Gc types and try to Gc until we get enough free memory. - for (size_t i = static_cast<size_t>(last_gc) + 1; i < static_cast<size_t>(kGcTypeMax); ++i) { + for (size_t i = static_cast<size_t>(last_gc) + 1; + i < static_cast<size_t>(collector::kGcTypeMax); ++i) { bool run_gc = false; - GcType gc_type = static_cast<GcType>(i); + collector::GcType gc_type = static_cast<collector::GcType>(i); switch (gc_type) { - case kGcTypeSticky: { + case collector::kGcTypeSticky: { const size_t alloc_space_size = alloc_space_->Size(); run_gc = alloc_space_size > min_alloc_space_size_for_sticky_gc_ && alloc_space_->Capacity() - alloc_space_size >= min_remaining_space_for_sticky_gc_; break; } - case kGcTypePartial: + case collector::kGcTypePartial: run_gc = have_zygote_space_; break; - case kGcTypeFull: + case collector::kGcTypeFull: run_gc = true; break; default: @@ -771,7 +822,7 @@ mirror::Object* Heap::Allocate(Thread* self, AllocSpace* space, size_t alloc_siz if (run_gc) { // If we actually ran a different type of Gc than requested, we can skip the index forwards. - GcType gc_type_ran = CollectGarbageInternal(gc_type, kGcCauseForAlloc, false); + collector::GcType gc_type_ran = CollectGarbageInternal(gc_type, kGcCauseForAlloc, false); DCHECK_GE(static_cast<size_t>(gc_type_ran), i); i = static_cast<size_t>(gc_type_ran); @@ -799,7 +850,7 @@ mirror::Object* Heap::Allocate(Thread* self, AllocSpace* space, size_t alloc_siz << " allocation"; // We don't need a WaitForConcurrentGcToComplete here either. - CollectGarbageInternal(kGcTypeFull, kGcCauseForAlloc, true); + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); return TryToAllocate(self, space, alloc_size, true); } @@ -809,45 +860,54 @@ void Heap::SetTargetHeapUtilization(float target) { target_utilization_ = target; } -int64_t Heap::GetMaxMemory() const { - return growth_limit_; -} - -int64_t Heap::GetTotalMemory() const { - return GetMaxMemory(); -} - -int64_t Heap::GetFreeMemory() const { - return GetMaxMemory() - num_bytes_allocated_; -} - -size_t Heap::GetTotalBytesFreed() const { - return total_bytes_freed_; -} - -size_t Heap::GetTotalObjectsFreed() const { - return total_objects_freed_; +size_t Heap::GetObjectsAllocated() const { + size_t total = 0; + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + total += space->AsDlMallocSpace()->GetObjectsAllocated(); + } + } + typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2; + for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { + space::DiscontinuousSpace* space = *it; + total += space->AsLargeObjectSpace()->GetObjectsAllocated(); + } + return total; } -size_t Heap::GetTotalObjectsAllocated() const { - size_t total = large_object_space_->GetTotalObjectsAllocated(); - for (Spaces::const_iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - Space* space = *it; - if (space->IsAllocSpace()) { - total += space->AsAllocSpace()->GetTotalObjectsAllocated(); +size_t Heap::GetObjectsAllocatedEver() const { + size_t total = 0; + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + total += space->AsDlMallocSpace()->GetTotalObjectsAllocated(); } } + typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2; + for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { + space::DiscontinuousSpace* space = *it; + total += space->AsLargeObjectSpace()->GetTotalObjectsAllocated(); + } return total; } -size_t Heap::GetTotalBytesAllocated() const { - size_t total = large_object_space_->GetTotalBytesAllocated(); - for (Spaces::const_iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - Space* space = *it; - if (space->IsAllocSpace()) { - total += space->AsAllocSpace()->GetTotalBytesAllocated(); +size_t Heap::GetBytesAllocatedEver() const { + size_t total = 0; + typedef std::vector<space::ContinuousSpace*>::const_iterator It; + for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + total += space->AsDlMallocSpace()->GetTotalBytesAllocated(); } } + typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2; + for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { + space::DiscontinuousSpace* space = *it; + total += space->AsLargeObjectSpace()->GetTotalBytesAllocated(); + } return total; } @@ -945,7 +1005,7 @@ class ReferringObjectsFinder { // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for // annotalysis on visitors. void operator()(const mirror::Object* o) const NO_THREAD_SAFETY_ANALYSIS { - MarkSweep::VisitObjectReferences(o, *this); + collector::MarkSweep::VisitObjectReferences(o, *this); } // For MarkSweep::VisitObjectReferences. @@ -983,7 +1043,7 @@ void Heap::CollectGarbage(bool clear_soft_references) { // last GC will not have necessarily been cleared. Thread* self = Thread::Current(); WaitForConcurrentGcToComplete(self); - CollectGarbageInternal(kGcTypeFull, kGcCauseExplicit, clear_soft_references); + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseExplicit, clear_soft_references); } void Heap::PreZygoteFork() { @@ -1006,29 +1066,22 @@ void Heap::PreZygoteFork() { FlushAllocStack(); } - // Replace the first alloc space we find with a zygote space. - // TODO: C++0x auto - for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - if ((*it)->IsAllocSpace()) { - DlMallocSpace* zygote_space = (*it)->AsAllocSpace(); - - // Turns the current alloc space into a Zygote space and obtain the new alloc space composed - // of the remaining available heap memory. - alloc_space_ = zygote_space->CreateZygoteSpace(); - alloc_space_->SetFootprintLimit(alloc_space_->Capacity()); - - // Change the GC retention policy of the zygote space to only collect when full. - zygote_space->SetGcRetentionPolicy(kGcRetentionPolicyFullCollect); - AddSpace(alloc_space_); - have_zygote_space_ = true; - break; - } - } + // Turns the current alloc space into a Zygote space and obtain the new alloc space composed + // of the remaining available heap memory. + space::DlMallocSpace* zygote_space = alloc_space_; + alloc_space_ = zygote_space->CreateZygoteSpace(); + alloc_space_->SetFootprintLimit(alloc_space_->Capacity()); + + // Change the GC retention policy of the zygote space to only collect when full. + zygote_space->SetGcRetentionPolicy(space::kGcRetentionPolicyFullCollect); + AddContinuousSpace(alloc_space_); + have_zygote_space_ = true; // Reset the cumulative loggers since we now have a few additional timing phases. // TODO: C++0x - for (Collectors::const_iterator it = mark_sweep_collectors_.begin(); - it != mark_sweep_collectors_.end(); ++it) { + typedef std::vector<collector::MarkSweep*>::const_iterator It; + for (It it = mark_sweep_collectors_.begin(), end = mark_sweep_collectors_.end(); + it != end; ++it) { (*it)->ResetCumulativeStatistics(); } } @@ -1039,11 +1092,8 @@ void Heap::FlushAllocStack() { allocation_stack_->Reset(); } -size_t Heap::GetUsedMemorySize() const { - return num_bytes_allocated_; -} - -void Heap::MarkAllocStack(SpaceBitmap* bitmap, SpaceSetMap* large_objects, ObjectStack* stack) { +void Heap::MarkAllocStack(accounting::SpaceBitmap* bitmap, accounting::SpaceSetMap* large_objects, + accounting::ObjectStack* stack) { mirror::Object** limit = stack->End(); for (mirror::Object** it = stack->Begin(); it != limit; ++it) { const mirror::Object* obj = *it; @@ -1056,7 +1106,8 @@ void Heap::MarkAllocStack(SpaceBitmap* bitmap, SpaceSetMap* large_objects, Objec } } -void Heap::UnMarkAllocStack(SpaceBitmap* bitmap, SpaceSetMap* large_objects, ObjectStack* stack) { +void Heap::UnMarkAllocStack(accounting::SpaceBitmap* bitmap, accounting::SpaceSetMap* large_objects, + accounting::ObjectStack* stack) { mirror::Object** limit = stack->End(); for (mirror::Object** it = stack->Begin(); it != limit; ++it) { const mirror::Object* obj = *it; @@ -1069,8 +1120,22 @@ void Heap::UnMarkAllocStack(SpaceBitmap* bitmap, SpaceSetMap* large_objects, Obj } } -GcType Heap::CollectGarbageInternal(GcType gc_type, GcCause gc_cause, bool clear_soft_references) { +collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCause gc_cause, + bool clear_soft_references) { Thread* self = Thread::Current(); + + switch (gc_cause) { + case kGcCauseForAlloc: + ATRACE_BEGIN("GC (alloc)"); + break; + case kGcCauseBackground: + ATRACE_BEGIN("GC (background)"); + break; + case kGcCauseExplicit: + ATRACE_BEGIN("GC (explicit)"); + break; + } + ScopedThreadStateChange tsc(self, kWaitingPerformingGc); Locks::mutator_lock_->AssertNotHeld(self); @@ -1103,31 +1168,37 @@ GcType Heap::CollectGarbageInternal(GcType gc_type, GcCause gc_cause, bool clear // We need to do partial GCs every now and then to avoid the heap growing too much and // fragmenting. - if (gc_type == kGcTypeSticky && ++sticky_gc_count_ > partial_gc_frequency_) { - gc_type = have_zygote_space_ ? kGcTypePartial : kGcTypeFull; - } - if (gc_type != kGcTypeSticky) { + // TODO: if sticky GCs are failing to free memory then we should lower the + // sticky_to_partial_gc_ratio_, if they are successful we can increase it. + if (gc_type == collector::kGcTypeSticky) { + ++sticky_gc_count_; + if (sticky_gc_count_ >= sticky_to_partial_gc_ratio_) { + gc_type = have_zygote_space_ ? collector::kGcTypePartial : collector::kGcTypeFull; + sticky_gc_count_ = 0; + } + } else { sticky_gc_count_ = 0; } - uint64_t gc_start_time = NanoTime(); + uint64_t gc_start_time_ns = NanoTime(); uint64_t gc_start_size = GetBytesAllocated(); // Approximate allocation rate in bytes / second. - if (UNLIKELY(gc_start_time == last_gc_time_)) { + if (UNLIKELY(gc_start_time_ns == last_gc_time_ns_)) { LOG(WARNING) << "Timers are broken (gc_start_time == last_gc_time_)."; } - uint64_t ms_delta = NsToMs(gc_start_time - last_gc_time_); + uint64_t ms_delta = NsToMs(gc_start_time_ns - last_gc_time_ns_); if (ms_delta != 0) { - allocation_rate_ = (gc_start_size - last_gc_size_) * 1000 / ms_delta; + allocation_rate_ = ((gc_start_size - last_gc_size_) * 1000) / ms_delta; VLOG(heap) << "Allocation rate: " << PrettySize(allocation_rate_) << "/s"; } - DCHECK_LT(gc_type, kGcTypeMax); - DCHECK_NE(gc_type, kGcTypeNone); - MarkSweep* collector = NULL; - for (Collectors::iterator it = mark_sweep_collectors_.begin(); - it != mark_sweep_collectors_.end(); ++it) { - MarkSweep* cur_collector = *it; + DCHECK_LT(gc_type, collector::kGcTypeMax); + DCHECK_NE(gc_type, collector::kGcTypeNone); + collector::MarkSweep* collector = NULL; + typedef std::vector<collector::MarkSweep*>::iterator It; + for (It it = mark_sweep_collectors_.begin(), end = mark_sweep_collectors_.end(); + it != end; ++it) { + collector::MarkSweep* cur_collector = *it; if (cur_collector->IsConcurrent() == concurrent_gc_ && cur_collector->GetGcType() == gc_type) { collector = cur_collector; break; @@ -1138,10 +1209,10 @@ GcType Heap::CollectGarbageInternal(GcType gc_type, GcCause gc_cause, bool clear << " and type=" << gc_type; collector->clear_soft_references_ = clear_soft_references; collector->Run(); - total_objects_freed_ += collector->GetFreedObjects(); - total_bytes_freed_ += collector->GetFreedBytes(); + total_objects_freed_ever_ += collector->GetFreedObjects(); + total_bytes_freed_ever_ += collector->GetFreedBytes(); - const size_t duration = collector->GetDuration(); + const size_t duration = collector->GetDurationNs(); std::vector<uint64_t> pauses = collector->GetPauseTimes(); bool was_slow = duration > kSlowGcThreshold || (gc_cause == kGcCauseForAlloc && duration > kLongGcPauseThreshold); @@ -1153,7 +1224,7 @@ GcType Heap::CollectGarbageInternal(GcType gc_type, GcCause gc_cause, bool clear if (was_slow) { const size_t percent_free = GetPercentFree(); - const size_t current_heap_size = GetUsedMemorySize(); + const size_t current_heap_size = GetBytesAllocated(); const size_t total_memory = GetTotalMemory(); std::ostringstream pause_string; for (size_t i = 0; i < pauses.size(); ++i) { @@ -1166,7 +1237,7 @@ GcType Heap::CollectGarbageInternal(GcType gc_type, GcCause gc_cause, bool clear << PrettySize(total_memory) << ", " << "paused " << pause_string.str() << " total " << PrettyDuration((duration / 1000) * 1000); if (VLOG_IS_ON(heap)) { - LOG(INFO) << Dumpable<TimingLogger>(collector->GetTimings()); + LOG(INFO) << Dumpable<base::NewTimingLogger>(collector->GetTimings()); } } @@ -1178,36 +1249,38 @@ GcType Heap::CollectGarbageInternal(GcType gc_type, GcCause gc_cause, bool clear gc_complete_cond_->Broadcast(self); } // Inform DDMS that a GC completed. + ATRACE_END(); Dbg::GcDidFinish(); return gc_type; } -void Heap::UpdateAndMarkModUnion(MarkSweep* mark_sweep, TimingLogger& timings, GcType gc_type) { - if (gc_type == kGcTypeSticky) { +void Heap::UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::NewTimingLogger& timings, + collector::GcType gc_type) { + if (gc_type == collector::kGcTypeSticky) { // Don't need to do anything for mod union table in this case since we are only scanning dirty // cards. return; } // Update zygote mod union table. - if (gc_type == kGcTypePartial) { + if (gc_type == collector::kGcTypePartial) { + timings.NewSplit("UpdateZygoteModUnionTable"); zygote_mod_union_table_->Update(); - timings.AddSplit("UpdateZygoteModUnionTable"); + timings.NewSplit("ZygoteMarkReferences"); zygote_mod_union_table_->MarkReferences(mark_sweep); - timings.AddSplit("ZygoteMarkReferences"); } // Processes the cards we cleared earlier and adds their objects into the mod-union table. - mod_union_table_->Update(); - timings.AddSplit("UpdateModUnionTable"); + timings.NewSplit("UpdateModUnionTable"); + image_mod_union_table_->Update(); // Scans all objects in the mod-union table. - mod_union_table_->MarkReferences(mark_sweep); - timings.AddSplit("MarkImageToAllocSpaceReferences"); + timings.NewSplit("MarkImageToAllocSpaceReferences"); + image_mod_union_table_->MarkReferences(mark_sweep); } -void Heap::RootMatchesObjectVisitor(const mirror::Object* root, void* arg) { +static void RootMatchesObjectVisitor(const mirror::Object* root, void* arg) { mirror::Object* obj = reinterpret_cast<mirror::Object*>(arg); if (root == obj) { LOG(INFO) << "Object " << obj << " is a root"; @@ -1221,94 +1294,109 @@ class ScanVisitor { } }; +// Verify a reference from an object. class VerifyReferenceVisitor { public: - VerifyReferenceVisitor(Heap* heap, bool* failed) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, - Locks::heap_bitmap_lock_) - : heap_(heap), - failed_(failed) { + VerifyReferenceVisitor(Heap* heap) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) + : heap_(heap), failed_(false) { + } + + bool Failed() const { + return failed_; } // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for smarter - // analysis. + // analysis on visitors. void operator ()(const mirror::Object* obj, const mirror::Object* ref, - const MemberOffset& /* offset */, bool /* is_static */) const + const MemberOffset& offset, bool /* is_static */) const NO_THREAD_SAFETY_ANALYSIS { // Verify that the reference is live. - if (ref != NULL && !IsLive(ref)) { - CardTable* card_table = heap_->GetCardTable(); - ObjectStack* alloc_stack = heap_->allocation_stack_.get(); - ObjectStack* live_stack = heap_->live_stack_.get(); - - byte* card_addr = card_table->CardFromAddr(obj); - LOG(ERROR) << "Object " << obj << " references dead object " << ref << "\n" - << "IsDirty = " << (*card_addr == CardTable::kCardDirty) << "\n" - << "Obj type " << PrettyTypeOf(obj) << "\n" - << "Ref type " << PrettyTypeOf(ref); - card_table->CheckAddrIsInCardTable(reinterpret_cast<const byte*>(obj)); - void* cover_begin = card_table->AddrFromCard(card_addr); - void* cover_end = reinterpret_cast<void*>(reinterpret_cast<size_t>(cover_begin) + - CardTable::kCardSize); - LOG(ERROR) << "Card " << reinterpret_cast<void*>(card_addr) << " covers " << cover_begin - << "-" << cover_end; - SpaceBitmap* bitmap = heap_->GetLiveBitmap()->GetSpaceBitmap(obj); - - // Print out how the object is live. - if (bitmap->Test(obj)) { - LOG(ERROR) << "Object " << obj << " found in live bitmap"; - } - if (std::binary_search(alloc_stack->Begin(), alloc_stack->End(), obj)) { - LOG(ERROR) << "Object " << obj << " found in allocation stack"; + if (UNLIKELY(ref != NULL && !IsLive(ref))) { + accounting::CardTable* card_table = heap_->GetCardTable(); + accounting::ObjectStack* alloc_stack = heap_->allocation_stack_.get(); + accounting::ObjectStack* live_stack = heap_->live_stack_.get(); + + if (obj != NULL) { + byte* card_addr = card_table->CardFromAddr(obj); + LOG(ERROR) << "Object " << obj << " references dead object " << ref << " at offset " << offset + << "\nIsDirty = " << (*card_addr == accounting::CardTable::kCardDirty) + << "\nObj type " << PrettyTypeOf(obj) + << "\nRef type " << PrettyTypeOf(ref); + card_table->CheckAddrIsInCardTable(reinterpret_cast<const byte*>(obj)); + void* cover_begin = card_table->AddrFromCard(card_addr); + void* cover_end = reinterpret_cast<void*>(reinterpret_cast<size_t>(cover_begin) + + accounting::CardTable::kCardSize); + LOG(ERROR) << "Card " << reinterpret_cast<void*>(card_addr) << " covers " << cover_begin + << "-" << cover_end; + accounting::SpaceBitmap* bitmap = heap_->GetLiveBitmap()->GetContinuousSpaceBitmap(obj); + + // Print out how the object is live. + if (bitmap != NULL && bitmap->Test(obj)) { + LOG(ERROR) << "Object " << obj << " found in live bitmap"; + } + if (alloc_stack->Contains(const_cast<mirror::Object*>(obj))) { + LOG(ERROR) << "Object " << obj << " found in allocation stack"; + } + if (live_stack->Contains(const_cast<mirror::Object*>(obj))) { + LOG(ERROR) << "Object " << obj << " found in live stack"; + } + // Attempt to see if the card table missed the reference. + ScanVisitor scan_visitor; + byte* byte_cover_begin = reinterpret_cast<byte*>(card_table->AddrFromCard(card_addr)); + card_table->Scan(bitmap, byte_cover_begin, + byte_cover_begin + accounting::CardTable::kCardSize, + scan_visitor, VoidFunctor()); + + // Search to see if any of the roots reference our object. + void* arg = const_cast<void*>(reinterpret_cast<const void*>(obj)); + Runtime::Current()->VisitRoots(&RootMatchesObjectVisitor, arg, false, false); + + // Search to see if any of the roots reference our reference. + arg = const_cast<void*>(reinterpret_cast<const void*>(ref)); + Runtime::Current()->VisitRoots(&RootMatchesObjectVisitor, arg, false, false); + } else { + LOG(ERROR) << "Root references dead object " << ref << "\nRef type " << PrettyTypeOf(ref); } - if (std::binary_search(live_stack->Begin(), live_stack->End(), obj)) { - LOG(ERROR) << "Object " << obj << " found in live stack"; + if (alloc_stack->Contains(const_cast<mirror::Object*>(ref))) { + LOG(ERROR) << "Reference " << ref << " found in allocation stack!"; } - if (std::binary_search(live_stack->Begin(), live_stack->End(), ref)) { + if (live_stack->Contains(const_cast<mirror::Object*>(ref))) { LOG(ERROR) << "Reference " << ref << " found in live stack!"; } - - // Attempt to see if the card table missed the reference. - ScanVisitor scan_visitor; - byte* byte_cover_begin = reinterpret_cast<byte*>(card_table->AddrFromCard(card_addr)); - card_table->Scan(bitmap, byte_cover_begin, byte_cover_begin + CardTable::kCardSize, - scan_visitor, VoidFunctor()); - - // Search to see if any of the roots reference our object. - void* arg = const_cast<void*>(reinterpret_cast<const void*>(obj)); - Runtime::Current()->VisitRoots(&Heap::RootMatchesObjectVisitor, arg); - *failed_ = true; + heap_->image_mod_union_table_->Dump(LOG(ERROR) << "Image mod-union table: "); + heap_->zygote_mod_union_table_->Dump(LOG(ERROR) << "Zygote mod-union table: "); + failed_ = true; } } bool IsLive(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS { - if (heap_->GetLiveBitmap()->Test(obj)) { - return true; - } - ObjectStack* alloc_stack = heap_->allocation_stack_.get(); - // At this point we need to search the allocation since things in the live stack may get swept. - // If the object is not either in the live bitmap or allocation stack, so the object must be - // dead. - return std::binary_search(alloc_stack->Begin(), alloc_stack->End(), obj); + return heap_->IsLiveObjectLocked(obj); + } + + static void VerifyRoots(const mirror::Object* root, void* arg) { + VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg); + (*visitor)(NULL, root, MemberOffset(0), true); } private: - Heap* heap_; - bool* failed_; + Heap* const heap_; + mutable bool failed_; }; +// Verify all references within an object, for use with HeapBitmap::Visit. class VerifyObjectVisitor { public: - VerifyObjectVisitor(Heap* heap) - : heap_(heap), - failed_(false) { - + VerifyObjectVisitor(Heap* heap) : heap_(heap), failed_(false) { } void operator ()(const mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - VerifyReferenceVisitor visitor(heap_, const_cast<bool*>(&failed_)); - MarkSweep::VisitObjectReferences(obj, visitor); + // Note: we are verifying the references in obj but not obj itself, this is because obj must + // be live or else how did we find it in the live bitmap? + VerifyReferenceVisitor visitor(heap_); + collector::MarkSweep::VisitObjectReferences(obj, visitor); + failed_ = failed_ || visitor.Failed(); } bool Failed() const { @@ -1316,18 +1404,19 @@ class VerifyObjectVisitor { } private: - Heap* heap_; - bool failed_; + Heap* const heap_; + mutable bool failed_; }; // Must do this with mutators suspended since we are directly accessing the allocation stacks. bool Heap::VerifyHeapReferences() { Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); // Lets sort our allocation stacks so that we can efficiently binary search them. - std::sort(allocation_stack_->Begin(), allocation_stack_->End()); - std::sort(live_stack_->Begin(), live_stack_->End()); + allocation_stack_->Sort(); + live_stack_->Sort(); // Perform the verification. VerifyObjectVisitor visitor(this); + Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRoots, &visitor, false, false); GetLiveBitmap()->Visit(visitor); // We don't want to verify the objects in the allocation stack since they themselves may be // pointing to dead objects if they are not reachable. @@ -1343,8 +1432,7 @@ class VerifyReferenceCardVisitor { VerifyReferenceCardVisitor(Heap* heap, bool* failed) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) - : heap_(heap), - failed_(failed) { + : heap_(heap), failed_(failed) { } // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for @@ -1354,7 +1442,7 @@ class VerifyReferenceCardVisitor { // Filter out class references since changing an object's class does not mark the card as dirty. // Also handles large objects, since the only reference they hold is a class reference. if (ref != NULL && !ref->IsClass()) { - CardTable* card_table = heap_->GetCardTable(); + accounting::CardTable* card_table = heap_->GetCardTable(); // If the object is not dirty and it is referencing something in the live stack other than // class, then it must be on a dirty card. if (!card_table->AddrIsInCardTable(obj)) { @@ -1363,9 +1451,9 @@ class VerifyReferenceCardVisitor { } else if (!card_table->IsDirty(obj)) { // Card should be either kCardDirty if it got re-dirtied after we aged it, or // kCardDirty - 1 if it didnt get touched since we aged it. - ObjectStack* live_stack = heap_->live_stack_.get(); - if (std::binary_search(live_stack->Begin(), live_stack->End(), ref)) { - if (std::binary_search(live_stack->Begin(), live_stack->End(), obj)) { + accounting::ObjectStack* live_stack = heap_->live_stack_.get(); + if (live_stack->Contains(const_cast<mirror::Object*>(ref))) { + if (live_stack->Contains(const_cast<mirror::Object*>(obj))) { LOG(ERROR) << "Object " << obj << " found in live stack"; } if (heap_->GetLiveBitmap()->Test(obj)) { @@ -1406,8 +1494,8 @@ class VerifyReferenceCardVisitor { } private: - Heap* heap_; - bool* failed_; + Heap* const heap_; + bool* const failed_; }; class VerifyLiveStackReferences { @@ -1421,7 +1509,7 @@ class VerifyLiveStackReferences { void operator ()(const mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { VerifyReferenceCardVisitor visitor(heap_, const_cast<bool*>(&failed_)); - MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(obj, visitor); } bool Failed() const { @@ -1429,7 +1517,7 @@ class VerifyLiveStackReferences { } private: - Heap* heap_; + Heap* const heap_; bool failed_; }; @@ -1437,7 +1525,7 @@ bool Heap::VerifyMissingCardMarks() { Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); // We need to sort the live stack since we binary search it. - std::sort(live_stack_->Begin(), live_stack_->End()); + live_stack_->Sort(); VerifyLiveStackReferences visitor(this); GetLiveBitmap()->Visit(visitor); @@ -1458,30 +1546,31 @@ void Heap::SwapStacks() { // Sort the live stack so that we can quickly binary search it later. if (verify_object_mode_ > kNoHeapVerification) { - std::sort(live_stack_->Begin(), live_stack_->End()); + live_stack_->Sort(); } } -void Heap::ProcessCards(TimingLogger& timings) { - // Clear image space cards and keep track of cards we cleared in the mod-union table. - for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - ContinuousSpace* space = *it; +void Heap::ProcessCards(base::NewTimingLogger& timings) { + // Clear cards and keep track of cards cleared in the mod-union table. + typedef std::vector<space::ContinuousSpace*>::iterator It; + for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { + space::ContinuousSpace* space = *it; if (space->IsImageSpace()) { - mod_union_table_->ClearCards(*it); - timings.AddSplit("ModUnionClearCards"); - } else if (space->GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) { + timings.NewSplit("ModUnionClearCards"); + image_mod_union_table_->ClearCards(space); + } else if (space->IsZygoteSpace()) { + timings.NewSplit("ZygoteModUnionClearCards"); zygote_mod_union_table_->ClearCards(space); - timings.AddSplit("ZygoteModUnionClearCards"); } else { // No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards // were dirty before the GC started. + timings.NewSplit("AllocSpaceClearCards"); card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), VoidFunctor()); - timings.AddSplit("AllocSpaceClearCards"); } } } -void Heap::PreGcVerification(GarbageCollector* gc) { +void Heap::PreGcVerification(collector::GarbageCollector* gc) { ThreadList* thread_list = Runtime::Current()->GetThreadList(); Thread* self = Thread::Current(); @@ -1516,44 +1605,50 @@ void Heap::PreGcVerification(GarbageCollector* gc) { ReaderMutexLock reader_lock(self, *Locks::heap_bitmap_lock_); zygote_mod_union_table_->Update(); zygote_mod_union_table_->Verify(); - mod_union_table_->Update(); - mod_union_table_->Verify(); + image_mod_union_table_->Update(); + image_mod_union_table_->Verify(); thread_list->ResumeAll(); } } -void Heap::PreSweepingGcVerification(GarbageCollector* gc) { +void Heap::PreSweepingGcVerification(collector::GarbageCollector* gc) { ThreadList* thread_list = Runtime::Current()->GetThreadList(); - Thread* self = Thread::Current(); // Called before sweeping occurs since we want to make sure we are not going so reclaim any // reachable objects. if (verify_post_gc_heap_) { + Thread* self = Thread::Current(); + CHECK_NE(self->GetState(), kRunnable); + Locks::mutator_lock_->SharedUnlock(self); thread_list->SuspendAll(); - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - // Swapping bound bitmaps does nothing. - live_bitmap_.swap(mark_bitmap_); - if (!VerifyHeapReferences()) { - LOG(FATAL) << "Post " << gc->GetName() << "Gc verification failed"; + { + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + // Swapping bound bitmaps does nothing. + gc->SwapBitmaps(); + if (!VerifyHeapReferences()) { + LOG(FATAL) << "Post " << gc->GetName() << "GC verification failed"; + } + gc->SwapBitmaps(); } - live_bitmap_.swap(mark_bitmap_); thread_list->ResumeAll(); + Locks::mutator_lock_->SharedLock(self); } } -void Heap::PostGcVerification(GarbageCollector* gc) { +void Heap::PostGcVerification(collector::GarbageCollector* gc) { Thread* self = Thread::Current(); if (verify_system_weaks_) { ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - MarkSweep* mark_sweep = down_cast<MarkSweep*>(gc); + collector::MarkSweep* mark_sweep = down_cast<collector::MarkSweep*>(gc); mark_sweep->VerifySystemWeaks(); } } -GcType Heap::WaitForConcurrentGcToComplete(Thread* self) { - GcType last_gc_type = kGcTypeNone; +collector::GcType Heap::WaitForConcurrentGcToComplete(Thread* self) { + collector::GcType last_gc_type = collector::kGcTypeNone; if (concurrent_gc_) { + ATRACE_BEGIN("GC: Wait For Concurrent"); bool do_wait; uint64_t wait_start = NanoTime(); { @@ -1578,12 +1673,13 @@ GcType Heap::WaitForConcurrentGcToComplete(Thread* self) { LOG(INFO) << "WaitForConcurrentGcToComplete blocked for " << PrettyDuration(wait_time); } } + ATRACE_END(); } return last_gc_type; } void Heap::DumpForSigQuit(std::ostream& os) { - os << "Heap: " << GetPercentFree() << "% free, " << PrettySize(GetUsedMemorySize()) << "/" + os << "Heap: " << GetPercentFree() << "% free, " << PrettySize(GetBytesAllocated()) << "/" << PrettySize(GetTotalMemory()) << "; " << GetObjectsAllocated() << " objects\n"; DumpGcPerformanceInfo(os); } @@ -1606,7 +1702,7 @@ void Heap::GrowForUtilization(uint64_t gc_duration) { // This doesn't actually resize any memory. It just lets the heap grow more when necessary. const size_t bytes_allocated = GetBytesAllocated(); last_gc_size_ = bytes_allocated; - last_gc_time_ = NanoTime(); + last_gc_time_ns_ = NanoTime(); size_t target_size = bytes_allocated / GetTargetHeapUtilization(); if (target_size > bytes_allocated + max_free_) { @@ -1617,20 +1713,23 @@ void Heap::GrowForUtilization(uint64_t gc_duration) { SetIdealFootprint(target_size); - // Calculate when to perform the next ConcurrentGC if we have enough free memory. - if (concurrent_gc_ && GetFreeMemory() >= concurrent_min_free_) { + // Calculate when to perform the next ConcurrentGC. + if (concurrent_gc_) { // Calculate the estimated GC duration. double gc_duration_seconds = NsToMs(gc_duration) / 1000.0; // Estimate how many remaining bytes we will have when we need to start the next GC. size_t remaining_bytes = allocation_rate_ * gc_duration_seconds; - if (remaining_bytes < max_allowed_footprint_) { + remaining_bytes = std::max(remaining_bytes, kMinConcurrentRemainingBytes); + if (UNLIKELY(remaining_bytes > max_allowed_footprint_)) { + // A never going to happen situation that from the estimated allocation rate we will exceed + // the applications entire footprint with the given estimated allocation rate. Schedule + // another GC straight away. + concurrent_start_bytes_ = bytes_allocated; + } else { // Start a concurrent GC when we get close to the estimated remaining bytes. When the // allocation rate is very high, remaining_bytes could tell us that we should start a GC // right away. concurrent_start_bytes_ = std::max(max_allowed_footprint_ - remaining_bytes, bytes_allocated); - } else { - // The estimated rate is so high that we should request another GC straight away. - concurrent_start_bytes_ = bytes_allocated; } DCHECK_LE(concurrent_start_bytes_, max_allowed_footprint_); DCHECK_LE(max_allowed_footprint_, growth_limit_); @@ -1736,30 +1835,6 @@ void Heap::AddFinalizerReference(Thread* self, mirror::Object* object) { arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); } -size_t Heap::GetBytesAllocated() const { - return num_bytes_allocated_; -} - -size_t Heap::GetObjectsAllocated() const { - size_t total = 0; - // TODO: C++0x - for (Spaces::const_iterator it = spaces_.begin(); it != spaces_.end(); ++it) { - Space* space = *it; - if (space->IsAllocSpace()) { - total += space->AsAllocSpace()->GetNumObjectsAllocated(); - } - } - return total; -} - -size_t Heap::GetConcurrentStartSize() const { - return concurrent_start_size_; -} - -size_t Heap::GetConcurrentMinFree() const { - return concurrent_min_free_; -} - void Heap::EnqueueClearedReferences(mirror::Object** cleared) { DCHECK(cleared != NULL); if (*cleared != NULL) { @@ -1811,11 +1886,11 @@ void Heap::ConcurrentGC(Thread* self) { } // Wait for any GCs currently running to finish. - if (WaitForConcurrentGcToComplete(self) == kGcTypeNone) { + if (WaitForConcurrentGcToComplete(self) == collector::kGcTypeNone) { if (alloc_space_->Size() > min_alloc_space_size_for_sticky_gc_) { - CollectGarbageInternal(kGcTypeSticky, kGcCauseBackground, false); + CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseBackground, false); } else { - CollectGarbageInternal(kGcTypePartial, kGcCauseBackground, false); + CollectGarbageInternal(collector::kGcTypePartial, kGcCauseBackground, false); } } } @@ -1835,8 +1910,8 @@ void Heap::RequestHeapTrim() { // not how much use we're making of those pages. uint64_t ms_time = MilliTime(); float utilization = - static_cast<float>(alloc_space_->GetNumBytesAllocated()) / alloc_space_->Size(); - if ((utilization > 0.75f) || ((ms_time - last_trim_time_) < 2 * 1000)) { + static_cast<float>(alloc_space_->GetBytesAllocated()) / alloc_space_->Size(); + if ((utilization > 0.75f) || ((ms_time - last_trim_time_ms_) < 2 * 1000)) { // Don't bother trimming the alloc space if it's more than 75% utilized, or if a // heap trim occurred in the last two seconds. return; @@ -1861,7 +1936,7 @@ void Heap::RequestHeapTrim() { return; } - last_trim_time_ = ms_time; + last_trim_time_ms_ = ms_time; JNIEnv* env = self->GetJniEnv(); DCHECK(WellKnownClasses::java_lang_Daemons != NULL); DCHECK(WellKnownClasses::java_lang_Daemons_requestHeapTrim != NULL); @@ -1875,4 +1950,5 @@ size_t Heap::Trim() { return alloc_space_->Trim(); } +} // namespace gc } // namespace art diff --git a/src/heap.h b/src/gc/heap.h index 642c43689d..d86c7dc7d2 100644 --- a/src/heap.h +++ b/src/gc/heap.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_SRC_HEAP_H_ -#define ART_SRC_HEAP_H_ +#ifndef ART_SRC_GC_HEAP_H_ +#define ART_SRC_GC_HEAP_H_ #include <iosfwd> #include <string> @@ -23,10 +23,9 @@ #include "atomic_integer.h" #include "base/timing_logger.h" -#include "gc/atomic_stack.h" -#include "gc/card_table.h" -#include "gc/gc_type.h" -#include "gc/heap_bitmap.h" +#include "gc/accounting/atomic_stack.h" +#include "gc/accounting/card_table.h" +#include "gc/collector/gc_type.h" #include "globals.h" #include "gtest/gtest.h" #include "locks.h" @@ -35,32 +34,44 @@ #include "thread_pool.h" namespace art { -namespace mirror { -class Class; -class Object; -} // namespace mirror -class AllocSpace; + class ConditionVariable; -class DlMallocSpace; -class GarbageCollector; -class HeapBitmap; -class ImageSpace; -class LargeObjectSpace; -class MarkSweep; -class ModUnionTable; class Mutex; -class Space; -class SpaceTest; class StackVisitor; class Thread; class TimingLogger; -typedef std::vector<ContinuousSpace*> Spaces; +namespace mirror { + class Class; + class Object; +} // namespace mirror + +namespace gc { +namespace accounting { + class HeapBitmap; + class ModUnionTable; + class SpaceSetMap; +} // namespace accounting + +namespace collector { + class GarbageCollector; + class MarkSweep; +} // namespace collector + +namespace space { + class AllocSpace; + class DiscontinuousSpace; + class DlMallocSpace; + class ImageSpace; + class LargeObjectSpace; + class Space; + class SpaceTest; +} // namespace space class AgeCardVisitor { public: byte operator ()(byte card) const { - if (card == CardTable::kCardDirty) { + if (card == accounting::CardTable::kCardDirty) { return card - 1; } else { return 0; @@ -68,9 +79,14 @@ class AgeCardVisitor { } }; +// What caused the GC? enum GcCause { + // GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before + // retrying allocation. kGcCauseForAlloc, + // A background GC trying to ensure there is free memory ahead of allocations. kGcCauseBackground, + // An explicit System.gc() call. kGcCauseExplicit, }; std::ostream& operator<<(std::ostream& os, const GcCause& policy); @@ -120,10 +136,8 @@ class Heap { // Check sanity of all live references. void VerifyHeap() LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); - static void RootMatchesObjectVisitor(const mirror::Object* root, void* arg); bool VerifyHeapReferences() - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); bool VerifyMissingCardMarks() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -139,20 +153,12 @@ class Heap { SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // Initiates an explicit garbage collection. - void CollectGarbage(bool clear_soft_references) - LOCKS_EXCLUDED(Locks::mutator_lock_); + void CollectGarbage(bool clear_soft_references) LOCKS_EXCLUDED(Locks::mutator_lock_); // Does a concurrent GC, should only be called by the GC daemon thread // through runtime. void ConcurrentGC(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_); - // Implements java.lang.Runtime.maxMemory. - int64_t GetMaxMemory() const; - // Implements java.lang.Runtime.totalMemory. - int64_t GetTotalMemory() const; - // Implements java.lang.Runtime.freeMemory. - int64_t GetFreeMemory() const; - // Implements VMDebug.countInstancesOfClass and JDWP VM_InstanceCount. // The boolean decides whether to use IsAssignableFrom or == when comparing classes. void CountInstances(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from, @@ -188,14 +194,14 @@ class Heap { // Blocks the caller until the garbage collector becomes idle and returns // true if we waited for the GC to complete. - GcType WaitForConcurrentGcToComplete(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_); + collector::GcType WaitForConcurrentGcToComplete(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_); - const Spaces& GetSpaces() const { - return spaces_; + const std::vector<space::ContinuousSpace*>& GetContinuousSpaces() const { + return continuous_spaces_; } - Spaces& GetSpaces() { - return spaces_; + const std::vector<space::DiscontinuousSpace*>& GetDiscontinuousSpaces() const { + return discontinuous_spaces_; } void SetReferenceOffsets(MemberOffset reference_referent_offset, @@ -257,47 +263,78 @@ class Heap { card_table_->MarkCard(dst); } - CardTable* GetCardTable() { + accounting::CardTable* GetCardTable() const { return card_table_.get(); } void AddFinalizerReference(Thread* self, mirror::Object* object); - size_t GetBytesAllocated() const; + // Returns the number of bytes currently allocated. + size_t GetBytesAllocated() const { + return num_bytes_allocated_; + } + + // Returns the number of objects currently allocated. size_t GetObjectsAllocated() const; - size_t GetConcurrentStartSize() const; - size_t GetConcurrentMinFree() const; - size_t GetUsedMemorySize() const; // Returns the total number of objects allocated since the heap was created. - size_t GetTotalObjectsAllocated() const; + size_t GetObjectsAllocatedEver() const; // Returns the total number of bytes allocated since the heap was created. - size_t GetTotalBytesAllocated() const; + size_t GetBytesAllocatedEver() const; // Returns the total number of objects freed since the heap was created. - size_t GetTotalObjectsFreed() const; + size_t GetObjectsFreedEver() const { + return total_objects_freed_ever_; + } // Returns the total number of bytes freed since the heap was created. - size_t GetTotalBytesFreed() const; + size_t GetBytesFreedEver() const { + return total_bytes_freed_ever_; + } + + // Implements java.lang.Runtime.maxMemory, returning the maximum amount of memory a program can + // consume. For a regular VM this would relate to the -Xmx option and would return -1 if no Xmx + // were specified. Android apps start with a growth limit (small heap size) which is + // cleared/extended for large apps. + int64_t GetMaxMemory() const { + return growth_limit_; + } + + // Implements java.lang.Runtime.totalMemory, returning the amount of memory consumed by an + // application. + int64_t GetTotalMemory() const { + // TODO: we use the footprint limit here which is conservative wrt number of pages really used. + // We could implement a more accurate count across all spaces. + return max_allowed_footprint_; + } - // Functions for getting the bitmap which corresponds to an object's address. - // This is probably slow, TODO: use better data structure like binary tree . - ContinuousSpace* FindSpaceFromObject(const mirror::Object*) const; + // Implements java.lang.Runtime.freeMemory. + int64_t GetFreeMemory() const { + return GetTotalMemory() - num_bytes_allocated_; + } + + // Get the space that corresponds to an object's address. Current implementation searches all + // spaces in turn. If fail_ok is false then failing to find a space will cause an abort. + // TODO: consider using faster data structure like binary tree. + space::ContinuousSpace* FindContinuousSpaceFromObject(const mirror::Object*, bool fail_ok) const; + space::DiscontinuousSpace* FindDiscontinuousSpaceFromObject(const mirror::Object*, + bool fail_ok) const; + space::Space* FindSpaceFromObject(const mirror::Object*, bool fail_ok) const; void DumpForSigQuit(std::ostream& os); size_t Trim(); - HeapBitmap* GetLiveBitmap() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + accounting::HeapBitmap* GetLiveBitmap() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { return live_bitmap_.get(); } - HeapBitmap* GetMarkBitmap() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + accounting::HeapBitmap* GetMarkBitmap() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { return mark_bitmap_.get(); } - ObjectStack* GetLiveStack() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + accounting::ObjectStack* GetLiveStack() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { return live_stack_.get(); } @@ -308,24 +345,32 @@ class Heap { EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // Mark all the objects in the allocation stack in the specified bitmap. - void MarkAllocStack(SpaceBitmap* bitmap, SpaceSetMap* large_objects, ObjectStack* stack) + void MarkAllocStack(accounting::SpaceBitmap* bitmap, accounting::SpaceSetMap* large_objects, + accounting::ObjectStack* stack) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // Unmark all the objects in the allocation stack in the specified bitmap. - void UnMarkAllocStack(SpaceBitmap* bitmap, SpaceSetMap* large_objects, ObjectStack* stack) + void UnMarkAllocStack(accounting::SpaceBitmap* bitmap, accounting::SpaceSetMap* large_objects, + accounting::ObjectStack* stack) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // Update and mark mod union table based on gc type. - void UpdateAndMarkModUnion(MarkSweep* mark_sweep, TimingLogger& timings, GcType gc_type) + void UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::NewTimingLogger& timings, + collector::GcType gc_type) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // DEPRECATED: Should remove in "near" future when support for multiple image spaces is added. // Assumes there is only one image space. - ImageSpace* GetImageSpace(); - DlMallocSpace* GetAllocSpace(); - LargeObjectSpace* GetLargeObjectsSpace() { - return large_object_space_.get(); + space::ImageSpace* GetImageSpace() const; + + space::DlMallocSpace* GetAllocSpace() const { + return alloc_space_; } + + space::LargeObjectSpace* GetLargeObjectsSpace() const { + return large_object_space_; + } + void DumpSpaces(); // UnReserve the address range where the oat file will be placed. @@ -344,12 +389,12 @@ class Heap { private: // Allocates uninitialized storage. Passing in a null space tries to place the object in the // large object space. - mirror::Object* Allocate(Thread* self, AllocSpace* space, size_t num_bytes) + mirror::Object* Allocate(Thread* self, space::AllocSpace* space, size_t num_bytes) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Try to allocate a number of bytes, this function never does any GCs. - mirror::Object* TryToAllocate(Thread* self, AllocSpace* space, size_t alloc_size, bool grow) + mirror::Object* TryToAllocate(Thread* self, space::AllocSpace* space, size_t alloc_size, bool grow) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -365,14 +410,16 @@ class Heap { // Sometimes CollectGarbageInternal decides to run a different Gc than you requested. Returns // which type of Gc was actually ran. - GcType CollectGarbageInternal(GcType gc_plan, GcCause gc_cause, bool clear_soft_references) + collector::GcType CollectGarbageInternal(collector::GcType gc_plan, GcCause gc_cause, + bool clear_soft_references) LOCKS_EXCLUDED(gc_complete_lock_, Locks::heap_bitmap_lock_, Locks::thread_suspend_count_lock_); - void PreGcVerification(GarbageCollector* gc); - void PreSweepingGcVerification(GarbageCollector* gc); - void PostGcVerification(GarbageCollector* gc); + void PreGcVerification(collector::GarbageCollector* gc); + void PreSweepingGcVerification(collector::GarbageCollector* gc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void PostGcVerification(collector::GarbageCollector* gc); // Given the current contents of the alloc space, increase the allowed heap footprint to match // the target utilization ratio. This should only be called immediately after a full garbage @@ -381,7 +428,9 @@ class Heap { size_t GetPercentFree(); - void AddSpace(ContinuousSpace* space) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); + void AddContinuousSpace(space::ContinuousSpace* space) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); + void AddDiscontinuousSpace(space::DiscontinuousSpace* space) + LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); // No thread saftey analysis since we call this everywhere and it is impossible to find a proper // lock ordering for it. @@ -394,26 +443,32 @@ class Heap { void SwapStacks(); // Clear cards and update the mod union table. - void ProcessCards(TimingLogger& timings); + void ProcessCards(base::NewTimingLogger& timings); - Spaces spaces_; + // All-known continuous spaces, where objects lie within fixed bounds. + std::vector<space::ContinuousSpace*> continuous_spaces_; - // A map that we use to temporarily reserve address range for the oat file. - UniquePtr<MemMap> oat_file_map_; + // All-known discontinuous spaces, where objects may be placed throughout virtual memory. + std::vector<space::DiscontinuousSpace*> discontinuous_spaces_; + + // The allocation space we are currently allocating into. + space::DlMallocSpace* alloc_space_; + + // The large object space we are currently allocating into. + space::LargeObjectSpace* large_object_space_; - // The alloc space which we are currently allocating into. - DlMallocSpace* alloc_space_; + // The card table, dirtied by the write barrier. + UniquePtr<accounting::CardTable> card_table_; // The mod-union table remembers all of the references from the image space to the alloc / - // zygote spaces. - UniquePtr<ModUnionTable> mod_union_table_; + // zygote spaces to allow the card table to be cleared. + UniquePtr<accounting::ModUnionTable> image_mod_union_table_; // This table holds all of the references from the zygote space to the alloc space. - UniquePtr<ModUnionTable> zygote_mod_union_table_; + UniquePtr<accounting::ModUnionTable> zygote_mod_union_table_; - UniquePtr<CardTable> card_table_; - - // True for concurrent mark sweep GC, false for mark sweep. + // What kind of concurrency behavior is the runtime after? True for concurrent mark sweep GC, + // false for stop-the-world mark sweep. const bool concurrent_gc_; // If we have a zygote space. @@ -424,40 +479,43 @@ class Heap { Mutex* gc_complete_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; UniquePtr<ConditionVariable> gc_complete_cond_ GUARDED_BY(gc_complete_lock_); - // Reference queue lock - UniquePtr<Mutex> reference_queue_lock_; + // Mutex held when adding references to reference queues. + // TODO: move to a UniquePtr, currently annotalysis is confused that UniquePtr isn't lockable. + Mutex* reference_queue_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; // True while the garbage collector is running. volatile bool is_gc_running_ GUARDED_BY(gc_complete_lock_); // Last Gc type we ran. Used by WaitForConcurrentGc to know which Gc was waited on. - volatile GcType last_gc_type_ GUARDED_BY(gc_complete_lock_); - - // If enabled, causes Gc for alloc when heap size reaches the current footprint limit before the - // Gc updates it. - const bool enforce_heap_growth_rate_; + volatile collector::GcType last_gc_type_ GUARDED_BY(gc_complete_lock_); // Maximum size that the heap can reach. - size_t capacity_; + const size_t capacity_; + // The size the heap is limited to. This is initially smaller than capacity, but for largeHeap + // programs it is "cleared" making it the same as capacity. size_t growth_limit_; + // When the number of bytes allocated exceeds the footprint TryAllocate returns NULL indicating + // a GC should be triggered. size_t max_allowed_footprint_; - // Minimum bytes before concurrent GC starts. - size_t concurrent_start_size_; - size_t concurrent_min_free_; + // When num_bytes_allocated_ exceeds this amount then a concurrent GC should be requested so that + // it completes ahead of an allocation failing. size_t concurrent_start_bytes_; - // Number of bytes allocated since the last Gc, we use this to help determine when to schedule concurrent GCs. + // Number of back-to-back sticky mark sweep collections. size_t sticky_gc_count_; - size_t total_bytes_freed_; - size_t total_objects_freed_; + // After how many sticky GCs we force to do a partial GC instead of sticky mark bits GC. + const size_t sticky_to_partial_gc_ratio_; - // Primitive objects larger than this size are put in the large object space. - size_t large_object_threshold_; + // Since the heap was created, how many bytes have been freed. + size_t total_bytes_freed_ever_; - // Large object space. - UniquePtr<LargeObjectSpace> large_object_space_; + // Since the heap was created, how many objects have been freed. + size_t total_objects_freed_ever_; + + // Primitive objects larger than this size are put in the large object space. + const size_t large_object_threshold_; // Number of bytes allocated. Adjusted after each allocation and free. AtomicInteger num_bytes_allocated_; @@ -472,9 +530,6 @@ class Heap { // Parallel GC data structures. UniquePtr<ThreadPool> thread_pool_; - // After how many GCs we force to do a partial GC instead of sticky mark bits GC. - const size_t partial_gc_frequency_; - // Sticky mark bits GC has some overhead, so if we have less a few megabytes of AllocSpace then // it's probably better to just do a partial GC. const size_t min_alloc_space_size_for_sticky_gc_; @@ -483,31 +538,34 @@ class Heap { // normal GC, it is important to not use it when we are almost out of memory. const size_t min_remaining_space_for_sticky_gc_; - // Last trim time - uint64_t last_trim_time_; + // The last time a heap trim occurred. + uint64_t last_trim_time_ms_; - // The time at which the last GC ended. - uint64_t last_gc_time_; + // The nanosecond time at which the last GC ended. + uint64_t last_gc_time_ns_; // How many bytes were allocated at the end of the last GC. uint64_t last_gc_size_; - // Estimated allocation rate (bytes / second). + // Estimated allocation rate (bytes / second). Computed between the time of the last GC cycle + // and the start of the current one. uint64_t allocation_rate_; - UniquePtr<HeapBitmap> live_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_); - UniquePtr<HeapBitmap> mark_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_); + // For a GC cycle, a bitmap that is set corresponding to the + UniquePtr<accounting::HeapBitmap> live_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_); + UniquePtr<accounting::HeapBitmap> mark_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_); // Mark stack that we reuse to avoid re-allocating the mark stack. - UniquePtr<ObjectStack> mark_stack_; + UniquePtr<accounting::ObjectStack> mark_stack_; // Allocation stack, new allocations go here so that we can do sticky mark bits. This enables us // to use the live bitmap as the old mark bitmap. const size_t max_allocation_stack_size_; - UniquePtr<ObjectStack> allocation_stack_; + bool is_allocation_stack_sorted_; + UniquePtr<accounting::ObjectStack> allocation_stack_; // Second allocation stack so that we can process allocation with the heap unlocked. - UniquePtr<ObjectStack> live_stack_; + UniquePtr<accounting::ObjectStack> live_stack_; // offset of java.lang.ref.Reference.referent MemberOffset reference_referent_offset_; @@ -544,22 +602,22 @@ class Heap { // The current state of heap verification, may be enabled or disabled. HeapVerificationMode verify_object_mode_; - typedef std::vector<MarkSweep*> Collectors; - Collectors mark_sweep_collectors_; + std::vector<collector::MarkSweep*> mark_sweep_collectors_; + + // A map that we use to temporarily reserve address range for the oat file. + UniquePtr<MemMap> oat_file_map_; - friend class MarkSweep; + friend class collector::MarkSweep; friend class VerifyReferenceCardVisitor; friend class VerifyReferenceVisitor; friend class VerifyObjectVisitor; friend class ScopedHeapLock; - FRIEND_TEST(SpaceTest, AllocAndFree); - FRIEND_TEST(SpaceTest, AllocAndFreeList); - FRIEND_TEST(SpaceTest, ZygoteSpace); - friend class SpaceTest; + friend class space::SpaceTest; DISALLOW_IMPLICIT_CONSTRUCTORS(Heap); }; +} // namespace gc } // namespace art -#endif // ART_SRC_HEAP_H_ +#endif // ART_SRC_GC_HEAP_H_ diff --git a/src/gc/heap_bitmap.cc b/src/gc/heap_bitmap.cc deleted file mode 100644 index cef6884374..0000000000 --- a/src/gc/heap_bitmap.cc +++ /dev/null @@ -1,49 +0,0 @@ -#include "heap_bitmap.h" -#include "space.h" - -namespace art { - -void HeapBitmap::ReplaceBitmap(SpaceBitmap* old_bitmap, SpaceBitmap* new_bitmap) { - // TODO: C++0x auto - for (Bitmaps::iterator it = bitmaps_.begin(); it != bitmaps_.end(); ++it) { - if (*it == old_bitmap) { - *it = new_bitmap; - return; - } - } - LOG(FATAL) << "bitmap " << static_cast<const void*>(old_bitmap) << " not found"; -} - -void HeapBitmap::AddSpaceBitmap(SpaceBitmap* bitmap) { - DCHECK(bitmap != NULL); - - // Check for interval overlap. - for (Bitmaps::const_iterator it = bitmaps_.begin(); it != bitmaps_.end(); ++it) { - SpaceBitmap* cur_bitmap = *it; - if (bitmap->HeapBegin() < cur_bitmap->HeapLimit() && - bitmap->HeapLimit() > cur_bitmap->HeapBegin()) { - LOG(FATAL) << "Overlapping space bitmaps added to heap bitmap!"; - } - } - bitmaps_.push_back(bitmap); -} - -void HeapBitmap::SetLargeObjects(SpaceSetMap* large_objects) { - DCHECK(large_objects != NULL); - large_objects_ = large_objects; -} - -HeapBitmap::HeapBitmap(Heap* heap) : heap_(heap), large_objects_(NULL) { - -} - -void HeapBitmap::Walk(SpaceBitmap::Callback* callback, void* arg) { - // TODO: C++0x auto - for (Bitmaps::iterator it = bitmaps_.begin(); it != bitmaps_.end(); ++it) { - (*it)->Walk(callback, arg); - } - - large_objects_->Walk(callback, arg); -} - -} // namespace art diff --git a/src/gc/heap_bitmap.h b/src/gc/heap_bitmap.h deleted file mode 100644 index 87e08483f5..0000000000 --- a/src/gc/heap_bitmap.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_SRC_GC_HEAP_BITMAP_H_ -#define ART_SRC_GC_HEAP_BITMAP_H_ - -#include "locks.h" -#include "space_bitmap.h" - -namespace art { -class Heap; - -class HeapBitmap { - public: - bool Test(const mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { - SpaceBitmap* bitmap = GetSpaceBitmap(obj); - if (LIKELY(bitmap != NULL)) { - return bitmap->Test(obj); - } else { - return large_objects_->Test(obj); - } - } - - void Clear(const mirror::Object* obj) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { - SpaceBitmap* bitmap = GetSpaceBitmap(obj); - if (LIKELY(bitmap != NULL)) { - bitmap->Clear(obj); - } else { - large_objects_->Clear(obj); - } - } - - void Set(const mirror::Object* obj) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { - SpaceBitmap* bitmap = GetSpaceBitmap(obj); - if (LIKELY(bitmap != NULL)) { - bitmap->Set(obj); - } else { - large_objects_->Set(obj); - } - } - - SpaceBitmap* GetSpaceBitmap(const mirror::Object* obj) { - // TODO: C++0x auto - for (Bitmaps::iterator it = bitmaps_.begin(); it != bitmaps_.end(); ++it) { - if ((*it)->HasAddress(obj)) { - return *it; - } - } - return NULL; - } - - void Walk(SpaceBitmap::Callback* callback, void* arg) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - - template <typename Visitor> - void Visit(const Visitor& visitor) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Find and replace a bitmap pointer, this is used by for the bitmap swapping in the GC. - void ReplaceBitmap(SpaceBitmap* old_bitmap, SpaceBitmap* new_bitmap) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - - HeapBitmap(Heap* heap); - - inline SpaceSetMap* GetLargeObjects() const { - return large_objects_; - } - - void SetLargeObjects(SpaceSetMap* large_objects); - - private: - - const Heap* const heap_; - - void AddSpaceBitmap(SpaceBitmap* bitmap); - - typedef std::vector<SpaceBitmap*> Bitmaps; - Bitmaps bitmaps_; - - // Large object sets. - SpaceSetMap* large_objects_; - - friend class Heap; -}; - -} // namespace art - -#endif // ART_SRC_GC_HEAP_BITMAP_H_ diff --git a/src/heap_test.cc b/src/gc/heap_test.cc index 8bed7e3175..02708e8341 100644 --- a/src/heap_test.cc +++ b/src/gc/heap_test.cc @@ -15,14 +15,15 @@ */ #include "common_test.h" -#include "gc/card_table-inl.h" -#include "gc/space_bitmap-inl.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/space_bitmap-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "sirt_ref.h" namespace art { +namespace gc { class HeapTest : public CommonTest {}; @@ -56,9 +57,15 @@ TEST_F(HeapTest, GarbageCollectClassLinkerInit) { TEST_F(HeapTest, HeapBitmapCapacityTest) { byte* heap_begin = reinterpret_cast<byte*>(0x1000); - const size_t heap_capacity = SpaceBitmap::kAlignment * (sizeof(intptr_t) * 8 + 1); - UniquePtr<SpaceBitmap> bitmap(SpaceBitmap::Create("test bitmap", heap_begin, heap_capacity)); - bitmap->Set(reinterpret_cast<const mirror::Object*>(&heap_begin[heap_capacity - SpaceBitmap::kAlignment])); + const size_t heap_capacity = accounting::SpaceBitmap::kAlignment * (sizeof(intptr_t) * 8 + 1); + UniquePtr<accounting::SpaceBitmap> bitmap(accounting::SpaceBitmap::Create("test bitmap", + heap_begin, + heap_capacity)); + mirror::Object* fake_end_of_heap_object = + reinterpret_cast<mirror::Object*>(&heap_begin[heap_capacity - + accounting::SpaceBitmap::kAlignment]); + bitmap->Set(fake_end_of_heap_object); } +} // namespace gc } // namespace art diff --git a/src/gc/mod_union_table-inl.h b/src/gc/mod_union_table-inl.h deleted file mode 100644 index c1c69fb379..0000000000 --- a/src/gc/mod_union_table-inl.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_SRC_GC_MOD_UNION_TABLE_INL_H_ -#define ART_SRC_GC_MOD_UNION_TABLE_INL_H_ - -#include "mod_union_table.h" - -namespace art { - -template <typename Implementation> -class ModUnionTableToZygoteAllocspace : public Implementation { -public: - ModUnionTableToZygoteAllocspace(Heap* heap) : Implementation(heap) { - } - - bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) { - const Spaces& spaces = Implementation::GetHeap()->GetSpaces(); - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - if ((*it)->Contains(ref)) { - return (*it)->IsAllocSpace(); - } - } - // Assume it points to a large object. - // TODO: Check. - return true; - } -}; - -template <typename Implementation> -class ModUnionTableToAllocspace : public Implementation { -public: - ModUnionTableToAllocspace(Heap* heap) : Implementation(heap) { - } - - bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) { - const Spaces& spaces = Implementation::GetHeap()->GetSpaces(); - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - if ((*it)->Contains(ref)) { - return (*it)->GetGcRetentionPolicy() == kGcRetentionPolicyAlwaysCollect; - } - } - // Assume it points to a large object. - // TODO: Check. - return true; - } -}; - -} // namespace art - -#endif // ART_SRC_GC_MOD_UNION_TABLE_INL_H_ diff --git a/src/gc/space.h b/src/gc/space.h deleted file mode 100644 index d2bcd53725..0000000000 --- a/src/gc/space.h +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_SRC_GC_SPACE_H_ -#define ART_SRC_GC_SPACE_H_ - -#include <string> - -#include "UniquePtr.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "globals.h" -#include "image.h" -#include "dlmalloc.h" -#include "mem_map.h" - -namespace art { - -static const bool kDebugSpaces = kIsDebugBuild; - -namespace mirror { -class Object; -} // namespace mirror -class DlMallocSpace; -class ImageSpace; -class LargeObjectSpace; -class SpaceBitmap; - -enum GcRetentionPolicy { - kGcRetentionPolicyNeverCollect, - kGcRetentionPolicyAlwaysCollect, - kGcRetentionPolicyFullCollect, // Collect only for full GC -}; -std::ostream& operator<<(std::ostream& os, const GcRetentionPolicy& policy); - -enum SpaceType { - kSpaceTypeImageSpace, - kSpaceTypeAllocSpace, - kSpaceTypeZygoteSpace, - kSpaceTypeLargeObjectSpace, -}; -std::ostream& operator<<(std::ostream& os, const SpaceType& space_type); - -// A space contains memory allocated for managed objects. -class Space { - public: - virtual bool CanAllocateInto() const = 0; - virtual bool IsCompactible() const = 0; - virtual bool Contains(const mirror::Object* obj) const = 0; - virtual SpaceType GetType() const = 0; - virtual GcRetentionPolicy GetGcRetentionPolicy() const = 0; - virtual std::string GetName() const = 0; - - ImageSpace* AsImageSpace(); - DlMallocSpace* AsAllocSpace(); - DlMallocSpace* AsZygoteSpace(); - LargeObjectSpace* AsLargeObjectSpace(); - - bool IsImageSpace() const { - return GetType() == kSpaceTypeImageSpace; - } - - bool IsAllocSpace() const { - return GetType() == kSpaceTypeAllocSpace || GetType() == kSpaceTypeZygoteSpace; - } - - bool IsZygoteSpace() const { - return GetType() == kSpaceTypeZygoteSpace; - } - - bool IsLargeObjectSpace() const { - return GetType() == kSpaceTypeLargeObjectSpace; - } - - virtual void Dump(std::ostream& /* os */) const { } - - virtual ~Space() {} - - protected: - Space() { } - - private: - DISALLOW_COPY_AND_ASSIGN(Space); -}; - -// AllocSpace interface. -class AllocSpace { - public: - virtual bool CanAllocateInto() const { - return true; - } - - // General statistics - virtual uint64_t GetNumBytesAllocated() const = 0; - virtual uint64_t GetNumObjectsAllocated() const = 0; - virtual uint64_t GetTotalBytesAllocated() const = 0; - virtual uint64_t GetTotalObjectsAllocated() const = 0; - - // Allocate num_bytes without allowing growth. - virtual mirror::Object* Alloc(Thread* self, size_t num_bytes) = 0; - - // Return the storage space required by obj. - virtual size_t AllocationSize(const mirror::Object* obj) = 0; - - // Returns how many bytes were freed. - virtual size_t Free(Thread* self, mirror::Object* ptr) = 0; - - // Returns how many bytes were freed. - virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) = 0; - - protected: - AllocSpace() {} - virtual ~AllocSpace() {} - - private: - DISALLOW_COPY_AND_ASSIGN(AllocSpace); -}; - -// Continuous spaces have bitmaps, and an address range. -class ContinuousSpace : public Space { - public: - // Address at which the space begins - byte* Begin() const { - return begin_; - } - - // Address at which the space ends, which may vary as the space is filled. - byte* End() const { - return end_; - } - - // Current size of space - size_t Size() const { - return End() - Begin(); - } - - virtual SpaceBitmap* GetLiveBitmap() const = 0; - virtual SpaceBitmap* GetMarkBitmap() const = 0; - - // Is object within this space? - bool HasAddress(const mirror::Object* obj) const { - const byte* byte_ptr = reinterpret_cast<const byte*>(obj); - return Begin() <= byte_ptr && byte_ptr < End(); - } - - virtual bool Contains(const mirror::Object* obj) const { - return HasAddress(obj); - } - - virtual ~ContinuousSpace() {} - - virtual std::string GetName() const { - return name_; - } - - virtual GcRetentionPolicy GetGcRetentionPolicy() const { - return gc_retention_policy_; - } - - protected: - ContinuousSpace(const std::string& name, byte* begin, byte* end, - GcRetentionPolicy gc_retention_policy); - - std::string name_; - GcRetentionPolicy gc_retention_policy_; - - // The beginning of the storage for fast access. - byte* begin_; - - // Current end of the space. - byte* end_; - - private: - DISALLOW_COPY_AND_ASSIGN(ContinuousSpace); -}; - -class DiscontinuousSpace : public virtual Space { - public: - // Is object within this space? - virtual bool Contains(const mirror::Object* obj) const = 0; - - virtual std::string GetName() const { - return name_; - } - - virtual GcRetentionPolicy GetGcRetentionPolicy() const { - return gc_retention_policy_; - } - -protected: - DiscontinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy); - -private: - std::string name_; - GcRetentionPolicy gc_retention_policy_; - - DISALLOW_COPY_AND_ASSIGN(DiscontinuousSpace); -}; - -std::ostream& operator<<(std::ostream& os, const Space& space); - -class MemMapSpace : public ContinuousSpace { - public: - // Maximum which the mapped space can grow to. - virtual size_t Capacity() const { - return mem_map_->Size(); - } - - // Size of the space without a limit on its growth. By default this is just the Capacity, but - // for the allocation space we support starting with a small heap and then extending it. - virtual size_t NonGrowthLimitCapacity() const { - return Capacity(); - } - - protected: - MemMapSpace(const std::string& name, MemMap* mem_map, size_t initial_size, - GcRetentionPolicy gc_retention_policy); - - MemMap* GetMemMap() { - return mem_map_.get(); - } - - const MemMap* GetMemMap() const { - return mem_map_.get(); - } - - private: - // Underlying storage of the space - UniquePtr<MemMap> mem_map_; - - DISALLOW_COPY_AND_ASSIGN(MemMapSpace); -}; - -// An alloc space is a space where objects may be allocated and garbage collected. -class DlMallocSpace : public MemMapSpace, public AllocSpace { - public: - typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg); - - virtual bool CanAllocateInto() const { - return true; - } - - virtual bool IsCompactible() const { - return false; - } - - virtual SpaceType GetType() const { - return kSpaceTypeAllocSpace; - } - - // Create a AllocSpace with the requested sizes. The requested - // base address is not guaranteed to be granted, if it is required, - // the caller should call Begin on the returned space to confirm - // the request was granted. - static DlMallocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit, - size_t capacity, byte* requested_begin); - - // Allocate num_bytes without allowing the underlying mspace to grow. - virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes); - - // Allocate num_bytes allowing the underlying mspace to grow. - virtual mirror::Object* Alloc(Thread* self, size_t num_bytes); - - // Return the storage space required by obj. - virtual size_t AllocationSize(const mirror::Object* obj); - virtual size_t Free(Thread* self, mirror::Object* ptr); - virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs); - - void* MoreCore(intptr_t increment); - - void* GetMspace() const { - return mspace_; - } - - // Hands unused pages back to the system. - size_t Trim(); - - // Perform a mspace_inspect_all which calls back for each allocation chunk. The chunk may not be - // in use, indicated by num_bytes equaling zero. - void Walk(WalkCallback callback, void* arg); - - // Returns the number of bytes that the heap is allowed to obtain from the system via MoreCore. - size_t GetFootprintLimit(); - - // Set the maximum number of bytes that the heap is allowed to obtain from the system via - // MoreCore. Note this is used to stop the mspace growing beyond the limit to Capacity. When - // allocations fail we GC before increasing the footprint limit and allowing the mspace to grow. - void SetFootprintLimit(size_t limit); - - // Removes the fork time growth limit on capacity, allowing the application to allocate up to the - // maximum reserved size of the heap. - void ClearGrowthLimit() { - growth_limit_ = NonGrowthLimitCapacity(); - } - - // Override capacity so that we only return the possibly limited capacity - virtual size_t Capacity() const { - return growth_limit_; - } - - // The total amount of memory reserved for the alloc space - virtual size_t NonGrowthLimitCapacity() const { - return GetMemMap()->Size(); - } - - virtual SpaceBitmap* GetLiveBitmap() const { - return live_bitmap_.get(); - } - - virtual SpaceBitmap* GetMarkBitmap() const { - return mark_bitmap_.get(); - } - - virtual void Dump(std::ostream& os) const; - - void SetGrowthLimit(size_t growth_limit); - - // Swap the live and mark bitmaps of this space. This is used by the GC for concurrent sweeping. - virtual void SwapBitmaps(); - - // Turn ourself into a zygote space and return a new alloc space which has our unused memory. - DlMallocSpace* CreateZygoteSpace(); - - void SetGcRetentionPolicy(GcRetentionPolicy gc_retention_policy) { - gc_retention_policy_ = gc_retention_policy; - } - - virtual uint64_t GetNumBytesAllocated() const { - return num_bytes_allocated_; - } - - virtual uint64_t GetNumObjectsAllocated() const { - return num_objects_allocated_; - } - - virtual uint64_t GetTotalBytesAllocated() const { - return total_bytes_allocated_; - } - - virtual uint64_t GetTotalObjectsAllocated() const { - return total_objects_allocated_; - } - - private: - size_t InternalAllocationSize(const mirror::Object* obj); - mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes) EXCLUSIVE_LOCKS_REQUIRED(lock_); - - UniquePtr<SpaceBitmap> live_bitmap_; - UniquePtr<SpaceBitmap> mark_bitmap_; - UniquePtr<SpaceBitmap> temp_bitmap_; - - // Approximate number of bytes which have been allocated into the space. - size_t num_bytes_allocated_; - size_t num_objects_allocated_; - size_t total_bytes_allocated_; - size_t total_objects_allocated_; - - static size_t bitmap_index_; - - DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, - size_t growth_limit); - - bool Init(size_t initial_size, size_t maximum_size, size_t growth_size, byte* requested_base); - - static void* CreateMallocSpace(void* base, size_t morecore_start, size_t initial_size); - - // The boundary tag overhead. - static const size_t kChunkOverhead = kWordSize; - - // Used to ensure mutual exclusion when the allocation spaces data structures are being modified. - Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - - // Underlying malloc space - void* const mspace_; - - // The capacity of the alloc space until such time that ClearGrowthLimit is called. - // The underlying mem_map_ controls the maximum size we allow the heap to grow to. The growth - // limit is a value <= to the mem_map_ capacity used for ergonomic reasons because of the zygote. - // Prior to forking the zygote the heap will have a maximally sized mem_map_ but the growth_limit_ - // will be set to a lower value. The growth_limit_ is used as the capacity of the alloc_space_, - // however, capacity normally can't vary. In the case of the growth_limit_ it can be cleared - // one time by a call to ClearGrowthLimit. - size_t growth_limit_; - - friend class MarkSweep; - - DISALLOW_COPY_AND_ASSIGN(DlMallocSpace); -}; - -// An image space is a space backed with a memory mapped image -class ImageSpace : public MemMapSpace { - public: - virtual bool CanAllocateInto() const { - return false; - } - - virtual bool IsCompactible() const { - return false; - } - - virtual SpaceType GetType() const { - return kSpaceTypeImageSpace; - } - - // create a Space from an image file. cannot be used for future allocation or collected. - static ImageSpace* Create(const std::string& image) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - const ImageHeader& GetImageHeader() const { - return *reinterpret_cast<ImageHeader*>(Begin()); - } - - const std::string GetImageFilename() const { - return GetName(); - } - - // Mark the objects defined in this space in the given live bitmap - void RecordImageAllocations(SpaceBitmap* live_bitmap) const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - virtual SpaceBitmap* GetLiveBitmap() const { - return live_bitmap_.get(); - } - - virtual SpaceBitmap* GetMarkBitmap() const { - // ImageSpaces have the same bitmap for both live and marked. This helps reduce the number of - // special cases to test against. - return live_bitmap_.get(); - } - - virtual void Dump(std::ostream& os) const; - - private: - friend class Space; - - UniquePtr<SpaceBitmap> live_bitmap_; - static size_t bitmap_index_; - - ImageSpace(const std::string& name, MemMap* mem_map); - - DISALLOW_COPY_AND_ASSIGN(ImageSpace); -}; - -// Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused -// pages back to the kernel. -void MspaceMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/); - -} // namespace art - -#endif // ART_SRC_GC_SPACE_H_ diff --git a/src/gc/space.cc b/src/gc/space/dlmalloc_space.cc index 1d3ee28d5b..02acd286dd 100644 --- a/src/gc/space.cc +++ b/src/gc/space/dlmalloc_space.cc @@ -13,33 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#include "space.h" - -#include "base/logging.h" -#include "base/stl_util.h" -#include "base/unix_file/fd_file.h" -#include "card_table.h" -#include "dlmalloc.h" -#include "image.h" -#include "mirror/array.h" -#include "mirror/abstract_method.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "os.h" +#include "dlmalloc_space.h" +#include "gc/accounting/card_table.h" +#include "gc/heap.h" #include "runtime.h" -#include "space_bitmap.h" -#include "space_bitmap-inl.h" #include "thread.h" -#include "UniquePtr.h" #include "utils.h" -namespace art { +//#include <valgrind/memcheck.h> +#include <valgrind.h> -static const bool kPrefetchDuringDlMallocFreeList = true; - -// Magic padding value that we use to check for buffer overruns. -static const word kPaddingValue = 0xBAC0BAC0; +namespace art { +namespace gc { +namespace space { // TODO: Remove define macro #define CHECK_MEMORY_CALL(call, args, what) \ @@ -51,45 +37,86 @@ static const word kPaddingValue = 0xBAC0BAC0; } \ } while (false) -ImageSpace* Space::AsImageSpace() { - DCHECK_EQ(GetType(), kSpaceTypeImageSpace); - return down_cast<ImageSpace*>(down_cast<MemMapSpace*>(this)); -} - -DlMallocSpace* Space::AsAllocSpace() { - DCHECK_EQ(GetType(), kSpaceTypeAllocSpace); - return down_cast<DlMallocSpace*>(down_cast<MemMapSpace*>(this)); -} +static const bool kPrefetchDuringDlMallocFreeList = true; -DlMallocSpace* Space::AsZygoteSpace() { - DCHECK_EQ(GetType(), kSpaceTypeZygoteSpace); - return down_cast<DlMallocSpace*>(down_cast<MemMapSpace*>(this)); -} +// Number of bytes to use as a red zone (rdz). A red zone of this size will be placed before and +// after each allocation. 8 bytes provides long/double alignment. +const size_t kValgrindRedZoneBytes = 8; + +// A specialization of DlMallocSpace that provides information to valgrind wrt allocations. +class ValgrindDlMallocSpace : public DlMallocSpace { + public: + virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes) { + void* obj_with_rdz = DlMallocSpace::AllocWithGrowth(self, num_bytes + (2 * kValgrindRedZoneBytes)); + if (obj_with_rdz != NULL) { + //VALGRIND_MAKE_MEM_UNDEFINED(); + mirror::Object* result = reinterpret_cast<mirror::Object*>(reinterpret_cast<byte*>(obj_with_rdz) + + kValgrindRedZoneBytes); + VALGRIND_MEMPOOL_ALLOC(GetMspace(), result, num_bytes); + LOG(INFO) << "AllocWithGrowth on " << self << " = " << obj_with_rdz + << " of size " << num_bytes; + return result; + } else { + return NULL; + } + } -LargeObjectSpace* Space::AsLargeObjectSpace() { - DCHECK_EQ(GetType(), kSpaceTypeLargeObjectSpace); - return reinterpret_cast<LargeObjectSpace*>(this); -} + virtual mirror::Object* Alloc(Thread* self, size_t num_bytes) { + void* obj_with_rdz = DlMallocSpace::Alloc(self, num_bytes + (2 * kValgrindRedZoneBytes)); + if (obj_with_rdz != NULL) { + mirror::Object* result = reinterpret_cast<mirror::Object*>(reinterpret_cast<byte*>(obj_with_rdz) + + kValgrindRedZoneBytes); + VALGRIND_MEMPOOL_ALLOC(GetMspace(), result, num_bytes); + LOG(INFO) << "Alloc on " << self << " = " << obj_with_rdz + << " of size " << num_bytes; + return result; + } else { + return NULL; + } + } -ContinuousSpace::ContinuousSpace(const std::string& name, byte* begin, byte* end, - GcRetentionPolicy gc_retention_policy) - : name_(name), gc_retention_policy_(gc_retention_policy), begin_(begin), end_(end) { + virtual size_t AllocationSize(const mirror::Object* obj) { + const void* obj_after_rdz = reinterpret_cast<const void*>(obj); + size_t result = DlMallocSpace::AllocationSize( + reinterpret_cast<const mirror::Object*>(reinterpret_cast<const byte*>(obj_after_rdz) - + kValgrindRedZoneBytes)); + return result - (2 * kValgrindRedZoneBytes); + } -} + virtual size_t Free(Thread* self, mirror::Object* ptr) { + void* obj_after_rdz = reinterpret_cast<void*>(ptr); + void* obj_with_rdz = reinterpret_cast<byte*>(obj_after_rdz) - kValgrindRedZoneBytes; + LOG(INFO) << "Free on " << self << " of " << obj_with_rdz; + size_t freed = DlMallocSpace::Free(self, reinterpret_cast<mirror::Object*>(obj_with_rdz)); + VALGRIND_MEMPOOL_FREE(GetMspace(), obj_after_rdz); + return freed - (2 * kValgrindRedZoneBytes); + } -DiscontinuousSpace::DiscontinuousSpace(const std::string& name, - GcRetentionPolicy gc_retention_policy) - : name_(name), gc_retention_policy_(gc_retention_policy) { + virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) { + size_t freed = 0; + for (size_t i = 0; i < num_ptrs; i++) { + void* obj_after_rdz = reinterpret_cast<void*>(ptrs[i]); + void* obj_with_rdz = reinterpret_cast<byte*>(obj_after_rdz) - kValgrindRedZoneBytes; + LOG(INFO) << "FreeList on " << self << " of " << obj_with_rdz; + freed += DlMallocSpace::Free(self, reinterpret_cast<mirror::Object*>(obj_with_rdz)); + VALGRIND_MEMPOOL_FREE(GetMspace(), obj_after_rdz); + } + return freed - (2 * kValgrindRedZoneBytes * num_ptrs); + } -} + ValgrindDlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, + byte* end, size_t growth_limit) : + DlMallocSpace(name, mem_map, mspace, begin, end, growth_limit) { + VALGRIND_CREATE_MEMPOOL(GetMspace(), kValgrindRedZoneBytes, true); + } -MemMapSpace::MemMapSpace(const std::string& name, MemMap* mem_map, size_t initial_size, - GcRetentionPolicy gc_retention_policy) - : ContinuousSpace(name, mem_map->Begin(), mem_map->Begin() + initial_size, gc_retention_policy), - mem_map_(mem_map) -{ + virtual ~ValgrindDlMallocSpace() { + VALGRIND_DESTROY_MEMPOOL(GetMspace()); + } -} + private: + DISALLOW_COPY_AND_ASSIGN(ValgrindDlMallocSpace); +}; size_t DlMallocSpace::bitmap_index_ = 0; @@ -103,15 +130,15 @@ DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* msp size_t bitmap_index = bitmap_index_++; - static const uintptr_t kGcCardSize = static_cast<uintptr_t>(CardTable::kCardSize); + static const uintptr_t kGcCardSize = static_cast<uintptr_t>(accounting::CardTable::kCardSize); CHECK(reinterpret_cast<uintptr_t>(mem_map->Begin()) % kGcCardSize == 0); CHECK(reinterpret_cast<uintptr_t>(mem_map->End()) % kGcCardSize == 0); - live_bitmap_.reset(SpaceBitmap::Create( + live_bitmap_.reset(accounting::SpaceBitmap::Create( StringPrintf("allocspace %s live-bitmap %d", name.c_str(), static_cast<int>(bitmap_index)), Begin(), Capacity())); DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace live bitmap #" << bitmap_index; - mark_bitmap_.reset(SpaceBitmap::Create( + mark_bitmap_.reset(accounting::SpaceBitmap::Create( StringPrintf("allocspace %s mark-bitmap %d", name.c_str(), static_cast<int>(bitmap_index)), Begin(), Capacity())); DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace mark bitmap #" << bitmap_index; @@ -177,8 +204,13 @@ DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_siz // Everything is set so record in immutable structure and leave MemMap* mem_map_ptr = mem_map.release(); - DlMallocSpace* space = new DlMallocSpace(name, mem_map_ptr, mspace, mem_map_ptr->Begin(), end, - growth_limit); + DlMallocSpace* space; + if (RUNNING_ON_VALGRIND > 0) { + space = new ValgrindDlMallocSpace(name, mem_map_ptr, mspace, mem_map_ptr->Begin(), end, + growth_limit); + } else { + space = new DlMallocSpace(name, mem_map_ptr, mspace, mem_map_ptr->Begin(), end, growth_limit); + } if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Space::CreateAllocSpace exiting (" << PrettyDuration(NanoTime() - start_time) << " ) " << *space; @@ -203,33 +235,26 @@ void* DlMallocSpace::CreateMallocSpace(void* begin, size_t morecore_start, size_ } void DlMallocSpace::SwapBitmaps() { - SpaceBitmap* temp_live_bitmap = live_bitmap_.release(); - live_bitmap_.reset(mark_bitmap_.release()); - mark_bitmap_.reset(temp_live_bitmap); + live_bitmap_.swap(mark_bitmap_); // Swap names to get more descriptive diagnostics. - std::string temp_name = live_bitmap_->GetName(); + std::string temp_name(live_bitmap_->GetName()); live_bitmap_->SetName(mark_bitmap_->GetName()); mark_bitmap_->SetName(temp_name); } mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(size_t num_bytes) { - if (kDebugSpaces) { - num_bytes += sizeof(word); - } - mirror::Object* result = reinterpret_cast<mirror::Object*>(mspace_calloc(mspace_, 1, num_bytes)); - if (kDebugSpaces && result != NULL) { - CHECK(Contains(result)) << "Allocation (" << reinterpret_cast<void*>(result) - << ") not in bounds of allocation space " << *this; - // Put a magic pattern before and after the allocation. - *reinterpret_cast<word*>(reinterpret_cast<byte*>(result) + AllocationSize(result) - - sizeof(word) - kChunkOverhead) = kPaddingValue; + if (result != NULL) { + if (kDebugSpaces) { + CHECK(Contains(result)) << "Allocation (" << reinterpret_cast<void*>(result) + << ") not in bounds of allocation space " << *this; + } + size_t allocation_size = AllocationSize(result); + num_bytes_allocated_ += allocation_size; + total_bytes_allocated_ += allocation_size; + ++total_objects_allocated_; + ++num_objects_allocated_; } - size_t allocation_size = AllocationSize(result); - num_bytes_allocated_ += allocation_size; - total_bytes_allocated_ += allocation_size; - ++total_objects_allocated_; - ++num_objects_allocated_; return result; } @@ -263,8 +288,8 @@ void DlMallocSpace::SetGrowthLimit(size_t growth_limit) { DlMallocSpace* DlMallocSpace::CreateZygoteSpace() { end_ = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(end_), kPageSize)); - DCHECK(IsAligned<CardTable::kCardSize>(begin_)); - DCHECK(IsAligned<CardTable::kCardSize>(end_)); + DCHECK(IsAligned<accounting::CardTable::kCardSize>(begin_)); + DCHECK(IsAligned<accounting::CardTable::kCardSize>(end_)); DCHECK(IsAligned<kPageSize>(begin_)); DCHECK(IsAligned<kPageSize>(end_)); size_t size = RoundUp(Size(), kPageSize); @@ -291,7 +316,7 @@ DlMallocSpace* DlMallocSpace::CreateZygoteSpace() { VLOG(heap) << "Size " << GetMemMap()->Size(); VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit); VLOG(heap) << "Capacity " << PrettySize(capacity); - UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(GetName().c_str(), End(), capacity, PROT_READ | PROT_WRITE)); + UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(GetName(), End(), capacity, PROT_READ | PROT_WRITE)); void* mspace = CreateMallocSpace(end_, starting_size, initial_size); // Protect memory beyond the initial size. byte* end = mem_map->Begin() + starting_size; @@ -314,9 +339,6 @@ size_t DlMallocSpace::Free(Thread* self, mirror::Object* ptr) { if (kDebugSpaces) { CHECK(ptr != NULL); CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this; - CHECK_EQ( - *reinterpret_cast<word*>(reinterpret_cast<byte*>(ptr) + AllocationSize(ptr) - - sizeof(word) - kChunkOverhead), kPaddingValue); } const size_t bytes_freed = InternalAllocationSize(ptr); num_bytes_allocated_ -= bytes_freed; @@ -374,20 +396,16 @@ void* DlMallocSpace::MoreCore(intptr_t increment) { lock_.AssertHeld(Thread::Current()); byte* original_end = end_; if (increment != 0) { - VLOG(heap) << "AllocSpace::MoreCore " << PrettySize(increment); + VLOG(heap) << "DlMallocSpace::MoreCore " << PrettySize(increment); byte* new_end = original_end + increment; if (increment > 0) { -#if DEBUG_SPACES // Should never be asked to increase the allocation beyond the capacity of the space. Enforced // by mspace_set_footprint_limit. CHECK_LE(new_end, Begin() + Capacity()); -#endif CHECK_MEMORY_CALL(mprotect, (original_end, increment, PROT_READ | PROT_WRITE), GetName()); } else { -#if DEBUG_SPACES // Should never be asked for negative footprint (ie before begin) CHECK_GT(original_end + increment, Begin()); -#endif // Advise we don't need the pages and protect them // TODO: by removing permissions to the pages we may be causing TLB shoot-down which can be // expensive (note the same isn't true for giving permissions to a page as the protected @@ -414,29 +432,13 @@ size_t DlMallocSpace::AllocationSize(const mirror::Object* obj) { return InternalAllocationSize(obj); } -void MspaceMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) { - // Is this chunk in use? - if (used_bytes != 0) { - return; - } - // Do we have any whole pages to give back? - start = reinterpret_cast<void*>(RoundUp(reinterpret_cast<uintptr_t>(start), kPageSize)); - end = reinterpret_cast<void*>(RoundDown(reinterpret_cast<uintptr_t>(end), kPageSize)); - if (end > start) { - size_t length = reinterpret_cast<byte*>(end) - reinterpret_cast<byte*>(start); - CHECK_MEMORY_CALL(madvise, (start, length, MADV_DONTNEED), "trim"); - size_t* reclaimed = reinterpret_cast<size_t*>(arg); - *reclaimed += length; - } -} - size_t DlMallocSpace::Trim() { MutexLock mu(Thread::Current(), lock_); // Trim to release memory at the end of the space. mspace_trim(mspace_, 0); // Visit space looking for page-sized holes to advise the kernel we don't need. size_t reclaimed = 0; - mspace_inspect_all(mspace_, MspaceMadviseCallback, &reclaimed); + mspace_inspect_all(mspace_, DlmallocMadviseCallback, &reclaimed); return reclaimed; } @@ -465,111 +467,14 @@ void DlMallocSpace::SetFootprintLimit(size_t new_size) { mspace_set_footprint_limit(mspace_, new_size); } -size_t ImageSpace::bitmap_index_ = 0; - -ImageSpace::ImageSpace(const std::string& name, MemMap* mem_map) - : MemMapSpace(name, mem_map, mem_map->Size(), kGcRetentionPolicyNeverCollect) { - const size_t bitmap_index = bitmap_index_++; - live_bitmap_.reset(SpaceBitmap::Create( - StringPrintf("imagespace %s live-bitmap %d", name.c_str(), static_cast<int>(bitmap_index)), - Begin(), Capacity())); - DCHECK(live_bitmap_.get() != NULL) << "could not create imagespace live bitmap #" << bitmap_index; -} - -ImageSpace* ImageSpace::Create(const std::string& image_file_name) { - CHECK(!image_file_name.empty()); - - uint64_t start_time = 0; - if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { - start_time = NanoTime(); - LOG(INFO) << "Space::CreateImageSpace entering" << " image_file_name=" << image_file_name; - } - - UniquePtr<File> file(OS::OpenFile(image_file_name.c_str(), false)); - if (file.get() == NULL) { - LOG(ERROR) << "Failed to open " << image_file_name; - return NULL; - } - ImageHeader image_header; - bool success = file->ReadFully(&image_header, sizeof(image_header)); - if (!success || !image_header.IsValid()) { - LOG(ERROR) << "Invalid image header " << image_file_name; - return NULL; - } - UniquePtr<MemMap> map(MemMap::MapFileAtAddress(image_header.GetImageBegin(), - file->GetLength(), - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_FIXED, - file->Fd(), - 0, - false)); - if (map.get() == NULL) { - LOG(ERROR) << "Failed to map " << image_file_name; - return NULL; - } - CHECK_EQ(image_header.GetImageBegin(), map->Begin()); - DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader))); - - Runtime* runtime = Runtime::Current(); - mirror::Object* resolution_method = image_header.GetImageRoot(ImageHeader::kResolutionMethod); - runtime->SetResolutionMethod(down_cast<mirror::AbstractMethod*>(resolution_method)); - - mirror::Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod); - runtime->SetCalleeSaveMethod(down_cast<mirror::AbstractMethod*>(callee_save_method), Runtime::kSaveAll); - callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsOnlySaveMethod); - runtime->SetCalleeSaveMethod(down_cast<mirror::AbstractMethod*>(callee_save_method), Runtime::kRefsOnly); - callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsAndArgsSaveMethod); - runtime->SetCalleeSaveMethod(down_cast<mirror::AbstractMethod*>(callee_save_method), Runtime::kRefsAndArgs); - - ImageSpace* space = new ImageSpace(image_file_name, map.release()); - if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { - LOG(INFO) << "Space::CreateImageSpace exiting (" << PrettyDuration(NanoTime() - start_time) - << ") " << *space; - } - return space; -} - -void ImageSpace::RecordImageAllocations(SpaceBitmap* live_bitmap) const { - uint64_t start_time = 0; - if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { - LOG(INFO) << "ImageSpace::RecordImageAllocations entering"; - start_time = NanoTime(); - } - DCHECK(!Runtime::Current()->IsStarted()); - CHECK(live_bitmap != NULL); - byte* current = Begin() + RoundUp(sizeof(ImageHeader), kObjectAlignment); - byte* end = End(); - while (current < end) { - DCHECK_ALIGNED(current, kObjectAlignment); - const mirror::Object* obj = reinterpret_cast<const mirror::Object*>(current); - live_bitmap->Set(obj); - current += RoundUp(obj->SizeOf(), kObjectAlignment); - } - if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { - LOG(INFO) << "ImageSpace::RecordImageAllocations exiting (" - << PrettyDuration(NanoTime() - start_time) << ")"; - } -} - -std::ostream& operator<<(std::ostream& os, const Space& space) { - space.Dump(os); - return os; -} - void DlMallocSpace::Dump(std::ostream& os) const { os << GetType() - << "begin=" << reinterpret_cast<void*>(Begin()) + << " begin=" << reinterpret_cast<void*>(Begin()) << ",end=" << reinterpret_cast<void*>(End()) << ",size=" << PrettySize(Size()) << ",capacity=" << PrettySize(Capacity()) << ",name=\"" << GetName() << "\"]"; } -void ImageSpace::Dump(std::ostream& os) const { - os << GetType() - << "begin=" << reinterpret_cast<void*>(Begin()) - << ",end=" << reinterpret_cast<void*>(End()) - << ",size=" << PrettySize(Size()) - << ",name=\"" << GetName() << "\"]"; -} - +} // namespace space +} // namespace gc } // namespace art diff --git a/src/gc/space/dlmalloc_space.h b/src/gc/space/dlmalloc_space.h new file mode 100644 index 0000000000..00df0e6d42 --- /dev/null +++ b/src/gc/space/dlmalloc_space.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_GC_SPACE_DLMALLOC_SPACE_H_ +#define ART_SRC_GC_SPACE_DLMALLOC_SPACE_H_ + +#include "gc/allocator/dlmalloc.h" +#include "space.h" + +namespace art { +namespace gc { + +namespace collector { + class MarkSweep; +} // namespace collector + +namespace space { + +// An alloc space is a space where objects may be allocated and garbage collected. +class DlMallocSpace : public MemMapSpace, public AllocSpace { + public: + typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg); + + SpaceType GetType() const { + if (GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) { + return kSpaceTypeZygoteSpace; + } else { + return kSpaceTypeAllocSpace; + } + } + + // Create a AllocSpace with the requested sizes. The requested + // base address is not guaranteed to be granted, if it is required, + // the caller should call Begin on the returned space to confirm + // the request was granted. + static DlMallocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit, + size_t capacity, byte* requested_begin); + + // Allocate num_bytes without allowing the underlying mspace to grow. + virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes); + + // Allocate num_bytes allowing the underlying mspace to grow. + virtual mirror::Object* Alloc(Thread* self, size_t num_bytes); + + // Return the storage space required by obj. + virtual size_t AllocationSize(const mirror::Object* obj); + virtual size_t Free(Thread* self, mirror::Object* ptr); + virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs); + + void* MoreCore(intptr_t increment); + + void* GetMspace() const { + return mspace_; + } + + // Hands unused pages back to the system. + size_t Trim(); + + // Perform a mspace_inspect_all which calls back for each allocation chunk. The chunk may not be + // in use, indicated by num_bytes equaling zero. + void Walk(WalkCallback callback, void* arg); + + // Returns the number of bytes that the heap is allowed to obtain from the system via MoreCore. + size_t GetFootprintLimit(); + + // Set the maximum number of bytes that the heap is allowed to obtain from the system via + // MoreCore. Note this is used to stop the mspace growing beyond the limit to Capacity. When + // allocations fail we GC before increasing the footprint limit and allowing the mspace to grow. + void SetFootprintLimit(size_t limit); + + // Removes the fork time growth limit on capacity, allowing the application to allocate up to the + // maximum reserved size of the heap. + void ClearGrowthLimit() { + growth_limit_ = NonGrowthLimitCapacity(); + } + + // Override capacity so that we only return the possibly limited capacity + size_t Capacity() const { + return growth_limit_; + } + + // The total amount of memory reserved for the alloc space. + size_t NonGrowthLimitCapacity() const { + return GetMemMap()->Size(); + } + + accounting::SpaceBitmap* GetLiveBitmap() const { + return live_bitmap_.get(); + } + + accounting::SpaceBitmap* GetMarkBitmap() const { + return mark_bitmap_.get(); + } + + void Dump(std::ostream& os) const; + + void SetGrowthLimit(size_t growth_limit); + + // Swap the live and mark bitmaps of this space. This is used by the GC for concurrent sweeping. + void SwapBitmaps(); + + // Turn ourself into a zygote space and return a new alloc space which has our unused memory. + DlMallocSpace* CreateZygoteSpace(); + + uint64_t GetBytesAllocated() const { + return num_bytes_allocated_; + } + + uint64_t GetObjectsAllocated() const { + return num_objects_allocated_; + } + + uint64_t GetTotalBytesAllocated() const { + return total_bytes_allocated_; + } + + uint64_t GetTotalObjectsAllocated() const { + return total_objects_allocated_; + } + + protected: + DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, + size_t growth_limit); + + private: + size_t InternalAllocationSize(const mirror::Object* obj); + mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes) EXCLUSIVE_LOCKS_REQUIRED(lock_); + + bool Init(size_t initial_size, size_t maximum_size, size_t growth_size, byte* requested_base); + + static void* CreateMallocSpace(void* base, size_t morecore_start, size_t initial_size); + + UniquePtr<accounting::SpaceBitmap> live_bitmap_; + UniquePtr<accounting::SpaceBitmap> mark_bitmap_; + UniquePtr<accounting::SpaceBitmap> temp_bitmap_; + + // Approximate number of bytes which have been allocated into the space. + size_t num_bytes_allocated_; + size_t num_objects_allocated_; + size_t total_bytes_allocated_; + size_t total_objects_allocated_; + + static size_t bitmap_index_; + + // The boundary tag overhead. + static const size_t kChunkOverhead = kWordSize; + + // Used to ensure mutual exclusion when the allocation spaces data structures are being modified. + Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + + // Underlying malloc space + void* const mspace_; + + // The capacity of the alloc space until such time that ClearGrowthLimit is called. + // The underlying mem_map_ controls the maximum size we allow the heap to grow to. The growth + // limit is a value <= to the mem_map_ capacity used for ergonomic reasons because of the zygote. + // Prior to forking the zygote the heap will have a maximally sized mem_map_ but the growth_limit_ + // will be set to a lower value. The growth_limit_ is used as the capacity of the alloc_space_, + // however, capacity normally can't vary. In the case of the growth_limit_ it can be cleared + // one time by a call to ClearGrowthLimit. + size_t growth_limit_; + + friend class collector::MarkSweep; + + DISALLOW_COPY_AND_ASSIGN(DlMallocSpace); +}; + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_SRC_GC_SPACE_DLMALLOC_SPACE_H_ diff --git a/src/gc/space/image_space.cc b/src/gc/space/image_space.cc new file mode 100644 index 0000000000..46c39378d7 --- /dev/null +++ b/src/gc/space/image_space.cc @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "image_space.h" + +#include "base/unix_file/fd_file.h" +#include "gc/accounting/space_bitmap-inl.h" +#include "mirror/abstract_method.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "os.h" +#include "runtime.h" +#include "space-inl.h" +#include "utils.h" + +namespace art { +namespace gc { +namespace space { + +size_t ImageSpace::bitmap_index_ = 0; + +ImageSpace::ImageSpace(const std::string& name, MemMap* mem_map) +: MemMapSpace(name, mem_map, mem_map->Size(), kGcRetentionPolicyNeverCollect) { + const size_t bitmap_index = bitmap_index_++; + live_bitmap_.reset(accounting::SpaceBitmap::Create( + StringPrintf("imagespace %s live-bitmap %d", name.c_str(), static_cast<int>(bitmap_index)), + Begin(), Capacity())); + DCHECK(live_bitmap_.get() != NULL) << "could not create imagespace live bitmap #" << bitmap_index; +} + +ImageSpace* ImageSpace::Create(const std::string& image_file_name) { + CHECK(!image_file_name.empty()); + + uint64_t start_time = 0; + if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { + start_time = NanoTime(); + LOG(INFO) << "Space::CreateImageSpace entering" << " image_file_name=" << image_file_name; + } + + UniquePtr<File> file(OS::OpenFile(image_file_name.c_str(), false)); + if (file.get() == NULL) { + LOG(ERROR) << "Failed to open " << image_file_name; + return NULL; + } + ImageHeader image_header; + bool success = file->ReadFully(&image_header, sizeof(image_header)); + if (!success || !image_header.IsValid()) { + LOG(ERROR) << "Invalid image header " << image_file_name; + return NULL; + } + UniquePtr<MemMap> map(MemMap::MapFileAtAddress(image_header.GetImageBegin(), + file->GetLength(), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, + file->Fd(), + 0, + false)); + if (map.get() == NULL) { + LOG(ERROR) << "Failed to map " << image_file_name; + return NULL; + } + CHECK_EQ(image_header.GetImageBegin(), map->Begin()); + DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader))); + + Runtime* runtime = Runtime::Current(); + mirror::Object* resolution_method = image_header.GetImageRoot(ImageHeader::kResolutionMethod); + runtime->SetResolutionMethod(down_cast<mirror::AbstractMethod*>(resolution_method)); + + mirror::Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod); + runtime->SetCalleeSaveMethod(down_cast<mirror::AbstractMethod*>(callee_save_method), Runtime::kSaveAll); + callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsOnlySaveMethod); + runtime->SetCalleeSaveMethod(down_cast<mirror::AbstractMethod*>(callee_save_method), Runtime::kRefsOnly); + callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsAndArgsSaveMethod); + runtime->SetCalleeSaveMethod(down_cast<mirror::AbstractMethod*>(callee_save_method), Runtime::kRefsAndArgs); + + ImageSpace* space = new ImageSpace(image_file_name, map.release()); + if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { + LOG(INFO) << "Space::CreateImageSpace exiting (" << PrettyDuration(NanoTime() - start_time) + << ") " << *space; + } + return space; +} + +void ImageSpace::RecordImageAllocations(accounting::SpaceBitmap* live_bitmap) const { + uint64_t start_time = 0; + if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { + LOG(INFO) << "ImageSpace::RecordImageAllocations entering"; + start_time = NanoTime(); + } + DCHECK(!Runtime::Current()->IsStarted()); + CHECK(live_bitmap != NULL); + byte* current = Begin() + RoundUp(sizeof(ImageHeader), kObjectAlignment); + byte* end = End(); + while (current < end) { + DCHECK_ALIGNED(current, kObjectAlignment); + const mirror::Object* obj = reinterpret_cast<const mirror::Object*>(current); + live_bitmap->Set(obj); + current += RoundUp(obj->SizeOf(), kObjectAlignment); + } + if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { + LOG(INFO) << "ImageSpace::RecordImageAllocations exiting (" + << PrettyDuration(NanoTime() - start_time) << ")"; + } +} + +void ImageSpace::Dump(std::ostream& os) const { + os << GetType() + << "begin=" << reinterpret_cast<void*>(Begin()) + << ",end=" << reinterpret_cast<void*>(End()) + << ",size=" << PrettySize(Size()) + << ",name=\"" << GetName() << "\"]"; +} + +} // namespace space +} // namespace gc +} // namespace art diff --git a/src/gc/space/image_space.h b/src/gc/space/image_space.h new file mode 100644 index 0000000000..afec5b7305 --- /dev/null +++ b/src/gc/space/image_space.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_GC_SPACE_IMAGE_SPACE_H_ +#define ART_SRC_GC_SPACE_IMAGE_SPACE_H_ + +#include "space.h" + +namespace art { +namespace gc { +namespace space { + +// An image space is a space backed with a memory mapped image. +class ImageSpace : public MemMapSpace { + public: + bool CanAllocateInto() const { + return false; + } + + SpaceType GetType() const { + return kSpaceTypeImageSpace; + } + + // create a Space from an image file. cannot be used for future allocation or collected. + static ImageSpace* Create(const std::string& image) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + const ImageHeader& GetImageHeader() const { + return *reinterpret_cast<ImageHeader*>(Begin()); + } + + const std::string GetImageFilename() const { + return GetName(); + } + + // Mark the objects defined in this space in the given live bitmap + void RecordImageAllocations(accounting::SpaceBitmap* live_bitmap) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + accounting::SpaceBitmap* GetLiveBitmap() const { + return live_bitmap_.get(); + } + + accounting::SpaceBitmap* GetMarkBitmap() const { + // ImageSpaces have the same bitmap for both live and marked. This helps reduce the number of + // special cases to test against. + return live_bitmap_.get(); + } + + void Dump(std::ostream& os) const; + + private: + friend class Space; + + static size_t bitmap_index_; + + UniquePtr<accounting::SpaceBitmap> live_bitmap_; + + ImageSpace(const std::string& name, MemMap* mem_map); + + DISALLOW_COPY_AND_ASSIGN(ImageSpace); +}; + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_SRC_GC_SPACE_IMAGE_SPACE_H_ diff --git a/src/gc/large_object_space.cc b/src/gc/space/large_object_space.cc index c3bf3824ad..3cee1b7355 100644 --- a/src/gc/large_object_space.cc +++ b/src/gc/space/large_object_space.cc @@ -14,18 +14,19 @@ * limitations under the License. */ +#include "large_object_space.h" + #include "base/logging.h" #include "base/stl_util.h" -#include "large_object_space.h" #include "UniquePtr.h" -#include "dlmalloc.h" #include "image.h" #include "os.h" -#include "space_bitmap.h" #include "thread.h" #include "utils.h" namespace art { +namespace gc { +namespace space { void LargeObjectSpace::SwapBitmaps() { live_objects_.swap(mark_objects_); @@ -39,8 +40,6 @@ LargeObjectSpace::LargeObjectSpace(const std::string& name) : DiscontinuousSpace(name, kGcRetentionPolicyAlwaysCollect), num_bytes_allocated_(0), num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0) { - live_objects_.reset(new SpaceSetMap("large live objects")); - mark_objects_.reset(new SpaceSetMap("large marked objects")); } @@ -281,4 +280,6 @@ void FreeListSpace::Dump(std::ostream& os) const{ << " end: " << reinterpret_cast<void*>(End()); } -} +} // namespace space +} // namespace gc +} // namespace art diff --git a/src/gc/large_object_space.h b/src/gc/space/large_object_space.h index 8a2f9707fd..197fad3854 100644 --- a/src/gc/large_object_space.h +++ b/src/gc/space/large_object_space.h @@ -14,51 +14,38 @@ * limitations under the License. */ -#ifndef ART_SRC_GC_LARGE_OBJECT_SPACE_H_ -#define ART_SRC_GC_LARGE_OBJECT_SPACE_H_ +#ifndef ART_SRC_GC_SPACE_LARGE_OBJECT_SPACE_H_ +#define ART_SRC_GC_SPACE_LARGE_OBJECT_SPACE_H_ -#include "space.h" + +#include "dlmalloc_space.h" #include "safe_map.h" +#include "space.h" #include <set> #include <vector> namespace art { -class SpaceSetMap; +namespace gc { +namespace space { // Abstraction implemented by all large object spaces. class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { public: - virtual bool CanAllocateInto() const { - return true; - } - - virtual bool IsCompactible() const { - return true; - } - virtual SpaceType GetType() const { return kSpaceTypeLargeObjectSpace; } - virtual SpaceSetMap* GetLiveObjects() const { - return live_objects_.get(); - } - - virtual SpaceSetMap* GetMarkObjects() const { - return mark_objects_.get(); - } - virtual void SwapBitmaps(); virtual void CopyLiveToMarked(); virtual void Walk(DlMallocSpace::WalkCallback, void* arg) = 0; virtual ~LargeObjectSpace() {} - uint64_t GetNumBytesAllocated() const { + uint64_t GetBytesAllocated() const { return num_bytes_allocated_; } - uint64_t GetNumObjectsAllocated() const { + uint64_t GetObjectsAllocated() const { return num_objects_allocated_; } @@ -82,10 +69,10 @@ class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { size_t total_bytes_allocated_; size_t total_objects_allocated_; - UniquePtr<SpaceSetMap> live_objects_; - UniquePtr<SpaceSetMap> mark_objects_; - friend class Space; + + private: + DISALLOW_COPY_AND_ASSIGN(LargeObjectSpace); }; // A discontinuous large object space implemented by individual mmap/munmap calls. @@ -96,12 +83,13 @@ class LargeObjectMapSpace : public LargeObjectSpace { static LargeObjectMapSpace* Create(const std::string& name); // Return the storage space required by obj. - virtual size_t AllocationSize(const mirror::Object* obj); - virtual mirror::Object* Alloc(Thread* self, size_t num_bytes); + size_t AllocationSize(const mirror::Object* obj); + mirror::Object* Alloc(Thread* self, size_t num_bytes); size_t Free(Thread* self, mirror::Object* ptr); - virtual void Walk(DlMallocSpace::WalkCallback, void* arg); + void Walk(DlMallocSpace::WalkCallback, void* arg); // TODO: disabling thread safety analysis as this may be called when we already hold lock_. - virtual bool Contains(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS; + bool Contains(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS; + private: LargeObjectMapSpace(const std::string& name); virtual ~LargeObjectMapSpace() {} @@ -114,6 +102,7 @@ private: }; // A continuous large object space with a free-list to handle holes. +// TODO: this implementation is buggy. class FreeListSpace : public LargeObjectSpace { public: virtual ~FreeListSpace(); @@ -140,7 +129,7 @@ class FreeListSpace : public LargeObjectSpace { return End() - Begin(); } - virtual void Dump(std::ostream& os) const; + void Dump(std::ostream& os) const; private: static const size_t kAlignment = kPageSize; @@ -197,6 +186,8 @@ class FreeListSpace : public LargeObjectSpace { FreeChunks free_chunks_ GUARDED_BY(lock_); }; -} +} // namespace space +} // namespace gc +} // namespace art -#endif // ART_SRC_GC_LARGE_OBJECT_SPACE_H_ +#endif // ART_SRC_GC_SPACE_LARGE_OBJECT_SPACE_H_ diff --git a/src/gc/space/space-inl.h b/src/gc/space/space-inl.h new file mode 100644 index 0000000000..8216d1b075 --- /dev/null +++ b/src/gc/space/space-inl.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_GC_SPACE_SPACE_INL_H_ +#define ART_SRC_GC_SPACE_SPACE_INL_H_ + +#include "space.h" + +#include "dlmalloc_space.h" +#include "image_space.h" + +namespace art { +namespace gc { +namespace space { + +inline ImageSpace* Space::AsImageSpace() { + DCHECK_EQ(GetType(), kSpaceTypeImageSpace); + return down_cast<ImageSpace*>(down_cast<MemMapSpace*>(this)); +} + +inline DlMallocSpace* Space::AsDlMallocSpace() { + DCHECK_EQ(GetType(), kSpaceTypeAllocSpace); + return down_cast<DlMallocSpace*>(down_cast<MemMapSpace*>(this)); +} + +inline DlMallocSpace* Space::AsZygoteSpace() { + DCHECK_EQ(GetType(), kSpaceTypeZygoteSpace); + return down_cast<DlMallocSpace*>(down_cast<MemMapSpace*>(this)); +} + +inline LargeObjectSpace* Space::AsLargeObjectSpace() { + DCHECK_EQ(GetType(), kSpaceTypeLargeObjectSpace); + return reinterpret_cast<LargeObjectSpace*>(this); +} + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_SRC_GC_SPACE_SPACE_INL_H_ diff --git a/src/gc/space/space.cc b/src/gc/space/space.cc new file mode 100644 index 0000000000..eae281ad40 --- /dev/null +++ b/src/gc/space/space.cc @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "space.h" + +#include "base/logging.h" + +namespace art { +namespace gc { +namespace space { + +Space::Space(const std::string& name, GcRetentionPolicy gc_retention_policy) : + name_(name), gc_retention_policy_(gc_retention_policy) { } + +void Space::Dump(std::ostream& os) const { + os << GetName() << ":" << GetGcRetentionPolicy(); +} + +std::ostream& operator<<(std::ostream& os, const Space& space) { + space.Dump(os); + return os; +} + + +DiscontinuousSpace::DiscontinuousSpace(const std::string& name, + GcRetentionPolicy gc_retention_policy) : + Space(name, gc_retention_policy), + live_objects_(new accounting::SpaceSetMap("large live objects")), + mark_objects_(new accounting::SpaceSetMap("large marked objects")) { +} + +} // namespace space +} // namespace gc +} // namespace art diff --git a/src/gc/space/space.h b/src/gc/space/space.h new file mode 100644 index 0000000000..ca01c55497 --- /dev/null +++ b/src/gc/space/space.h @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_GC_SPACE_SPACE_H_ +#define ART_SRC_GC_SPACE_SPACE_H_ + +#include <string> + +#include "UniquePtr.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "gc/accounting/space_bitmap.h" +#include "globals.h" +#include "image.h" +#include "mem_map.h" + +namespace art { +namespace mirror { + class Object; +} // namespace mirror + +namespace gc { + +namespace accounting { + class SpaceBitmap; +} // namespace accounting + +class Heap; + +namespace space { + +class DlMallocSpace; +class ImageSpace; +class LargeObjectSpace; + +static const bool kDebugSpaces = kIsDebugBuild; + +// See Space::GetGcRetentionPolicy. +enum GcRetentionPolicy { + // Objects are retained forever with this policy for a space. + kGcRetentionPolicyNeverCollect, + // Every GC cycle will attempt to collect objects in this space. + kGcRetentionPolicyAlwaysCollect, + // Objects will be considered for collection only in "full" GC cycles, ie faster partial + // collections won't scan these areas such as the Zygote. + kGcRetentionPolicyFullCollect, +}; +std::ostream& operator<<(std::ostream& os, const GcRetentionPolicy& policy); + +enum SpaceType { + kSpaceTypeImageSpace, + kSpaceTypeAllocSpace, + kSpaceTypeZygoteSpace, + kSpaceTypeLargeObjectSpace, +}; +std::ostream& operator<<(std::ostream& os, const SpaceType& space_type); + +// A space contains memory allocated for managed objects. +class Space { + public: + // Dump space. Also key method for C++ vtables. + virtual void Dump(std::ostream& os) const; + + // Name of the space. May vary, for example before/after the Zygote fork. + const char* GetName() const { + return name_.c_str(); + } + + // The policy of when objects are collected associated with this space. + GcRetentionPolicy GetGcRetentionPolicy() const { + return gc_retention_policy_; + } + + // Does the space support allocation? + virtual bool CanAllocateInto() const { + return true; + } + + // Is the given object contained within this space? + virtual bool Contains(const mirror::Object* obj) const = 0; + + // The kind of space this: image, alloc, zygote, large object. + virtual SpaceType GetType() const = 0; + + // Is this an image space, ie one backed by a memory mapped image file. + bool IsImageSpace() const { + return GetType() == kSpaceTypeImageSpace; + } + ImageSpace* AsImageSpace(); + + // Is this a dlmalloc backed allocation space? + bool IsDlMallocSpace() const { + SpaceType type = GetType(); + return type == kSpaceTypeAllocSpace || type == kSpaceTypeZygoteSpace; + } + DlMallocSpace* AsDlMallocSpace(); + + // Is this the space allocated into by the Zygote and no-longer in use? + bool IsZygoteSpace() const { + return GetType() == kSpaceTypeZygoteSpace; + } + DlMallocSpace* AsZygoteSpace(); + + // Does this space hold large objects and implement the large object space abstraction? + bool IsLargeObjectSpace() const { + return GetType() == kSpaceTypeLargeObjectSpace; + } + LargeObjectSpace* AsLargeObjectSpace(); + + virtual ~Space() {} + + protected: + Space(const std::string& name, GcRetentionPolicy gc_retention_policy); + + void SetGcRetentionPolicy(GcRetentionPolicy gc_retention_policy) { + gc_retention_policy_ = gc_retention_policy; + } + + // Name of the space that may vary due to the Zygote fork. + std::string name_; + + private: + // When should objects within this space be reclaimed? Not constant as we vary it in the case + // of Zygote forking. + GcRetentionPolicy gc_retention_policy_; + + friend class art::gc::Heap; + + DISALLOW_COPY_AND_ASSIGN(Space); +}; +std::ostream& operator<<(std::ostream& os, const Space& space); + +// AllocSpace interface. +class AllocSpace { + public: + // Number of bytes currently allocated. + virtual uint64_t GetBytesAllocated() const = 0; + // Number of objects currently allocated. + virtual uint64_t GetObjectsAllocated() const = 0; + // Number of bytes allocated since the space was created. + virtual uint64_t GetTotalBytesAllocated() const = 0; + // Number of objects allocated since the space was created. + virtual uint64_t GetTotalObjectsAllocated() const = 0; + + // Allocate num_bytes without allowing growth. + virtual mirror::Object* Alloc(Thread* self, size_t num_bytes) = 0; + + // Return the storage space required by obj. + virtual size_t AllocationSize(const mirror::Object* obj) = 0; + + // Returns how many bytes were freed. + virtual size_t Free(Thread* self, mirror::Object* ptr) = 0; + + // Returns how many bytes were freed. + virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) = 0; + + protected: + AllocSpace() {} + virtual ~AllocSpace() {} + + private: + DISALLOW_COPY_AND_ASSIGN(AllocSpace); +}; + +// Continuous spaces have bitmaps, and an address range. Although not required, objects within +// continuous spaces can be marked in the card table. +class ContinuousSpace : public Space { + public: + // Address at which the space begins + byte* Begin() const { + return begin_; + } + + // Address at which the space ends, which may vary as the space is filled. + byte* End() const { + return end_; + } + + // Current size of space + size_t Size() const { + return End() - Begin(); + } + + virtual accounting::SpaceBitmap* GetLiveBitmap() const = 0; + virtual accounting::SpaceBitmap* GetMarkBitmap() const = 0; + + // Is object within this space? We check to see if the pointer is beyond the end first as + // continuous spaces are iterated over from low to high. + bool HasAddress(const mirror::Object* obj) const { + const byte* byte_ptr = reinterpret_cast<const byte*>(obj); + return byte_ptr < End() && byte_ptr >= Begin(); + } + + bool Contains(const mirror::Object* obj) const { + return HasAddress(obj); + } + + virtual ~ContinuousSpace() {} + + protected: + ContinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy, + byte* begin, byte* end) : + Space(name, gc_retention_policy), begin_(begin), end_(end) { + } + + + // The beginning of the storage for fast access. + byte* const begin_; + + // Current end of the space. + byte* end_; + + private: + DISALLOW_COPY_AND_ASSIGN(ContinuousSpace); +}; + +// A space where objects may be allocated higgledy-piggledy throughout virtual memory. Currently +// the card table can't cover these objects and so the write barrier shouldn't be triggered. This +// is suitable for use for large primitive arrays. +class DiscontinuousSpace : public Space { + public: + accounting::SpaceSetMap* GetLiveObjects() const { + return live_objects_.get(); + } + + accounting::SpaceSetMap* GetMarkObjects() const { + return mark_objects_.get(); + } + + virtual ~DiscontinuousSpace() {} + + protected: + DiscontinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy); + + UniquePtr<accounting::SpaceSetMap> live_objects_; + UniquePtr<accounting::SpaceSetMap> mark_objects_; + + private: + DISALLOW_COPY_AND_ASSIGN(DiscontinuousSpace); +}; + +class MemMapSpace : public ContinuousSpace { + public: + // Maximum which the mapped space can grow to. + virtual size_t Capacity() const { + return mem_map_->Size(); + } + + // Size of the space without a limit on its growth. By default this is just the Capacity, but + // for the allocation space we support starting with a small heap and then extending it. + virtual size_t NonGrowthLimitCapacity() const { + return Capacity(); + } + + protected: + MemMapSpace(const std::string& name, MemMap* mem_map, size_t initial_size, + GcRetentionPolicy gc_retention_policy) + : ContinuousSpace(name, gc_retention_policy, + mem_map->Begin(), mem_map->Begin() + initial_size), + mem_map_(mem_map) { + } + + MemMap* GetMemMap() { + return mem_map_.get(); + } + + const MemMap* GetMemMap() const { + return mem_map_.get(); + } + + private: + // Underlying storage of the space + UniquePtr<MemMap> mem_map_; + + DISALLOW_COPY_AND_ASSIGN(MemMapSpace); +}; + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_SRC_GC_SPACE_SPACE_H_ diff --git a/src/gc/space_test.cc b/src/gc/space/space_test.cc index 372ec7710c..08ae894e58 100644 --- a/src/gc/space_test.cc +++ b/src/gc/space/space_test.cc @@ -14,22 +14,27 @@ * limitations under the License. */ -#include "space.h" +#include "dlmalloc_space.h" #include "common_test.h" -#include "dlmalloc.h" #include "globals.h" #include "UniquePtr.h" #include <stdint.h> namespace art { +namespace gc { +namespace space { class SpaceTest : public CommonTest { public: void SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr_t object_size, int round, size_t growth_limit); void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size); + + void AddContinuousSpace(ContinuousSpace* space) { + Runtime::Current()->GetHeap()->AddContinuousSpace(space); + } }; TEST_F(SpaceTest, Init) { @@ -79,7 +84,7 @@ TEST_F(SpaceTest, ZygoteSpace) { ASSERT_TRUE(space != NULL); // Make space findable to the heap, will also delete space when runtime is cleaned up - Runtime::Current()->GetHeap()->AddSpace(space); + AddContinuousSpace(space); Thread* self = Thread::Current(); // Succeeds, fits without adjusting the footprint limit. @@ -121,7 +126,7 @@ TEST_F(SpaceTest, ZygoteSpace) { space = space->CreateZygoteSpace(); // Make space findable to the heap, will also delete space when runtime is cleaned up - Runtime::Current()->GetHeap()->AddSpace(space); + AddContinuousSpace(space); // Succeeds, fits without adjusting the footprint limit. ptr1 = space->Alloc(self, 1 * MB); @@ -148,7 +153,7 @@ TEST_F(SpaceTest, AllocAndFree) { Thread* self = Thread::Current(); // Make space findable to the heap, will also delete space when runtime is cleaned up - Runtime::Current()->GetHeap()->AddSpace(space); + AddContinuousSpace(space); // Succeeds, fits without adjusting the footprint limit. mirror::Object* ptr1 = space->Alloc(self, 1 * MB); @@ -190,7 +195,7 @@ TEST_F(SpaceTest, AllocAndFreeList) { ASSERT_TRUE(space != NULL); // Make space findable to the heap, will also delete space when runtime is cleaned up - Runtime::Current()->GetHeap()->AddSpace(space); + AddContinuousSpace(space); Thread* self = Thread::Current(); // Succeeds, fits without adjusting the max allowed footprint. @@ -384,7 +389,7 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size) { EXPECT_EQ(space->NonGrowthLimitCapacity(), capacity); // Make space findable to the heap, will also delete space when runtime is cleaned up - Runtime::Current()->GetHeap()->AddSpace(space); + AddContinuousSpace(space); // In this round we don't allocate with growth and therefore can't grow past the initial size. // This effectively makes the growth_limit the initial_size, so assert this. @@ -419,4 +424,6 @@ TEST_SizeFootPrintGrowthLimitAndTrim(1MB, 1 * MB) TEST_SizeFootPrintGrowthLimitAndTrim(4MB, 4 * MB) TEST_SizeFootPrintGrowthLimitAndTrim(8MB, 8 * MB) +} // namespace space +} // namespace gc } // namespace art diff --git a/src/gc/sticky_mark_sweep.cc b/src/gc/sticky_mark_sweep.cc deleted file mode 100644 index 988d4e79e7..0000000000 --- a/src/gc/sticky_mark_sweep.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "heap.h" -#include "large_object_space.h" -#include "space.h" -#include "sticky_mark_sweep.h" -#include "thread.h" - -namespace art { - -StickyMarkSweep::StickyMarkSweep(Heap* heap, bool is_concurrent) - : PartialMarkSweep(heap, is_concurrent) { - cumulative_timings_.SetName(GetName()); -} - -StickyMarkSweep::~StickyMarkSweep() { - -} - -void StickyMarkSweep::BindBitmaps() { - PartialMarkSweep::BindBitmaps(); - - Spaces& spaces = GetHeap()->GetSpaces(); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - // For sticky GC, we want to bind the bitmaps of both the zygote space and the alloc space. - // This lets us start with the mark bitmap of the previous garbage collection as the current - // mark bitmap of the alloc space. After the sticky GC finishes, we then unbind the bitmaps, - // making it so that the live bitmap of the alloc space is contains the newly marked objects - // from the sticky GC. - for (Spaces::iterator it = spaces.begin(); it != spaces.end(); ++it) { - if ((*it)->GetGcRetentionPolicy() == kGcRetentionPolicyAlwaysCollect) { - BindLiveToMarkBitmap(*it); - } - } - - GetHeap()->GetLargeObjectsSpace()->CopyLiveToMarked(); -} - -void StickyMarkSweep::MarkReachableObjects() { - DisableFinger(); - RecursiveMarkDirtyObjects(CardTable::kCardDirty - 1); -} - -void StickyMarkSweep::Sweep(TimingLogger& timings, bool swap_bitmaps) { - ObjectStack* live_stack = GetHeap()->GetLiveStack(); - SweepArray(timings_, live_stack, false); - timings_.AddSplit("SweepArray"); -} - -} // namespace art diff --git a/src/hprof/hprof.cc b/src/hprof/hprof.cc index 7539066c85..d66ec7933b 100644 --- a/src/hprof/hprof.cc +++ b/src/hprof/hprof.cc @@ -44,8 +44,10 @@ #include "common_throws.h" #include "debugger.h" #include "dex_file-inl.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/heap.h" +#include "gc/space/space.h" #include "globals.h" -#include "heap.h" #include "mirror/class.h" #include "mirror/class-inl.h" #include "mirror/field.h" @@ -55,7 +57,6 @@ #include "os.h" #include "safe_map.h" #include "scoped_thread_state_change.h" -#include "gc/space.h" #include "thread_list.h" namespace art { @@ -412,7 +413,7 @@ class Hprof { LOCKS_EXCLUDED(Locks::heap_bitmap_lock_) { // Walk the roots and the heap. current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME); - Runtime::Current()->VisitRoots(RootVisitor, this); + Runtime::Current()->VisitRoots(RootVisitor, this, false, false); Thread* self = Thread::Current(); { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); diff --git a/src/image_test.cc b/src/image_test.cc index 8066a90957..9f86a1aedf 100644 --- a/src/image_test.cc +++ b/src/image_test.cc @@ -22,7 +22,7 @@ #include "image_writer.h" #include "oat_writer.h" #include "signal_catcher.h" -#include "gc/space.h" +#include "gc/space/image_space.h" #include "UniquePtr.h" #include "utils.h" #include "vector_output_stream.h" @@ -43,27 +43,19 @@ TEST_F(ImageTest, WriteRead) { { std::vector<uint8_t> oat_contents; { + jobject class_loader = NULL; + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath()); + ScopedObjectAccess soa(Thread::Current()); - std::vector<const DexFile*> dex_files; - dex_files.push_back(java_lang_dex_file_); - dex_files.push_back(conscrypt_file_); VectorOutputStream output_stream(tmp_elf.GetFilename(), oat_contents); - bool success_oat = OatWriter::Create(output_stream, dex_files, 0, 0, "", *compiler_driver_.get()); + bool success_oat = OatWriter::Create(output_stream, class_linker->GetBootClassPath(), + 0, 0, "", *compiler_driver_.get()); ASSERT_TRUE(success_oat); - // Force all system classes into memory - for (size_t dex_file_index = 0; dex_file_index < dex_files.size(); ++dex_file_index) { - const DexFile* dex_file = dex_files[dex_file_index]; - for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const char* descriptor = dex_file->GetClassDescriptor(class_def); - mirror::Class* klass = class_linker_->FindSystemClass(descriptor); - EXPECT_TRUE(klass != NULL) << descriptor; - } - } bool success_elf = compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, - dex_files, + class_linker->GetBootClassPath(), oat_contents, tmp_elf.GetFile()); ASSERT_TRUE(success_elf); @@ -76,10 +68,9 @@ TEST_F(ImageTest, WriteRead) { ScratchFile tmp_image; const uintptr_t requested_image_base = ART_BASE_ADDRESS; { - ImageWriter writer(NULL); + ImageWriter writer(*compiler_driver_.get()); bool success_image = writer.Write(tmp_image.GetFilename(), requested_image_base, - tmp_oat->GetPath(), tmp_oat->GetPath(), - *compiler_driver_.get()); + tmp_oat->GetPath(), tmp_oat->GetPath()); ASSERT_TRUE(success_image); bool success_fixup = compiler_driver_->FixupElf(tmp_oat.get(), writer.GetOatDataBegin()); ASSERT_TRUE(success_fixup); @@ -92,15 +83,18 @@ TEST_F(ImageTest, WriteRead) { file->ReadFully(&image_header, sizeof(image_header)); ASSERT_TRUE(image_header.IsValid()); - Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_EQ(1U, heap->GetSpaces().size()); - ContinuousSpace* space = heap->GetSpaces().front(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + ASSERT_EQ(1U, heap->GetContinuousSpaces().size()); + gc::space::ContinuousSpace* space = heap->GetContinuousSpaces().front(); ASSERT_FALSE(space->IsImageSpace()); ASSERT_TRUE(space != NULL); - ASSERT_TRUE(space->IsAllocSpace()); + ASSERT_TRUE(space->IsDlMallocSpace()); ASSERT_GE(sizeof(image_header) + space->Size(), static_cast<size_t>(file->GetLength())); } + ASSERT_TRUE(compiler_driver_->GetImageClasses() != NULL); + CompilerDriver::DescriptorSet image_classes(*compiler_driver_->GetImageClasses()); + // Need to delete the compiler since it has worker threads which are attached to runtime. compiler_driver_.reset(); @@ -131,14 +125,14 @@ TEST_F(ImageTest, WriteRead) { ASSERT_TRUE(runtime_.get() != NULL); class_linker_ = runtime_->GetClassLinker(); - Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_EQ(2U, heap->GetSpaces().size()); - ASSERT_TRUE(heap->GetSpaces()[0]->IsImageSpace()); - ASSERT_FALSE(heap->GetSpaces()[0]->IsAllocSpace()); - ASSERT_FALSE(heap->GetSpaces()[1]->IsImageSpace()); - ASSERT_TRUE(heap->GetSpaces()[1]->IsAllocSpace()); + gc::Heap* heap = Runtime::Current()->GetHeap(); + ASSERT_EQ(2U, heap->GetContinuousSpaces().size()); + ASSERT_TRUE(heap->GetContinuousSpaces()[0]->IsImageSpace()); + ASSERT_FALSE(heap->GetContinuousSpaces()[0]->IsDlMallocSpace()); + ASSERT_FALSE(heap->GetContinuousSpaces()[1]->IsImageSpace()); + ASSERT_TRUE(heap->GetContinuousSpaces()[1]->IsDlMallocSpace()); - ImageSpace* image_space = heap->GetImageSpace(); + gc::space::ImageSpace* image_space = heap->GetImageSpace(); byte* image_begin = image_space->Begin(); byte* image_end = image_space->End(); CHECK_EQ(requested_image_base, reinterpret_cast<uintptr_t>(image_begin)); @@ -148,7 +142,13 @@ TEST_F(ImageTest, WriteRead) { mirror::Class* klass = class_linker_->FindSystemClass(descriptor); EXPECT_TRUE(klass != NULL) << descriptor; EXPECT_LT(image_begin, reinterpret_cast<byte*>(klass)) << descriptor; - EXPECT_LT(reinterpret_cast<byte*>(klass), image_end) << descriptor; + if (image_classes.find(descriptor) != image_classes.end()) { + // image classes should be located before the end of the image. + EXPECT_LT(reinterpret_cast<byte*>(klass), image_end) << descriptor; + } else { + // non image classes should be in a space after the image. + EXPECT_GT(reinterpret_cast<byte*>(klass), image_end) << descriptor; + } EXPECT_EQ(*klass->GetRawLockWordAddress(), 0); // address should have been removed from monitor } } diff --git a/src/image_writer.cc b/src/image_writer.cc index a989a4e0a3..f0b49be8a2 100644 --- a/src/image_writer.cc +++ b/src/image_writer.cc @@ -26,17 +26,18 @@ #include "compiled_method.h" #include "compiler/driver/compiler_driver.h" #include "dex_file-inl.h" -#include "gc/card_table-inl.h" -#include "gc/large_object_space.h" -#include "gc/space.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/heap.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" #include "globals.h" -#include "heap.h" #include "image.h" #include "intern_table.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" -#include "mirror/dex_cache.h" +#include "mirror/dex_cache-inl.h" #include "mirror/field-inl.h" #include "mirror/abstract_method-inl.h" #include "mirror/object-inl.h" @@ -57,16 +58,12 @@ namespace art { bool ImageWriter::Write(const std::string& image_filename, uintptr_t image_begin, const std::string& oat_filename, - const std::string& oat_location, - const CompilerDriver& compiler_driver) { + const std::string& oat_location) { CHECK(!image_filename.empty()); CHECK_NE(image_begin, 0U); image_begin_ = reinterpret_cast<byte*>(image_begin); - Heap* heap = Runtime::Current()->GetHeap(); - const Spaces& spaces = heap->GetSpaces(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const std::vector<DexCache*>& all_dex_caches = class_linker->GetDexCaches(); for (size_t i = 0; i < all_dex_caches.size(); i++) { @@ -81,6 +78,10 @@ bool ImageWriter::Write(const std::string& image_filename, } oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location); class_linker->RegisterOatFile(*oat_file_); + interpreter_to_interpreter_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToInterpreterEntryOffset(); + interpreter_to_quick_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToQuickEntryOffset(); + portable_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset(); + quick_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset(); { Thread::Current()->TransitionFromSuspendedToRunnable(); @@ -89,12 +90,16 @@ bool ImageWriter::Write(const std::string& image_filename, ComputeEagerResolvedStrings(); Thread::Current()->TransitionFromRunnableToSuspended(kNative); } - heap->CollectGarbage(false); // Remove garbage - // Trim size of alloc spaces + gc::Heap* heap = Runtime::Current()->GetHeap(); + heap->CollectGarbage(false); // Remove garbage. + // Trim size of alloc spaces. + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); // TODO: C++0x auto - for (Spaces::const_iterator cur = spaces.begin(); cur != spaces.end(); ++cur) { - if ((*cur)->IsAllocSpace()) { - (*cur)->AsAllocSpace()->Trim(); + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + space->AsDlMallocSpace()->Trim(); } } @@ -110,10 +115,10 @@ bool ImageWriter::Write(const std::string& image_filename, Thread::Current()->TransitionFromSuspendedToRunnable(); size_t oat_loaded_size = 0; size_t oat_data_offset = 0; - compiler_driver.GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset); + compiler_driver_.GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset); CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset); CopyAndFixupObjects(); - PatchOatCodeAndMethods(compiler_driver); + PatchOatCodeAndMethods(); Thread::Current()->TransitionFromRunnableToSuspended(kNative); UniquePtr<File> image_file(OS::OpenFile(image_filename.c_str(), true)); @@ -134,11 +139,15 @@ bool ImageWriter::Write(const std::string& image_filename, } bool ImageWriter::AllocMemory() { - const Spaces& spaces = Runtime::Current()->GetHeap()->GetSpaces(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); size_t size = 0; - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - if ((*it)->IsAllocSpace()) { - size += (*it)->Size(); + // TODO: C++0x auto + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + size += space->Size(); } } @@ -168,13 +177,13 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { return; } String* string = obj->AsString(); - std::string utf8_string(string->ToModifiedUtf8()); + const uint16_t* utf16_string = string->GetCharArray()->GetData() + string->GetOffset(); ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg); typedef Set::const_iterator CacheIt; // TODO: C++0x auto for (CacheIt it = writer->dex_caches_.begin(), end = writer->dex_caches_.end(); it != end; ++it) { DexCache* dex_cache = *it; const DexFile& dex_file = *dex_cache->GetDexFile(); - const DexFile::StringId* string_id = dex_file.FindStringId(utf8_string); + const DexFile::StringId* string_id = dex_file.FindStringId(utf16_string); if (string_id != NULL) { // This string occurs in this dex file, assign the dex cache entry. uint32_t string_idx = dex_file.GetIndexForStringId(*string_id); @@ -188,49 +197,28 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { void ImageWriter::ComputeEagerResolvedStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // TODO: Check image spaces only? - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); heap->FlushAllocStack(); heap->GetLiveBitmap()->Walk(ComputeEagerResolvedStringsCallback, this); } bool ImageWriter::IsImageClass(const Class* klass) { - if (image_classes_ == NULL) { - return true; - } - while (klass->IsArrayClass()) { - klass = klass->GetComponentType(); - } - if (klass->IsPrimitive()) { - return true; - } - const std::string descriptor(ClassHelper(klass).GetDescriptor()); - return image_classes_->find(descriptor) != image_classes_->end(); + return compiler_driver_.IsImageClass(ClassHelper(klass).GetDescriptor()); } - struct NonImageClasses { ImageWriter* image_writer; std::set<std::string>* non_image_classes; }; void ImageWriter::PruneNonImageClasses() { - if (image_classes_ == NULL) { + if (compiler_driver_.GetImageClasses() == NULL) { return; } Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - // Update image_classes_ with classes for objects created by <clinit> methods. - Thread* self = Thread::Current(); - const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); - Heap* heap = Runtime::Current()->GetHeap(); - // TODO: Image spaces only? - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this); - self->EndAssertNoThreadSuspension(old_cause); - // Make a list of classes we would like to prune. std::set<std::string> non_image_classes; NonImageClasses context; @@ -271,28 +259,6 @@ void ImageWriter::PruneNonImageClasses() { } } -void ImageWriter::FindClinitImageClassesCallback(Object* object, void* arg) { - DCHECK(object != NULL); - DCHECK(arg != NULL); - ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); - Class* klass = object->GetClass(); - while (klass->IsArrayClass()) { - klass = klass->GetComponentType(); - } - if (klass->IsPrimitive()) { - return; - } - while (!klass->IsObjectClass()) { - ClassHelper kh(klass); - const char* descriptor = kh.GetDescriptor(); - std::pair<DescriptorSet::iterator, bool> result = image_writer->image_classes_->insert(descriptor); - if (result.second) { - LOG(INFO) << "Adding " << descriptor << " to image classes"; - } - klass = klass->GetSuperClass(); - } -} - bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { NonImageClasses* context = reinterpret_cast<NonImageClasses*>(arg); if (!context->image_writer->IsImageClass(klass)) { @@ -303,11 +269,11 @@ bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { void ImageWriter::CheckNonImageClassesRemoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (image_classes_ == NULL) { + if (compiler_driver_.GetImageClasses() == NULL) { return; } - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); Thread* self = Thread::Current(); { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); @@ -332,8 +298,10 @@ void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) { } void ImageWriter::DumpImageClasses() { + CompilerDriver::DescriptorSet* image_classes = compiler_driver_.GetImageClasses(); + CHECK(image_classes != NULL); typedef std::set<std::string>::const_iterator It; // TODO: C++0x auto - for (It it = image_classes_->begin(), end = image_classes_->end(); it != end; ++it) { + for (It it = image_classes->begin(), end = image_classes->end(); it != end; ++it) { LOG(INFO) << " " << *it; } } @@ -410,8 +378,8 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d Thread* self = Thread::Current(); SirtRef<ObjectArray<Object> > image_roots(self, CreateImageRoots()); - Heap* heap = Runtime::Current()->GetHeap(); - const Spaces& spaces = heap->GetSpaces(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); DCHECK(!spaces.empty()); DCHECK_EQ(0U, image_end_); @@ -426,8 +394,11 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d // TODO: Add InOrderWalk to heap bitmap. const char* old = self->StartAssertNoThreadSuspension("ImageWriter"); DCHECK(heap->GetLargeObjectsSpace()->GetLiveObjects()->IsEmpty()); - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - (*it)->GetLiveBitmap()->InOrderWalk(CalculateNewObjectOffsetsCallback, this); + // TODO: C++0x auto + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::ContinuousSpace* space = *it; + space->GetLiveBitmap()->InOrderWalk(CalculateNewObjectOffsetsCallback, this); DCHECK_LT(image_end_, image_->Size()); } self->EndAssertNoThreadSuspension(old); @@ -455,7 +426,7 @@ void ImageWriter::CopyAndFixupObjects() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Thread* self = Thread::Current(); const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); // TODO: heap validation can't handle this fix up pass heap->DisableObjectValidation(); // TODO: Image spaces only? @@ -512,17 +483,34 @@ void ImageWriter::FixupMethod(const AbstractMethod* orig, AbstractMethod* copy) if (orig->IsAbstract()) { // Code for abstract methods is set to the abstract method error stub when we load the image. copy->SetEntryPointFromCompiledCode(NULL); + copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*> + (GetOatAddress(interpreter_to_interpreter_entry_offset_))); return; + } else { + copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*> + (GetOatAddress(interpreter_to_quick_entry_offset_))); } if (orig == Runtime::Current()->GetResolutionMethod()) { - // The resolution method's code is set to the resolution trampoline when we load the image. - copy->SetEntryPointFromCompiledCode(NULL); +#if defined(ART_USE_PORTABLE_COMPILER) + copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_)); +#else + copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_)); +#endif return; } - // Non-abstract methods have code - copy->SetEntryPointFromCompiledCode(GetOatAddress(orig->GetOatCodeOffset())); + // Use original code if it exists. Otherwise, set the code pointer to the resolution trampoline. + const byte* code = GetOatAddress(orig->GetOatCodeOffset()); + if (code != NULL) { + copy->SetEntryPointFromCompiledCode(code); + } else { +#if defined(ART_USE_PORTABLE_COMPILER) + copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_)); +#else + copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_)); +#endif + } if (orig->IsNative()) { // The native method's pointer is set to a stub to lookup via dlsym when we load the image. @@ -638,13 +626,13 @@ static AbstractMethod* GetTargetMethod(const CompilerDriver::PatchInformation* p return method; } -void ImageWriter::PatchOatCodeAndMethods(const CompilerDriver& compiler) { +void ImageWriter::PatchOatCodeAndMethods() { Thread* self = Thread::Current(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); typedef std::vector<const CompilerDriver::PatchInformation*> Patches; - const Patches& code_to_patch = compiler.GetCodeToPatch(); + const Patches& code_to_patch = compiler_driver_.GetCodeToPatch(); for (size_t i = 0; i < code_to_patch.size(); i++) { const CompilerDriver::PatchInformation* patch = code_to_patch[i]; AbstractMethod* target = GetTargetMethod(patch); @@ -654,7 +642,7 @@ void ImageWriter::PatchOatCodeAndMethods(const CompilerDriver& compiler) { SetPatchLocation(patch, reinterpret_cast<uint32_t>(GetOatAddress(code_offset))); } - const Patches& methods_to_patch = compiler.GetMethodsToPatch(); + const Patches& methods_to_patch = compiler_driver_.GetMethodsToPatch(); for (size_t i = 0; i < methods_to_patch.size(); i++) { const CompilerDriver::PatchInformation* patch = methods_to_patch[i]; AbstractMethod* target = GetTargetMethod(patch); diff --git a/src/image_writer.h b/src/image_writer.h index 30a7f7f5da..b79cb2f0c2 100644 --- a/src/image_writer.h +++ b/src/image_writer.h @@ -29,7 +29,7 @@ #include "mirror/dex_cache.h" #include "os.h" #include "safe_map.h" -#include "gc/space.h" +#include "gc/space/space.h" #include "UniquePtr.h" namespace art { @@ -37,18 +37,18 @@ namespace art { // Write a Space built during compilation for use during execution. class ImageWriter { public: - typedef std::set<std::string> DescriptorSet; - explicit ImageWriter(DescriptorSet* image_classes) - : oat_file_(NULL), image_end_(0), image_begin_(NULL), image_classes_(image_classes), - oat_data_begin_(NULL) {} + explicit ImageWriter(const CompilerDriver& compiler_driver) + : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL), + oat_data_begin_(NULL), interpreter_to_interpreter_entry_offset_(0), + interpreter_to_quick_entry_offset_(0), portable_resolution_trampoline_offset_(0), + quick_resolution_trampoline_offset_(0) {} ~ImageWriter() {} bool Write(const std::string& image_filename, uintptr_t image_begin, const std::string& oat_filename, - const std::string& oat_location, - const CompilerDriver& compiler_driver) + const std::string& oat_location) LOCKS_EXCLUDED(Locks::mutator_lock_); uintptr_t GetOatDataBegin() { @@ -130,8 +130,6 @@ class ImageWriter { // Remove unwanted classes from various roots. void PruneNonImageClasses() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void FindClinitImageClassesCallback(mirror::Object* object, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static bool NonImageClassesVisitor(mirror::Class* c, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -170,12 +168,14 @@ class ImageWriter { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Patches references in OatFile to expect runtime addresses. - void PatchOatCodeAndMethods(const CompilerDriver& compiler) + void PatchOatCodeAndMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const CompilerDriver& compiler_driver_; + // Map of Object to where it will be at runtime. SafeMap<const mirror::Object*, size_t> offsets_; @@ -191,13 +191,16 @@ class ImageWriter { // Beginning target image address for the output image. byte* image_begin_; - // Set of classes to be include in the image, or NULL for all. - DescriptorSet* image_classes_; - // Beginning target oat address for the pointers from the output image to its oat file. const byte* oat_data_begin_; - // DexCaches seen while scanning for fixing up CodeAndDirectMethods. + // Offset from oat_data_begin_ to the stubs. + uint32_t interpreter_to_interpreter_entry_offset_; + uint32_t interpreter_to_quick_entry_offset_; + uint32_t portable_resolution_trampoline_offset_; + uint32_t quick_resolution_trampoline_offset_; + + // DexCaches seen while scanning for fixing up CodeAndDirectMethods typedef std::set<mirror::DexCache*> Set; Set dex_caches_; }; diff --git a/src/instrumentation.cc b/src/instrumentation.cc index 39fd37766a..8af0885ef4 100644 --- a/src/instrumentation.cc +++ b/src/instrumentation.cc @@ -62,7 +62,7 @@ bool Instrumentation::InstallStubsForClass(mirror::Class* klass) { if (is_initialized || !method->IsStatic() || method->IsConstructor()) { new_code = class_linker->GetOatCodeFor(method); } else { - new_code = GetResolutionTrampoline(); + new_code = GetResolutionTrampoline(class_linker); } } else { // !uninstall if (!interpreter_stubs_installed_ || method->IsNative()) { @@ -380,7 +380,7 @@ const void* Instrumentation::GetQuickCodeFor(const mirror::AbstractMethod* metho if (LIKELY(!instrumentation_stubs_installed_)) { const void* code = method->GetEntryPointFromCompiledCode(); DCHECK(code != NULL); - if (LIKELY(code != GetResolutionTrampoline())) { + if (LIKELY(code != GetResolutionTrampoline(runtime->GetClassLinker()))) { return code; } } diff --git a/src/instrumentation.h b/src/instrumentation.h index e6fa251076..e79c75efac 100644 --- a/src/instrumentation.h +++ b/src/instrumentation.h @@ -137,12 +137,24 @@ class Instrumentation { return instrumentation_stubs_installed_; } + bool HasMethodEntryListeners() const { + return have_method_entry_listeners_; + } + + bool HasMethodExitListeners() const { + return have_method_exit_listeners_; + } + + bool HasDexPcListeners() const { + return have_dex_pc_listeners_; + } + // Inform listeners that a method has been entered. A dex PC is provided as we may install // listeners into executing code and get method enter events for methods already on the stack. void MethodEnterEvent(Thread* thread, mirror::Object* this_object, const mirror::AbstractMethod* method, uint32_t dex_pc) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (have_method_entry_listeners_) { + if (UNLIKELY(HasMethodEntryListeners())) { MethodEnterEventImpl(thread, this_object, method, dex_pc); } } @@ -152,7 +164,7 @@ class Instrumentation { const mirror::AbstractMethod* method, uint32_t dex_pc, const JValue& return_value) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (have_method_exit_listeners_) { + if (UNLIKELY(HasMethodExitListeners())) { MethodExitEventImpl(thread, this_object, method, dex_pc, return_value); } } @@ -166,7 +178,7 @@ class Instrumentation { void DexPcMovedEvent(Thread* thread, mirror::Object* this_object, const mirror::AbstractMethod* method, uint32_t dex_pc) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (UNLIKELY(have_dex_pc_listeners_)) { + if (UNLIKELY(HasDexPcListeners())) { DexPcMovedEventImpl(thread, this_object, method, dex_pc); } } diff --git a/src/intern_table.cc b/src/intern_table.cc index fa3c0753a9..d1ad2dbadc 100644 --- a/src/intern_table.cc +++ b/src/intern_table.cc @@ -38,13 +38,15 @@ void InternTable::DumpForSigQuit(std::ostream& os) const { << image_strong_interns_.size() << " image strong\n"; } -void InternTable::VisitRoots(RootVisitor* visitor, void* arg) { +void InternTable::VisitRoots(RootVisitor* visitor, void* arg, bool clean_dirty) { MutexLock mu(Thread::Current(), intern_table_lock_); typedef Table::const_iterator It; // TODO: C++0x auto for (It it = strong_interns_.begin(), end = strong_interns_.end(); it != end; ++it) { visitor(it->second, arg); } - is_dirty_ = false; + if (clean_dirty) { + is_dirty_ = false; + } // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots. } diff --git a/src/intern_table.h b/src/intern_table.h index 3018317e0f..1ff4f6d3c6 100644 --- a/src/intern_table.h +++ b/src/intern_table.h @@ -66,7 +66,7 @@ class InternTable { size_t Size() const; - void VisitRoots(RootVisitor* visitor, void* arg); + void VisitRoots(RootVisitor* visitor, void* arg, bool clean_dirty); void DumpForSigQuit(std::ostream& os) const; diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index e2cc3d63aa..1e8ee9c63c 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -23,7 +23,8 @@ #include "common_throws.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" -#include "gc/card_table-inl.h" +#include "dex_instruction.h" +#include "gc/accounting/card_table-inl.h" #include "invoke_arg_array_builder.h" #include "nth_caller_visitor.h" #include "mirror/class.h" @@ -50,12 +51,13 @@ static const int32_t kMinInt = std::numeric_limits<int32_t>::min(); static const int64_t kMaxLong = std::numeric_limits<int64_t>::max(); static const int64_t kMinLong = std::numeric_limits<int64_t>::min(); -static void UnstartedRuntimeInvoke(Thread* self, AbstractMethod* target_method, - ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) +static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, + JValue* result, size_t arg_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // In a runtime that's not started we intercept certain methods to avoid complicated dependency // problems in core libraries. - std::string name(PrettyMethod(target_method)); + std::string name(PrettyMethod(shadow_frame->GetMethod())); if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str())); ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); @@ -132,7 +134,7 @@ static void UnstartedRuntimeInvoke(Thread* self, AbstractMethod* target_method, } } else { // Not special, continue with regular interpreter execution. - EnterInterpreterFromInterpreter(self, shadow_frame, result); + artInterpreterToInterpreterEntry(self, mh, code_item, shadow_frame, result); } } @@ -383,43 +385,133 @@ static void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS { ref->MonitorExit(self); } -static void DoInvoke(Thread* self, MethodHelper& mh, ShadowFrame& shadow_frame, - const Instruction* inst, InvokeType type, bool is_range, - JValue* result) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template<InvokeType type, bool is_range, bool do_access_check> +static void DoInvoke(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) NO_THREAD_SAFETY_ANALYSIS; + +template<InvokeType type, bool is_range, bool do_access_check> +static void DoInvoke(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) { + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - Object* receiver; - if (type == kStatic) { - receiver = NULL; + Object* receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC); + AbstractMethod* method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self, + do_access_check, type); + if (UNLIKELY(method == NULL)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return; + } + + MethodHelper mh(method); + const DexFile::CodeItem* code_item = mh.GetCodeItem(); + uint16_t num_regs; + uint16_t num_ins; + if (LIKELY(code_item != NULL)) { + num_regs = code_item->registers_size_; + num_ins = code_item->ins_size_; + } else if (method->IsAbstract()) { + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;", + "abstract method \"%s\"", PrettyMethod(method).c_str()); + return; } else { - receiver = shadow_frame.GetVRegReference(vregC); + DCHECK(method->IsNative() || method->IsProxyMethod()); + num_regs = num_ins = AbstractMethod::NumArgRegisters(mh.GetShorty()); + if (!method->IsStatic()) { + num_regs++; + num_ins++; + } } - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - AbstractMethod* target_method = FindMethodFromCode(method_idx, receiver, - shadow_frame.GetMethod(), - self, true, type); - if (UNLIKELY(target_method == NULL)) { + + void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); + ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory)); + size_t cur_reg = num_regs - num_ins; + if (receiver != NULL) { + new_shadow_frame->SetVRegReference(cur_reg, receiver); + ++cur_reg; + } + + size_t arg_offset = (receiver == NULL) ? 0 : 1; + const char* shorty = mh.GetShorty(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } + for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { + DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); + size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; + switch (shorty[shorty_pos + 1]) { + case 'L': { + Object* o = shadow_frame.GetVRegReference(arg_pos); + new_shadow_frame->SetVRegReference(cur_reg, o); + break; + } + case 'J': case 'D': { + uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) | + static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos)); + new_shadow_frame->SetVRegLong(cur_reg, wide_value); + cur_reg++; + arg_offset++; + break; + } + default: + new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); + break; + } + } + + if (LIKELY(Runtime::Current()->IsStarted())) { + (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); + } else { + UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); + } +} + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template<bool is_range> +static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) + NO_THREAD_SAFETY_ANALYSIS; + +template<bool is_range> +static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) { + uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); + Object* receiver = shadow_frame.GetVRegReference(vregC); + if (UNLIKELY(receiver == NULL)) { + // We lost the reference to the method index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return; + } + uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + AbstractMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx); + if (UNLIKELY(method == NULL)) { CHECK(self->IsExceptionPending()); result->SetJ(0); return; } - MethodHelper target_mh(target_method); + MethodHelper mh(method); - const DexFile::CodeItem* code_item = target_mh.GetCodeItem(); + const DexFile::CodeItem* code_item = mh.GetCodeItem(); uint16_t num_regs; uint16_t num_ins; if (code_item != NULL) { num_regs = code_item->registers_size_; num_ins = code_item->ins_size_; - } else if (target_method->IsAbstract()) { + } else if (method->IsAbstract()) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;", - "abstract method \"%s\"", PrettyMethod(target_method).c_str()); + "abstract method \"%s\"", PrettyMethod(method).c_str()); return; } else { - DCHECK(target_method->IsNative() || target_method->IsProxyMethod()); - num_regs = num_ins = AbstractMethod::NumArgRegisters(target_mh.GetShorty()); - if (!target_method->IsStatic()) { + DCHECK(method->IsNative() || method->IsProxyMethod()); + num_regs = num_ins = AbstractMethod::NumArgRegisters(mh.GetShorty()); + if (!method->IsStatic()) { num_regs++; num_ins++; } @@ -427,7 +519,7 @@ static void DoInvoke(Thread* self, MethodHelper& mh, ShadowFrame& shadow_frame, void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, - target_method, 0, memory)); + method, 0, memory)); size_t cur_reg = num_regs - num_ins; if (receiver != NULL) { new_shadow_frame->SetVRegReference(cur_reg, receiver); @@ -435,13 +527,13 @@ static void DoInvoke(Thread* self, MethodHelper& mh, ShadowFrame& shadow_frame, } size_t arg_offset = (receiver == NULL) ? 0 : 1; - const char* shorty = target_mh.GetShorty(); + const char* shorty = mh.GetShorty(); uint32_t arg[5]; if (!is_range) { inst->GetArgs(arg); } for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { - DCHECK_LT(shorty_pos + 1, target_mh.GetShortyLength()); + DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; switch (shorty[shorty_pos + 1]) { case 'L': { @@ -464,24 +556,33 @@ static void DoInvoke(Thread* self, MethodHelper& mh, ShadowFrame& shadow_frame, } if (LIKELY(Runtime::Current()->IsStarted())) { - (target_method->GetEntryPointFromInterpreter())(self, new_shadow_frame, result); + (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); } else { - UnstartedRuntimeInvoke(self, target_method, new_shadow_frame, result, num_regs - num_ins); + UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); } } +// We use template functions to optimize compiler inlining process. Otherwise, +// some parts of the code (like a switch statement) which depend on a constant +// parameter would not be inlined while it should be. These constant parameters +// are now part of the template arguments. +// Note these template functions are static and inlined so they should not be +// part of the final object file. +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check> static void DoFieldGet(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, FindFieldType find_type, - Primitive::Type field_type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE; + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; +template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check> static inline void DoFieldGet(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, FindFieldType find_type, - Primitive::Type field_type) { + const Instruction* inst) { bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead); uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); Field* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, - find_type, Primitive::FieldSize(field_type)); + find_type, Primitive::FieldSize(field_type), + do_access_check); if (UNLIKELY(f == NULL)) { CHECK(self->IsExceptionPending()); return; @@ -524,18 +625,56 @@ static inline void DoFieldGet(Thread* self, ShadowFrame& shadow_frame, } } -static void DoFieldPut(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, FindFieldType find_type, - Primitive::Type field_type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE; +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template<Primitive::Type field_type> +static void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template<Primitive::Type field_type> +static inline void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + // We lost the reference to the field index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return; + } + MemberOffset field_offset(inst->VRegC_22c()); + const bool is_volatile = false; // iget-x-quick only on non volatile fields. + const uint32_t vregA = inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimInt: + shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset, is_volatile))); + break; + case Primitive::kPrimLong: + shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset, is_volatile))); + break; + case Primitive::kPrimNot: + shadow_frame.SetVRegReference(vregA, obj->GetFieldObject<mirror::Object*>(field_offset, is_volatile)); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } +} + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check> +static void DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; -static inline void DoFieldPut(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, FindFieldType find_type, - Primitive::Type field_type) { +template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check> +static inline void DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, + const Instruction* inst) { bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); Field* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, - find_type, Primitive::FieldSize(field_type)); + find_type, Primitive::FieldSize(field_type), + do_access_check); if (UNLIKELY(f == NULL)) { CHECK(self->IsExceptionPending()); return; @@ -579,6 +718,41 @@ static inline void DoFieldPut(Thread* self, ShadowFrame& shadow_frame, } } +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template<Primitive::Type field_type> +static void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template<Primitive::Type field_type> +static inline void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + // We lost the reference to the field index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return; + } + MemberOffset field_offset(inst->VRegC_22c()); + const bool is_volatile = false; // iput-x-quick only on non volatile fields. + const uint32_t vregA = inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimInt: + obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile); + break; + case Primitive::kPrimLong: + obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile); + break; + case Primitive::kPrimNot: + obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } +} + static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Class* java_lang_string_class = String::GetJavaLangString(); @@ -593,10 +767,11 @@ static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t str return mh.ResolveString(string_idx); } -static inline void DoIntDivide(Thread* self, ShadowFrame& shadow_frame, size_t result_reg, - int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static inline void DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg, + int32_t dividend, int32_t divisor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(divisor == 0)) { - ThrowArithmeticExceptionDivideByZero(self); + ThrowArithmeticExceptionDivideByZero(); } else if (UNLIKELY(dividend == kMinInt && divisor == -1)) { shadow_frame.SetVReg(result_reg, kMinInt); } else { @@ -604,10 +779,11 @@ static inline void DoIntDivide(Thread* self, ShadowFrame& shadow_frame, size_t r } } -static inline void DoIntRemainder(Thread* self, ShadowFrame& shadow_frame, size_t result_reg, - int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static inline void DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg, + int32_t dividend, int32_t divisor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(divisor == 0)) { - ThrowArithmeticExceptionDivideByZero(self); + ThrowArithmeticExceptionDivideByZero(); } else if (UNLIKELY(dividend == kMinInt && divisor == -1)) { shadow_frame.SetVReg(result_reg, 0); } else { @@ -615,10 +791,11 @@ static inline void DoIntRemainder(Thread* self, ShadowFrame& shadow_frame, size_ } } -static inline void DoLongDivide(Thread* self, ShadowFrame& shadow_frame, size_t result_reg, - int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static inline void DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg, + int64_t dividend, int64_t divisor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(divisor == 0)) { - ThrowArithmeticExceptionDivideByZero(self); + ThrowArithmeticExceptionDivideByZero(); } else if (UNLIKELY(dividend == kMinLong && divisor == -1)) { shadow_frame.SetVRegLong(result_reg, kMinLong); } else { @@ -626,10 +803,11 @@ static inline void DoLongDivide(Thread* self, ShadowFrame& shadow_frame, size_t } } -static inline void DoLongRemainder(Thread* self, ShadowFrame& shadow_frame, size_t result_reg, - int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static inline void DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg, + int64_t dividend, int64_t divisor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(divisor == 0)) { - ThrowArithmeticExceptionDivideByZero(self); + ThrowArithmeticExceptionDivideByZero(); } else if (UNLIKELY(dividend == kMinLong && divisor == -1)) { shadow_frame.SetVRegLong(result_reg, 0); } else { @@ -637,6 +815,111 @@ static inline void DoLongRemainder(Thread* self, ShadowFrame& shadow_frame, size } } +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +// Returns true on success, otherwise throws an exception and returns false. +template <bool is_range, bool do_access_check> +static bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, + Thread* self, JValue* result) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template <bool is_range, bool do_access_check> +static inline bool DoFilledNewArray(const Instruction* inst, + const ShadowFrame& shadow_frame, + Thread* self, JValue* result) { + DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY || + inst->Opcode() == Instruction::FILLED_NEW_ARRAY_RANGE); + const int32_t length = is_range ? inst->VRegA_3rc() : inst->VRegA_35c(); + if (!is_range) { + // Checks FILLED_NEW_ARRAY's length does not exceed 5 arguments. + CHECK_LE(length, 5); + } + if (UNLIKELY(length < 0)) { + ThrowNegativeArraySizeException(length); + return false; + } + uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + Class* arrayClass = ResolveVerifyAndClinit(type_idx, shadow_frame.GetMethod(), + self, false, do_access_check); + if (UNLIKELY(arrayClass == NULL)) { + DCHECK(self->IsExceptionPending()); + return false; + } + CHECK(arrayClass->IsArrayClass()); + Class* componentClass = arrayClass->GetComponentType(); + if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) { + if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) { + ThrowRuntimeException("Bad filled array request for type %s", + PrettyDescriptor(componentClass).c_str()); + } else { + self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), + "Ljava/lang/InternalError;", + "Found type %s; filled-new-array not implemented for anything but \'int\'", + PrettyDescriptor(componentClass).c_str()); + } + return false; + } + Object* newArray = Array::Alloc(self, arrayClass, length); + if (UNLIKELY(newArray == NULL)) { + DCHECK(self->IsExceptionPending()); + return false; + } + if (is_range) { + uint32_t vregC = inst->VRegC_3rc(); + const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); + for (int32_t i = 0; i < length; ++i) { + if (is_primitive_int_component) { + newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(vregC + i)); + } else { + newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(vregC + i)); + } + } + } else { + uint32_t arg[5]; + inst->GetArgs(arg); + const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); + for (int32_t i = 0; i < length; ++i) { + if (is_primitive_int_component) { + newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(arg[i])); + } else { + newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(arg[i])); + } + } + } + + result->SetL(newArray); + return true; +} + +static inline const Instruction* DoSparseSwitch(const Instruction* inst, + const ShadowFrame& shadow_frame) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(inst->Opcode() == Instruction::SPARSE_SWITCH); + const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t(); + int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t()); + DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature)); + uint16_t size = switch_data[1]; + DCHECK_GT(size, 0); + const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]); + DCHECK(IsAligned<4>(keys)); + const int32_t* entries = keys + size; + DCHECK(IsAligned<4>(entries)); + int lo = 0; + int hi = size - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + int32_t foundVal = keys[mid]; + if (test_val < foundVal) { + hi = mid - 1; + } else if (test_val > foundVal) { + lo = mid + 1; + } else { + return inst->RelativeAt(entries[mid]); + } + } + return inst->Next_3xx(); +} + static inline const Instruction* FindNextInstructionFollowingException(Thread* self, ShadowFrame& shadow_frame, uint32_t dex_pc, @@ -699,11 +982,15 @@ static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh) // Code to run before each dex instruction. #define PREAMBLE() -static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template<bool do_access_check> +static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) __attribute__ ((hot)); + NO_THREAD_SAFETY_ANALYSIS __attribute__ ((hot)); -static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, +template<bool do_access_check> +static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) { if (UNLIKELY(!shadow_frame.HasReferenceArray())) { LOG(FATAL) << "Invalid shadow frame for interpreter use"; @@ -716,12 +1003,14 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c // As the 'this' object won't change during the execution of current code, we // want to cache it in local variables. Nevertheless, in order to let the // garbage collector access it, we store it into sirt references. - SirtRef<Object> this_object_ref(self, shadow_frame.GetThisObject()); + SirtRef<Object> this_object_ref(self, shadow_frame.GetThisObject(code_item->ins_size_)); const Instruction* inst = Instruction::At(insns + shadow_frame.GetDexPC()); if (inst->GetDexPc(insns) == 0) { // We are entering the method as opposed to deoptimizing.. - instrumentation->MethodEnterEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), 0); + if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { + instrumentation->MethodEnterEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), 0); + } } while (true) { if (UNLIKELY(self->TestAllFlags())) { @@ -729,8 +1018,10 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c } const uint32_t dex_pc = inst->GetDexPc(insns); shadow_frame.SetDexPC(dex_pc); - instrumentation->DexPcMovedEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), dex_pc); + if (instrumentation->HasDexPcListeners()) { + instrumentation->DexPcMovedEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), dex_pc); + } const bool kTracing = false; if (kTracing) { #define TRACE_LOG std::cerr @@ -838,8 +1129,11 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::RETURN_VOID: { PREAMBLE(); JValue result; - instrumentation->MethodExitEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), inst->GetDexPc(insns), result); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), inst->GetDexPc(insns), + result); + } return result; } case Instruction::RETURN: { @@ -847,16 +1141,22 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c JValue result; result.SetJ(0); result.SetI(shadow_frame.GetVReg(inst->VRegA_11x())); - instrumentation->MethodExitEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), inst->GetDexPc(insns), result); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), inst->GetDexPc(insns), + result); + } return result; } case Instruction::RETURN_WIDE: { PREAMBLE(); JValue result; result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x())); - instrumentation->MethodExitEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), inst->GetDexPc(insns), result); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), inst->GetDexPc(insns), + result); + } return result; } case Instruction::RETURN_OBJECT: { @@ -864,14 +1164,17 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c JValue result; result.SetJ(0); result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x())); - instrumentation->MethodExitEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), inst->GetDexPc(insns), result); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), inst->GetDexPc(insns), + result); + } return result; } case Instruction::CONST_4: { PREAMBLE(); uint32_t dst = inst->VRegA_11n(); - int32_t val = static_cast<int32_t>(inst->VRegB_11n() << 28) >> 28; + int32_t val = inst->VRegB_11n(); shadow_frame.SetVReg(dst, val); if (val == 0) { shadow_frame.SetVRegReference(dst, NULL); @@ -882,7 +1185,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::CONST_16: { PREAMBLE(); uint32_t dst = inst->VRegA_21s(); - int32_t val = static_cast<int16_t>(inst->VRegB_21s()); + int32_t val = inst->VRegB_21s(); shadow_frame.SetVReg(dst, val); if (val == 0) { shadow_frame.SetVRegReference(dst, NULL); @@ -904,7 +1207,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::CONST_HIGH16: { PREAMBLE(); uint32_t dst = inst->VRegA_21h(); - int32_t val = inst->VRegB_21h() << 16; + int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16); shadow_frame.SetVReg(dst, val); if (val == 0) { shadow_frame.SetVRegReference(dst, NULL); @@ -914,14 +1217,12 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c } case Instruction::CONST_WIDE_16: PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_21s(), - static_cast<int16_t>(inst->VRegB_21s())); + shadow_frame.SetVRegLong(inst->VRegA_21s(), inst->VRegB_21s()); inst = inst->Next_2xx(); break; case Instruction::CONST_WIDE_32: PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_31i(), - static_cast<int32_t>(inst->VRegB_31i())); + shadow_frame.SetVRegLong(inst->VRegA_31i(), inst->VRegB_31i()); inst = inst->Next_3xx(); break; case Instruction::CONST_WIDE: @@ -959,7 +1260,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::CONST_CLASS: { PREAMBLE(); Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, false, true); + self, false, do_access_check); if (UNLIKELY(c == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -995,7 +1296,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::CHECK_CAST: { PREAMBLE(); Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, false, true); + self, false, do_access_check); if (UNLIKELY(c == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -1012,7 +1313,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::INSTANCE_OF: { PREAMBLE(); Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(), - self, false, true); + self, false, do_access_check); if (UNLIKELY(c == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -1028,16 +1329,16 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c if (UNLIKELY(array == NULL)) { ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); - break; + } else { + shadow_frame.SetVReg(inst->VRegA_12x(), array->AsArray()->GetLength()); + inst = inst->Next_1xx(); } - shadow_frame.SetVReg(inst->VRegA_12x(), array->AsArray()->GetLength()); - inst = inst->Next_1xx(); break; } case Instruction::NEW_INSTANCE: { PREAMBLE(); Object* obj = AllocObjectFromCode(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, true); + self, do_access_check); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -1050,7 +1351,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c PREAMBLE(); int32_t length = shadow_frame.GetVReg(inst->VRegB_22c()); Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(), - length, self, true); + length, self, do_access_check); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -1061,97 +1362,23 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c } case Instruction::FILLED_NEW_ARRAY: { PREAMBLE(); - const int32_t length = inst->VRegA_35c(); - CHECK(length <= 5); - if (UNLIKELY(length < 0)) { - ThrowNegativeArraySizeException(length); - HANDLE_PENDING_EXCEPTION(); - break; - } - Class* arrayClass = ResolveVerifyAndClinit(inst->VRegB_35c(), shadow_frame.GetMethod(), - self, false, true); - if (UNLIKELY(arrayClass == NULL)) { - HANDLE_PENDING_EXCEPTION(); - break; - } - CHECK(arrayClass->IsArrayClass()); - Class* componentClass = arrayClass->GetComponentType(); - if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) { - if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) { - ThrowRuntimeException("Bad filled array request for type %s", - PrettyDescriptor(componentClass).c_str()); - } else { - self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), - "Ljava/lang/InternalError;", - "Found type %s; filled-new-array not implemented for anything but \'int\'", - PrettyDescriptor(componentClass).c_str()); - } - HANDLE_PENDING_EXCEPTION(); - break; - } - Object* newArray = Array::Alloc(self, arrayClass, length); - if (UNLIKELY(newArray == NULL)) { - HANDLE_PENDING_EXCEPTION(); - } else { - uint32_t arg[5]; - inst->GetArgs(arg); - const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); - for (int32_t i = 0; i < length; ++i) { - if (is_primitive_int_component) { - newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(arg[i])); - } else { - newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(arg[i])); - } - } - result_register.SetL(newArray); + bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame, + self, &result_register); + if (LIKELY(success)) { inst = inst->Next_3xx(); + } else { + HANDLE_PENDING_EXCEPTION(); } break; } case Instruction::FILLED_NEW_ARRAY_RANGE: { PREAMBLE(); - int32_t length = inst->VRegA_3rc(); - if (UNLIKELY(length < 0)) { - ThrowNegativeArraySizeException(length); - HANDLE_PENDING_EXCEPTION(); - break; - } - Class* arrayClass = ResolveVerifyAndClinit(inst->VRegB_3rc(), shadow_frame.GetMethod(), - self, false, true); - if (UNLIKELY(arrayClass == NULL)) { - HANDLE_PENDING_EXCEPTION(); - break; - } - CHECK(arrayClass->IsArrayClass()); - Class* componentClass = arrayClass->GetComponentType(); - if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) { - if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) { - ThrowRuntimeException("Bad filled array request for type %s", - PrettyDescriptor(componentClass).c_str()); - } else { - self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), - "Ljava/lang/InternalError;", - "Found type %s; filled-new-array not implemented for anything but \'int\'", - PrettyDescriptor(componentClass).c_str()); - } - HANDLE_PENDING_EXCEPTION(); - break; - } - Object* newArray = Array::Alloc(self, arrayClass, length); - if (UNLIKELY(newArray == NULL)) { - HANDLE_PENDING_EXCEPTION(); - } else { - uint32_t vregC = inst->VRegC_3rc(); - const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); - for (int32_t i = 0; i < length; ++i) { - if (is_primitive_int_component) { - newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(vregC + i)); - } else { - newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(vregC + i)); - } - } - result_register.SetL(newArray); + bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame, + self, &result_register); + if (LIKELY(success)) { inst = inst->Next_3xx(); + } else { + HANDLE_PENDING_EXCEPTION(); } break; } @@ -1165,8 +1392,9 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c } Array* array = obj->AsArray(); DCHECK(array->IsArrayInstance() && !array->IsObjectArray()); + const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t(); const Instruction::ArrayDataPayload* payload = - reinterpret_cast<const Instruction::ArrayDataPayload*>(insns + inst->GetDexPc(insns) + inst->VRegB_31t()); + reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr); if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) { self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), "Ljava/lang/ArrayIndexOutOfBoundsException;", @@ -1193,34 +1421,34 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c } case Instruction::GOTO: { PREAMBLE(); - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegA_10t()); + inst = inst->RelativeAt(inst->VRegA_10t()); break; } case Instruction::GOTO_16: { PREAMBLE(); - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegA_20t()); + inst = inst->RelativeAt(inst->VRegA_20t()); break; } case Instruction::GOTO_32: { PREAMBLE(); - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegA_30t()); + inst = inst->RelativeAt(inst->VRegA_30t()); break; } case Instruction::PACKED_SWITCH: { PREAMBLE(); - const uint16_t* switch_data = insns + inst->GetDexPc(insns) + inst->VRegB_31t(); + const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t(); int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t()); DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature)); uint16_t size = switch_data[1]; DCHECK_GT(size, 0); const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]); - CHECK(IsAligned<4>(keys)); + DCHECK(IsAligned<4>(keys)); int32_t first_key = keys[0]; const int32_t* targets = reinterpret_cast<const int32_t*>(&switch_data[4]); DCHECK(IsAligned<4>(targets)); int32_t index = test_val - first_key; if (index >= 0 && index < size) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + targets[index]); + inst = inst->RelativeAt(targets[index]); } else { inst = inst->Next_3xx(); } @@ -1228,31 +1456,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c } case Instruction::SPARSE_SWITCH: { PREAMBLE(); - uint32_t dex_pc = inst->GetDexPc(insns); - const uint16_t* switch_data = insns + dex_pc + inst->VRegB_31t(); - int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t()); - CHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature)); - uint16_t size = switch_data[1]; - CHECK_GT(size, 0); - const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]); - CHECK(IsAligned<4>(keys)); - const int32_t* entries = keys + size; - CHECK(IsAligned<4>(entries)); - int lo = 0; - int hi = size - 1; - inst = inst->Next_3xx(); - while (lo <= hi) { - int mid = (lo + hi) / 2; - int32_t foundVal = keys[mid]; - if (test_val < foundVal) { - hi = mid - 1; - } else if (test_val > foundVal) { - lo = mid + 1; - } else { - inst = Instruction::At(insns + dex_pc + entries[mid]); - break; - } - } + inst = DoSparseSwitch(inst, shadow_frame); break; } case Instruction::CMPL_FLOAT: { @@ -1339,7 +1543,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_EQ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) == shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegC_22t()); + inst = inst->RelativeAt(inst->VRegC_22t()); } else { inst = inst->Next_2xx(); } @@ -1348,7 +1552,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_NE: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegC_22t()); + inst = inst->RelativeAt(inst->VRegC_22t()); } else { inst = inst->Next_2xx(); } @@ -1357,7 +1561,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_LT: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegC_22t()); + inst = inst->RelativeAt(inst->VRegC_22t()); } else { inst = inst->Next_2xx(); } @@ -1366,7 +1570,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_GE: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegC_22t()); + inst = inst->RelativeAt(inst->VRegC_22t()); } else { inst = inst->Next_2xx(); } @@ -1375,7 +1579,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_GT: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegC_22t()); + inst = inst->RelativeAt(inst->VRegC_22t()); } else { inst = inst->Next_2xx(); } @@ -1384,7 +1588,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_LE: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegC_22t()); + inst = inst->RelativeAt(inst->VRegC_22t()); } else { inst = inst->Next_2xx(); } @@ -1393,7 +1597,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_EQZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegB_21t()); + inst = inst->RelativeAt(inst->VRegB_21t()); } else { inst = inst->Next_2xx(); } @@ -1402,7 +1606,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_NEZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegB_21t()); + inst = inst->RelativeAt(inst->VRegB_21t()); } else { inst = inst->Next_2xx(); } @@ -1411,7 +1615,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_LTZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegB_21t()); + inst = inst->RelativeAt(inst->VRegB_21t()); } else { inst = inst->Next_2xx(); } @@ -1420,7 +1624,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_GEZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegB_21t()); + inst = inst->RelativeAt(inst->VRegB_21t()); } else { inst = inst->Next_2xx(); } @@ -1429,7 +1633,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_GTZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegB_21t()); + inst = inst->RelativeAt(inst->VRegB_21t()); } else { inst = inst->Next_2xx(); } @@ -1438,7 +1642,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::IF_LEZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) { - inst = Instruction::At(insns + inst->GetDexPc(insns) + inst->VRegB_21t()); + inst = inst->RelativeAt(inst->VRegB_21t()); } else { inst = inst->Next_2xx(); } @@ -1705,192 +1909,232 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c } case Instruction::IGET_BOOLEAN: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, InstancePrimitiveRead, Primitive::kPrimBoolean); + DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IGET_BYTE: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, InstancePrimitiveRead, Primitive::kPrimByte); + DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IGET_CHAR: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, InstancePrimitiveRead, Primitive::kPrimChar); + DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IGET_SHORT: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, InstancePrimitiveRead, Primitive::kPrimShort); + DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IGET: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, InstancePrimitiveRead, Primitive::kPrimInt); + DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IGET_WIDE: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, InstancePrimitiveRead, Primitive::kPrimLong); + DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IGET_OBJECT: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, InstanceObjectRead, Primitive::kPrimNot); + DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IGET_QUICK: + PREAMBLE(); + DoIGetQuick<Primitive::kPrimInt>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IGET_WIDE_QUICK: + PREAMBLE(); + DoIGetQuick<Primitive::kPrimLong>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IGET_OBJECT_QUICK: + PREAMBLE(); + DoIGetQuick<Primitive::kPrimNot>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SGET_BOOLEAN: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, StaticPrimitiveRead, Primitive::kPrimBoolean); + DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SGET_BYTE: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, StaticPrimitiveRead, Primitive::kPrimByte); + DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SGET_CHAR: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, StaticPrimitiveRead, Primitive::kPrimChar); + DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SGET_SHORT: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, StaticPrimitiveRead, Primitive::kPrimShort); + DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SGET: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, StaticPrimitiveRead, Primitive::kPrimInt); + DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SGET_WIDE: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, StaticPrimitiveRead, Primitive::kPrimLong); + DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SGET_OBJECT: PREAMBLE(); - DoFieldGet(self, shadow_frame, inst, StaticObjectRead, Primitive::kPrimNot); + DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IPUT_BOOLEAN: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, InstancePrimitiveWrite, Primitive::kPrimBoolean); + DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IPUT_BYTE: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, InstancePrimitiveWrite, Primitive::kPrimByte); + DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IPUT_CHAR: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, InstancePrimitiveWrite, Primitive::kPrimChar); + DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IPUT_SHORT: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, InstancePrimitiveWrite, Primitive::kPrimShort); + DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IPUT: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, InstancePrimitiveWrite, Primitive::kPrimInt); + DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IPUT_WIDE: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, InstancePrimitiveWrite, Primitive::kPrimLong); + DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::IPUT_OBJECT: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, InstanceObjectWrite, Primitive::kPrimNot); + DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IPUT_QUICK: + PREAMBLE(); + DoIPutQuick<Primitive::kPrimInt>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IPUT_WIDE_QUICK: + PREAMBLE(); + DoIPutQuick<Primitive::kPrimLong>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IPUT_OBJECT_QUICK: + PREAMBLE(); + DoIPutQuick<Primitive::kPrimNot>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SPUT_BOOLEAN: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, StaticPrimitiveWrite, Primitive::kPrimBoolean); + DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SPUT_BYTE: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, StaticPrimitiveWrite, Primitive::kPrimByte); + DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SPUT_CHAR: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, StaticPrimitiveWrite, Primitive::kPrimChar); + DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SPUT_SHORT: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, StaticPrimitiveWrite, Primitive::kPrimShort); + DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SPUT: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, StaticPrimitiveWrite, Primitive::kPrimInt); + DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SPUT_WIDE: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, StaticPrimitiveWrite, Primitive::kPrimLong); + DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::SPUT_OBJECT: PREAMBLE(); - DoFieldPut(self, shadow_frame, inst, StaticObjectWrite, Primitive::kPrimNot); + DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::INVOKE_VIRTUAL: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kVirtual, false, &result_register); + DoInvoke<kVirtual, false, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::INVOKE_VIRTUAL_RANGE: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kVirtual, true, &result_register); + DoInvoke<kVirtual, true, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::INVOKE_SUPER: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kSuper, false, &result_register); + DoInvoke<kSuper, false, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::INVOKE_SUPER_RANGE: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kSuper, true, &result_register); + DoInvoke<kSuper, true, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::INVOKE_DIRECT: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kDirect, false, &result_register); + DoInvoke<kDirect, false, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::INVOKE_DIRECT_RANGE: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kDirect, true, &result_register); + DoInvoke<kDirect, true, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::INVOKE_INTERFACE: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kInterface, false, &result_register); + DoInvoke<kInterface, false, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::INVOKE_INTERFACE_RANGE: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kInterface, true, &result_register); + DoInvoke<kInterface, true, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::INVOKE_STATIC: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kStatic, false, &result_register); + DoInvoke<kStatic, false, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::INVOKE_STATIC_RANGE: PREAMBLE(); - DoInvoke(self, mh, shadow_frame, inst, kStatic, true, &result_register); + DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); + break; + case Instruction::INVOKE_VIRTUAL_QUICK: + PREAMBLE(); + DoInvokeVirtualQuick<false>(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); + break; + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: + PREAMBLE(); + DoInvokeVirtualQuick<true>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; case Instruction::NEG_INT: @@ -1955,33 +2199,35 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c break; case Instruction::FLOAT_TO_INT: { PREAMBLE(); - uint32_t dst = inst->VRegA_12x(); float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); + int32_t result; if (val != val) { - shadow_frame.SetVReg(dst, 0); + result = 0; } else if (val > static_cast<float>(kMaxInt)) { - shadow_frame.SetVReg(dst, kMaxInt); + result = kMaxInt; } else if (val < static_cast<float>(kMinInt)) { - shadow_frame.SetVReg(dst, kMinInt); + result = kMinInt; } else { - shadow_frame.SetVReg(dst, val); + result = val; } + shadow_frame.SetVReg(inst->VRegA_12x(), result); inst = inst->Next_1xx(); break; } case Instruction::FLOAT_TO_LONG: { PREAMBLE(); - uint32_t dst = inst->VRegA_12x(); float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); + int64_t result; if (val != val) { - shadow_frame.SetVRegLong(dst, 0); + result = 0; } else if (val > static_cast<float>(kMaxLong)) { - shadow_frame.SetVRegLong(dst, kMaxLong); + result = kMaxLong; } else if (val < static_cast<float>(kMinLong)) { - shadow_frame.SetVRegLong(dst, kMinLong); + result = kMinLong; } else { - shadow_frame.SetVRegLong(dst, val); + result = val; } + shadow_frame.SetVRegLong(inst->VRegA_12x(), result); inst = inst->Next_1xx(); break; } @@ -1992,33 +2238,35 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c break; case Instruction::DOUBLE_TO_INT: { PREAMBLE(); - uint32_t dst = inst->VRegA_12x(); double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); + int32_t result; if (val != val) { - shadow_frame.SetVReg(dst, 0); + result = 0; } else if (val > static_cast<double>(kMaxInt)) { - shadow_frame.SetVReg(dst, kMaxInt); + result = kMaxInt; } else if (val < static_cast<double>(kMinInt)) { - shadow_frame.SetVReg(dst, kMinInt); + result = kMinInt; } else { - shadow_frame.SetVReg(dst, val); + result = val; } + shadow_frame.SetVReg(inst->VRegA_12x(), result); inst = inst->Next_1xx(); break; } case Instruction::DOUBLE_TO_LONG: { PREAMBLE(); - uint32_t dst = inst->VRegA_12x(); double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); + int64_t result; if (val != val) { - shadow_frame.SetVRegLong(dst, 0); + result = 0; } else if (val > static_cast<double>(kMaxLong)) { - shadow_frame.SetVRegLong(dst, kMaxLong); + result = kMaxLong; } else if (val < static_cast<double>(kMinLong)) { - shadow_frame.SetVRegLong(dst, kMinLong); + result = kMinLong; } else { - shadow_frame.SetVRegLong(dst, val); + result = val; } + shadow_frame.SetVRegLong(inst->VRegA_12x(), result); inst = inst->Next_1xx(); break; } @@ -2068,14 +2316,14 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c break; case Instruction::DIV_INT: PREAMBLE(); - DoIntDivide(self, shadow_frame, inst->VRegA_23x(), + DoIntDivide(shadow_frame, inst->VRegA_23x(), shadow_frame.GetVReg(inst->VRegB_23x()), shadow_frame.GetVReg(inst->VRegC_23x())); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::REM_INT: PREAMBLE(); - DoIntRemainder(self, shadow_frame, inst->VRegA_23x(), + DoIntRemainder(shadow_frame, inst->VRegA_23x(), shadow_frame.GetVReg(inst->VRegB_23x()), shadow_frame.GetVReg(inst->VRegC_23x())); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); @@ -2145,14 +2393,14 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c break; case Instruction::DIV_LONG: PREAMBLE(); - DoLongDivide(self, shadow_frame, inst->VRegA_23x(), + DoLongDivide(shadow_frame, inst->VRegA_23x(), shadow_frame.GetVRegLong(inst->VRegB_23x()), shadow_frame.GetVRegLong(inst->VRegC_23x())); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::REM_LONG: PREAMBLE(); - DoLongRemainder(self, shadow_frame, inst->VRegA_23x(), + DoLongRemainder(shadow_frame, inst->VRegA_23x(), shadow_frame.GetVRegLong(inst->VRegB_23x()), shadow_frame.GetVRegLong(inst->VRegC_23x())); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); @@ -2296,10 +2544,18 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c inst = inst->Next_1xx(); break; } + case Instruction::DIV_INT_2ADDR: { + PREAMBLE(); + uint32_t vregA = inst->VRegA_12x(); + DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } case Instruction::REM_INT_2ADDR: { PREAMBLE(); uint32_t vregA = inst->VRegA_12x(); - DoIntRemainder(self, shadow_frame, vregA, shadow_frame.GetVReg(vregA), + DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA), shadow_frame.GetVReg(inst->VRegB_12x())); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_1xx); break; @@ -2358,14 +2614,6 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c inst = inst->Next_1xx(); break; } - case Instruction::DIV_INT_2ADDR: { - PREAMBLE(); - uint32_t vregA = inst->VRegA_12x(); - DoIntDivide(self, shadow_frame, vregA, shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } case Instruction::ADD_LONG_2ADDR: { PREAMBLE(); uint32_t vregA = inst->VRegA_12x(); @@ -2396,7 +2644,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::DIV_LONG_2ADDR: { PREAMBLE(); uint32_t vregA = inst->VRegA_12x(); - DoLongDivide(self, shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), + DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), shadow_frame.GetVRegLong(inst->VRegB_12x())); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_1xx); break; @@ -2404,7 +2652,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::REM_LONG_2ADDR: { PREAMBLE(); uint32_t vregA = inst->VRegA_12x(); - DoLongRemainder(self, shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), + DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), shadow_frame.GetVRegLong(inst->VRegB_12x())); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_1xx); break; @@ -2576,13 +2824,13 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c break; case Instruction::DIV_INT_LIT16: PREAMBLE(); - DoIntDivide(self, shadow_frame, inst->VRegA_22s(), + DoIntDivide(shadow_frame, inst->VRegA_22s(), shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::REM_INT_LIT16: PREAMBLE(); - DoIntRemainder(self, shadow_frame, inst->VRegA_22s(), + DoIntRemainder(shadow_frame, inst->VRegA_22s(), shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; @@ -2630,13 +2878,13 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c break; case Instruction::DIV_INT_LIT8: PREAMBLE(); - DoIntDivide(self, shadow_frame, inst->VRegA_22b(), + DoIntDivide(shadow_frame, inst->VRegA_22b(), shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; case Instruction::REM_INT_LIT8: PREAMBLE(); - DoIntRemainder(self, shadow_frame, inst->VRegA_22b(), + DoIntRemainder(shadow_frame, inst->VRegA_22b(), shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; @@ -2683,7 +2931,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c inst = inst->Next_2xx(); break; case Instruction::UNUSED_3E ... Instruction::UNUSED_43: - case Instruction::UNUSED_E3 ... Instruction::UNUSED_FF: + case Instruction::UNUSED_EB ... Instruction::UNUSED_FF: case Instruction::UNUSED_73: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: @@ -2692,10 +2940,25 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c } } +static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +static inline JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register) { + if (shadow_frame.GetMethod()->IsPreverified()) { + // Enter the "without access check" interpreter. + return ExecuteImpl<false>(self, mh, code_item, shadow_frame, result_register); + } else { + // Enter the "with access check" interpreter. + return ExecuteImpl<true>(self, mh, code_item, shadow_frame, result_register); + } +} + void EnterInterpreterFromInvoke(Thread* self, AbstractMethod* method, Object* receiver, uint32_t* args, JValue* result) { DCHECK_EQ(self, Thread::Current()); - if (__builtin_frame_address(0) < self->GetStackEnd()) { + if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { ThrowStackOverflowError(self); return; } @@ -2799,7 +3062,7 @@ JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::C ShadowFrame& shadow_frame) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK_EQ(self, Thread::Current()); - if (__builtin_frame_address(0) < self->GetStackEnd()) { + if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { ThrowStackOverflowError(self); return JValue(); } @@ -2807,9 +3070,11 @@ JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::C return Execute(self, mh, code_item, shadow_frame, JValue()); } -void EnterInterpreterFromInterpreter(Thread* self, ShadowFrame* shadow_frame, JValue* result) +void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (__builtin_frame_address(0) < self->GetStackEnd()) { + if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { ThrowStackOverflowError(self); return; } @@ -2826,8 +3091,6 @@ void EnterInterpreterFromInterpreter(Thread* self, ShadowFrame* shadow_frame, JV self->PushShadowFrame(shadow_frame); - MethodHelper mh(method); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); if (LIKELY(!method->IsNative())) { result->SetJ(Execute(self, mh, code_item, *shadow_frame, JValue()).GetJ()); } else { diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index 96fa05034e..20166ac545 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -47,7 +47,9 @@ extern JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, ShadowFrame& shadow_frame) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -extern void EnterInterpreterFromInterpreter(Thread* self, ShadowFrame* shadow_frame, JValue* result) +extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); } // namespace interpreter diff --git a/src/jni_internal.cc b/src/jni_internal.cc index 2673074302..e457edcd8f 100644 --- a/src/jni_internal.cc +++ b/src/jni_internal.cc @@ -28,7 +28,7 @@ #include "base/stringpiece.h" #include "class_linker.h" #include "dex_file-inl.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "invoke_arg_array_builder.h" #include "jni.h" #include "mirror/class-inl.h" diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc index 0f584444e5..c8b9eb95e3 100644 --- a/src/jni_internal_test.cc +++ b/src/jni_internal_test.cc @@ -16,6 +16,7 @@ #include "jni_internal.h" +#include <limits.h> #include <cfloat> #include <cmath> diff --git a/src/locks.cc b/src/locks.cc index eb0620c0c3..51a40c383a 100644 --- a/src/locks.cc +++ b/src/locks.cc @@ -22,7 +22,7 @@ namespace art { Mutex* Locks::abort_lock_ = NULL; Mutex* Locks::breakpoint_lock_ = NULL; -Mutex* Locks::classlinker_classes_lock_ = NULL; +ReaderWriterMutex* Locks::classlinker_classes_lock_ = NULL; ReaderWriterMutex* Locks::heap_bitmap_lock_ = NULL; Mutex* Locks::logging_lock_ = NULL; ReaderWriterMutex* Locks::mutator_lock_ = NULL; @@ -52,7 +52,8 @@ void Locks::Init() { DCHECK(breakpoint_lock_ == NULL); breakpoint_lock_ = new Mutex("breakpoint lock", kBreakpointLock); DCHECK(classlinker_classes_lock_ == NULL); - classlinker_classes_lock_ = new Mutex("ClassLinker classes lock", kClassLinkerClassesLock); + classlinker_classes_lock_ = new ReaderWriterMutex("ClassLinker classes lock", + kClassLinkerClassesLock); DCHECK(heap_bitmap_lock_ == NULL); heap_bitmap_lock_ = new ReaderWriterMutex("heap bitmap lock", kHeapBitmapLock); DCHECK(mutator_lock_ == NULL); diff --git a/src/locks.h b/src/locks.h index 431a14816a..202fa025a3 100644 --- a/src/locks.h +++ b/src/locks.h @@ -36,9 +36,9 @@ enum LockLevel { kUnexpectedSignalLock, kThreadSuspendCountLock, kAbortLock, + kAllocSpaceLock, kDefaultMutexLevel, kJdwpSerialLock, - kAllocSpaceLock, kMarkSweepLargeObjectLock, kPinTableLock, kLoadLibraryLock, @@ -143,7 +143,7 @@ class Locks { static Mutex* trace_lock_ ACQUIRED_AFTER(breakpoint_lock_); // Guards lists of classes within the class linker. - static Mutex* classlinker_classes_lock_ ACQUIRED_AFTER(trace_lock_); + static ReaderWriterMutex* classlinker_classes_lock_ ACQUIRED_AFTER(trace_lock_); // When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code // doesn't try to hold a higher level Mutex. diff --git a/src/mem_map.cc b/src/mem_map.cc index fb19424c48..c75dffa63c 100644 --- a/src/mem_map.cc +++ b/src/mem_map.cc @@ -183,4 +183,26 @@ bool MemMap::Protect(int prot) { return false; } +bool MemMap::ProtectRegion(uint8_t* addr, size_t length, int prot) { + CHECK_GE(addr, base_begin_); + CHECK_LT(addr + length, reinterpret_cast<const uint8_t*>(base_begin_) + base_size_); + + /* + * Align "addr" to a page boundary and adjust "length" appropriately. + * (The address must be page-aligned, the length doesn't need to be, + * but we do need to ensure we cover the same range.) + */ + uint8_t* alignAddr = (uint8_t*) ((uintptr_t) addr & ~(kPageSize-1)); + size_t alignLength = length + (addr - alignAddr); + + if (mprotect(alignAddr, alignLength, prot) == 0) { + prot_ = prot; + return true; + } + + PLOG(ERROR) << "mprotect(" << reinterpret_cast<void*>(alignAddr) << ", " << alignLength << ", " + << prot << ") failed"; + return false; +} + } // namespace art diff --git a/src/mem_map.h b/src/mem_map.h index 7310f78ddf..2eb7772705 100644 --- a/src/mem_map.h +++ b/src/mem_map.h @@ -61,6 +61,8 @@ class MemMap { bool Protect(int prot); + bool ProtectRegion(uint8_t* addr, size_t length, int prot); + int GetProtect() const { return prot_; } diff --git a/src/mirror/abstract_method-inl.h b/src/mirror/abstract_method-inl.h index d4f0f2c6bc..a8238867aa 100644 --- a/src/mirror/abstract_method-inl.h +++ b/src/mirror/abstract_method-inl.h @@ -117,7 +117,8 @@ inline void AbstractMethod::AssertPcIsWithinCode(uintptr_t pc) const { if (GetEntryPointFromCompiledCode() == GetInterpreterEntryPoint()) { return; } - if (GetEntryPointFromCompiledCode() == GetResolutionTrampoline()) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (GetEntryPointFromCompiledCode() == GetResolutionTrampoline(class_linker)) { return; } DCHECK(IsWithinCode(pc)) diff --git a/src/mirror/abstract_method.cc b/src/mirror/abstract_method.cc index 5258795ace..88a9dc1aa6 100644 --- a/src/mirror/abstract_method.cc +++ b/src/mirror/abstract_method.cc @@ -20,7 +20,7 @@ #include "base/stringpiece.h" #include "class-inl.h" #include "dex_file-inl.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "interpreter/interpreter.h" #include "jni_internal.h" #include "object-inl.h" @@ -268,45 +268,28 @@ void AbstractMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JV result->SetJ(0); } } else { - bool interpret = runtime->GetInstrumentation()->InterpretOnly() && !IsNative() && - !IsProxyMethod(); const bool kLogInvocationStartAndReturn = false; if (GetEntryPointFromCompiledCode() != NULL) { - if (!interpret) { - if (kLogInvocationStartAndReturn) { - LOG(INFO) << StringPrintf("Invoking '%s' code=%p", PrettyMethod(this).c_str(), GetEntryPointFromCompiledCode()); - } + if (kLogInvocationStartAndReturn) { + LOG(INFO) << StringPrintf("Invoking '%s' code=%p", PrettyMethod(this).c_str(), GetEntryPointFromCompiledCode()); + } #ifdef ART_USE_PORTABLE_COMPILER - (*art_portable_invoke_stub)(this, args, args_size, self, result, result_type); + (*art_portable_invoke_stub)(this, args, args_size, self, result, result_type); #else - (*art_quick_invoke_stub)(this, args, args_size, self, result, result_type); + (*art_quick_invoke_stub)(this, args, args_size, self, result, result_type); #endif - if (UNLIKELY(reinterpret_cast<int32_t>(self->GetException(NULL)) == -1)) { - // Unusual case where we were running LLVM generated code and an - // exception was thrown to force the activations to be removed from the - // stack. Continue execution in the interpreter. - self->ClearException(); - ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result); - self->SetTopOfStack(NULL, 0); - self->SetTopOfShadowStack(shadow_frame); - interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result); - } - if (kLogInvocationStartAndReturn) { - LOG(INFO) << StringPrintf("Returned '%s' code=%p", PrettyMethod(this).c_str(), GetEntryPointFromCompiledCode()); - } - } else { - if (kLogInvocationStartAndReturn) { - LOG(INFO) << "Interpreting " << PrettyMethod(this) << "'"; - } - if (this->IsStatic()) { - art::interpreter::EnterInterpreterFromInvoke(self, this, NULL, args, result); - } else { - Object* receiver = reinterpret_cast<Object*>(args[0]); - art::interpreter::EnterInterpreterFromInvoke(self, this, receiver, args + 1, result); - } - if (kLogInvocationStartAndReturn) { - LOG(INFO) << "Returned '" << PrettyMethod(this) << "'"; - } + if (UNLIKELY(reinterpret_cast<int32_t>(self->GetException(NULL)) == -1)) { + // Unusual case where we were running LLVM generated code and an + // exception was thrown to force the activations to be removed from the + // stack. Continue execution in the interpreter. + self->ClearException(); + ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result); + self->SetTopOfStack(NULL, 0); + self->SetTopOfShadowStack(shadow_frame); + interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result); + } + if (kLogInvocationStartAndReturn) { + LOG(INFO) << StringPrintf("Returned '%s' code=%p", PrettyMethod(this).c_str(), GetEntryPointFromCompiledCode()); } } else { LOG(INFO) << "Not invoking '" << PrettyMethod(this) diff --git a/src/mirror/abstract_method.h b/src/mirror/abstract_method.h index c8aa11e5df..339471dd5d 100644 --- a/src/mirror/abstract_method.h +++ b/src/mirror/abstract_method.h @@ -18,6 +18,7 @@ #define ART_SRC_MIRROR_METHOD_H_ #include "class.h" +#include "dex_file.h" #include "invoke_type.h" #include "locks.h" #include "modifiers.h" @@ -29,6 +30,7 @@ struct AbstractMethodOffsets; struct ConstructorMethodOffsets; union JValue; struct MethodClassOffsets; +class MethodHelper; struct MethodOffsets; class StringPiece; class ShadowFrame; @@ -37,7 +39,8 @@ namespace mirror { class StaticStorageBase; -typedef void (EntryPointFromInterpreter)(Thread* self, ShadowFrame* shadow_frame, JValue* result); +typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result); // C++ mirror of java.lang.reflect.Method and java.lang.reflect.Constructor class MANAGED AbstractMethod : public Object { @@ -120,6 +123,14 @@ class MANAGED AbstractMethod : public Object { bool IsProxyMethod() const; + bool IsPreverified() const { + return (GetAccessFlags() & kAccPreverified) != 0; + } + + void SetPreverified() { + SetAccessFlags(GetAccessFlags() | kAccPreverified); + } + bool CheckIncompatibleClassChange(InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); uint16_t GetMethodIndex() const; diff --git a/src/mirror/array.cc b/src/mirror/array.cc index 84c2dc651a..88cd309eeb 100644 --- a/src/mirror/array.cc +++ b/src/mirror/array.cc @@ -20,7 +20,7 @@ #include "class-inl.h" #include "common_throws.h" #include "dex_file-inl.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "object_array.h" #include "object_array-inl.h" @@ -51,7 +51,7 @@ Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, return NULL; } - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); Array* array = down_cast<Array*>(heap->AllocObject(self, array_class, size)); if (array != NULL) { DCHECK(array->IsArrayInstance()); @@ -134,14 +134,12 @@ Array* Array::CreateMultiArray(Thread* self, Class* element_class, IntArray* dim return new_array; } -bool Array::ThrowArrayIndexOutOfBoundsException(int32_t index) const { +void Array::ThrowArrayIndexOutOfBoundsException(int32_t index) const { art::ThrowArrayIndexOutOfBoundsException(index, GetLength()); - return false; } -bool Array::ThrowArrayStoreException(Object* object) const { +void Array::ThrowArrayStoreException(Object* object) const { art::ThrowArrayStoreException(object->GetClass(), this->GetClass()); - return false; } template<typename T> diff --git a/src/mirror/array.h b/src/mirror/array.h index 33c0aeb152..98b8ea0008 100644 --- a/src/mirror/array.h +++ b/src/mirror/array.h @@ -73,15 +73,16 @@ class MANAGED Array : public Object { bool IsValidIndex(int32_t index) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(index < 0 || index >= GetLength())) { - return ThrowArrayIndexOutOfBoundsException(index); + ThrowArrayIndexOutOfBoundsException(index); + return false; } return true; } protected: - bool ThrowArrayIndexOutOfBoundsException(int32_t index) const + void ThrowArrayIndexOutOfBoundsException(int32_t index) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool ThrowArrayStoreException(Object* object) const + void ThrowArrayStoreException(Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: diff --git a/src/mirror/class.cc b/src/mirror/class.cc index 15129ab6dc..2d2130c39e 100644 --- a/src/mirror/class.cc +++ b/src/mirror/class.cc @@ -23,7 +23,7 @@ #include "dex_cache.h" #include "dex_file-inl.h" #include "field-inl.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "object_array-inl.h" #include "object_utils.h" @@ -604,5 +604,22 @@ Field* Class::FindField(const StringPiece& name, const StringPiece& type) { return NULL; } +static void SetPreverifiedFlagOnMethods(mirror::ObjectArray<mirror::AbstractMethod>* methods) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (methods != NULL) { + for (int32_t index = 0, end = methods->GetLength(); index < end; ++index) { + mirror::AbstractMethod* method = methods->GetWithoutChecks(index); + DCHECK(method != NULL); + method->SetPreverified(); + } + } +} + +void Class::SetPreverifiedFlagOnAllMethods() { + DCHECK(IsVerified()); + SetPreverifiedFlagOnMethods(GetDirectMethods()); + SetPreverifiedFlagOnMethods(GetVirtualMethods()); +} + } // namespace mirror } // namespace art diff --git a/src/mirror/class.h b/src/mirror/class.h index 0661b42170..084aa24c7c 100644 --- a/src/mirror/class.h +++ b/src/mirror/class.h @@ -235,6 +235,23 @@ class MANAGED Class : public StaticStorageBase { return (GetAccessFlags() & kAccClassIsPhantomReference) != 0; } + // Can references of this type be assigned to by things of another type? For non-array types + // this is a matter of whether sub-classes may exist - which they can't if the type is final. + // For array classes, where all the classes are final due to there being no sub-classes, an + // Object[] may be assigned to by a String[] but a String[] may not be assigned to by other + // types as the component is final. + bool CannotBeAssignedFromOtherTypes() const { + if (!IsArrayClass()) { + return IsFinal(); + } else { + Class* component = GetComponentType(); + if (component->IsPrimitive()) { + return false; + } else { + return component->CannotBeAssignedFromOtherTypes(); + } + } + } String* GetName() const; // Returns the cached name. void SetName(String* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Sets the cached name. @@ -726,6 +743,9 @@ class MANAGED Class : public StaticStorageBase { static void SetClassClass(Class* java_lang_Class); static void ResetClass(); + // When class is verified, set the kAccPreverified flag on each method. + void SetPreverifiedFlagOnAllMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: void SetVerifyErrorClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/src/mirror/dex_cache-inl.h b/src/mirror/dex_cache-inl.h new file mode 100644 index 0000000000..3b17c428a5 --- /dev/null +++ b/src/mirror/dex_cache-inl.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_MIRROR_DEX_CACHE_INL_H_ +#define ART_SRC_MIRROR_DEX_CACHE_INL_H_ + +#include "dex_cache.h" + +namespace art { +namespace mirror { + +inline AbstractMethod* DexCache::GetResolvedMethod(uint32_t method_idx) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + AbstractMethod* method = GetResolvedMethods()->Get(method_idx); + // Hide resolution trampoline methods from the caller + if (method != NULL && method->IsRuntimeMethod()) { + DCHECK(method == Runtime::Current()->GetResolutionMethod()); + return NULL; + } else { + return method; + } +} + +} // namespace mirror +} // namespace art + +#endif // ART_SRC_MIRROR_DEX_CACHE_INL_H_ diff --git a/src/mirror/dex_cache.cc b/src/mirror/dex_cache.cc index 3009786baa..239dc5e0c3 100644 --- a/src/mirror/dex_cache.cc +++ b/src/mirror/dex_cache.cc @@ -19,8 +19,8 @@ #include "abstract_method-inl.h" #include "base/logging.h" #include "class_linker.h" -#include "heap.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/heap.h" #include "globals.h" #include "object.h" #include "object-inl.h" @@ -78,17 +78,5 @@ void DexCache::Fixup(AbstractMethod* trampoline) { } } -AbstractMethod* DexCache::GetResolvedMethod(uint32_t method_idx) const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - AbstractMethod* method = GetResolvedMethods()->Get(method_idx); - // Hide resolution trampoline methods from the caller - if (method != NULL && method->IsRuntimeMethod()) { - DCHECK(method == Runtime::Current()->GetResolutionMethod()); - return NULL; - } else { - return method; - } -} - } // namespace mirror } // namespace art diff --git a/src/mirror/dex_cache_test.cc b/src/mirror/dex_cache_test.cc index 3d753e1e15..441c6da8a0 100644 --- a/src/mirror/dex_cache_test.cc +++ b/src/mirror/dex_cache_test.cc @@ -17,7 +17,7 @@ #include "class_linker.h" #include "common_test.h" #include "dex_cache.h" -#include "heap.h" +#include "gc/heap.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" #include "sirt_ref.h" diff --git a/src/mirror/field-inl.h b/src/mirror/field-inl.h index cda461b1dc..be5dcab03d 100644 --- a/src/mirror/field-inl.h +++ b/src/mirror/field-inl.h @@ -20,7 +20,7 @@ #include "field.h" #include "base/logging.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "jvalue.h" #include "object-inl.h" #include "object_utils.h" diff --git a/src/mirror/field.cc b/src/mirror/field.cc index 6e2559a62d..a96e8c8d54 100644 --- a/src/mirror/field.cc +++ b/src/mirror/field.cc @@ -17,7 +17,7 @@ #include "field.h" #include "field-inl.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "object_utils.h" #include "runtime.h" diff --git a/src/mirror/object.cc b/src/mirror/object.cc index 4acb5679f9..b2d6e71478 100644 --- a/src/mirror/object.cc +++ b/src/mirror/object.cc @@ -22,8 +22,8 @@ #include "class_linker-inl.h" #include "field.h" #include "field-inl.h" -#include "gc/card_table-inl.h" -#include "heap.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/heap.h" #include "iftable-inl.h" #include "monitor.h" #include "object-inl.h" @@ -44,7 +44,7 @@ Object* Object::Clone(Thread* self) { // Object::SizeOf gets the right size even if we're an array. // Using c->AllocObject() here would be wrong. size_t num_bytes = SizeOf(); - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); SirtRef<Object> copy(self, heap->AllocObject(self, c, num_bytes)); if (copy.get() == NULL) { return NULL; diff --git a/src/mirror/object_array-inl.h b/src/mirror/object_array-inl.h index 05bce9580d..b130dac514 100644 --- a/src/mirror/object_array-inl.h +++ b/src/mirror/object_array-inl.h @@ -19,7 +19,7 @@ #include "object_array.h" -#include "heap.h" +#include "gc/heap.h" #include "mirror/class.h" #include "mirror/field.h" #include "runtime.h" @@ -101,7 +101,7 @@ inline void ObjectArray<T>::Copy(const ObjectArray<T>* src, int src_pos, MemberOffset src_offset(DataOffset(sizeof(Object*)).Int32Value() + src_pos * sizeof(Object*)); MemberOffset dst_offset(DataOffset(sizeof(Object*)).Int32Value() + dst_pos * sizeof(Object*)); Class* array_class = dst->GetClass(); - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); if (array_class == src->GetClass()) { // No need for array store checks if arrays are of the same type for (size_t i = 0; i < length; i++) { diff --git a/src/mirror/object_test.cc b/src/mirror/object_test.cc index abf6c2968f..53a1df95a6 100644 --- a/src/mirror/object_test.cc +++ b/src/mirror/object_test.cc @@ -27,8 +27,8 @@ #include "common_test.h" #include "dex_file.h" #include "field-inl.h" -#include "gc/card_table-inl.h" -#include "heap.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/heap.h" #include "iftable-inl.h" #include "abstract_method-inl.h" #include "object-inl.h" @@ -283,7 +283,7 @@ TEST_F(ObjectTest, StaticFieldFromCode) { uint32_t field_idx = dex_file->GetIndexForFieldId(*field_id); Field* field = FindFieldFromCode(field_idx, clinit, Thread::Current(), StaticObjectRead, - sizeof(Object*)); + sizeof(Object*), true); Object* s0 = field->GetObj(klass); EXPECT_TRUE(s0 != NULL); diff --git a/src/mirror/stack_trace_element.cc b/src/mirror/stack_trace_element.cc index 9d557ec9e8..1ad01823b2 100644 --- a/src/mirror/stack_trace_element.cc +++ b/src/mirror/stack_trace_element.cc @@ -17,7 +17,7 @@ #include "stack_trace_element.h" #include "class.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "string.h" diff --git a/src/mirror/string.cc b/src/mirror/string.cc index 45a6779c45..97126cba4c 100644 --- a/src/mirror/string.cc +++ b/src/mirror/string.cc @@ -17,7 +17,7 @@ #include "string.h" #include "array.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "intern_table.h" #include "object-inl.h" #include "runtime.h" diff --git a/src/mirror/throwable.cc b/src/mirror/throwable.cc index bbff9c2f82..78b76dc6ef 100644 --- a/src/mirror/throwable.cc +++ b/src/mirror/throwable.cc @@ -19,7 +19,7 @@ #include "abstract_method-inl.h" #include "class-inl.h" #include "dex_file-inl.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "object_array.h" #include "object_array-inl.h" diff --git a/src/modifiers.h b/src/modifiers.h index a15b096da2..85bc06da65 100644 --- a/src/modifiers.h +++ b/src/modifiers.h @@ -46,7 +46,9 @@ static const uint32_t kAccConstructor = 0x00010000; // method (dex only) <init> static const uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only) static const uint32_t kAccClassIsProxy = 0x00040000; // class (dex only) // TODO: JACK CLASS ACCESS (HACK TO BE REMOVED) -static const uint32_t kAccClassJack = 0x000080000; // class (dex only) +static const uint32_t kAccClassJack = 0x00080000; // class (dex only) + +static const uint32_t kAccPreverified = 0x00100000; // method (dex only) // Special runtime-only flags. // Note: if only kAccClassIsReference is set, we have a soft reference. diff --git a/src/native/dalvik_system_DexFile.cc b/src/native/dalvik_system_DexFile.cc index e07339cbb6..b9838f879a 100644 --- a/src/native/dalvik_system_DexFile.cc +++ b/src/native/dalvik_system_DexFile.cc @@ -20,7 +20,8 @@ #include "class_linker.h" #include "common_throws.h" #include "dex_file-inl.h" -#include "gc/space.h" +#include "gc/space/image_space.h" +#include "gc/space/space-inl.h" #include "image.h" #include "jni_internal.h" #include "mirror/class_loader.h" @@ -248,13 +249,14 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename return JNI_TRUE; } - Heap* heap = runtime->GetHeap(); - const Spaces& spaces = heap->GetSpaces(); + gc::Heap* heap = runtime->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); // TODO: C++0x auto - for (Spaces::const_iterator cur = spaces.begin(); cur != spaces.end(); ++cur) { - if ((*cur)->IsImageSpace()) { + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + if ((*it)->IsImageSpace()) { // TODO: Ensure this works with multiple image spaces. - const ImageHeader& image_header = (*cur)->AsImageSpace()->GetImageHeader(); + const ImageHeader& image_header = (*it)->AsImageSpace()->GetImageHeader(); if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != image_header.GetOatChecksum()) { ScopedObjectAccess soa(env); LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location diff --git a/src/native/dalvik_system_VMRuntime.cc b/src/native/dalvik_system_VMRuntime.cc index d2ef43c7e2..ce3cc932a0 100644 --- a/src/native/dalvik_system_VMRuntime.cc +++ b/src/native/dalvik_system_VMRuntime.cc @@ -20,13 +20,14 @@ #include "common_throws.h" #include "debugger.h" #include "dex_file-inl.h" +#include "gc/allocator/dlmalloc.h" +#include "gc/space/dlmalloc_space.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object.h" #include "mirror/object-inl.h" #include "object_utils.h" #include "scoped_thread_state_change.h" -#include "gc/space.h" #include "thread.h" #include "thread_list.h" #include "toStringArray.h" @@ -125,6 +126,10 @@ static jstring VMRuntime_vmVersion(JNIEnv* env, jobject) { return env->NewStringUTF(Runtime::Current()->GetVersion()); } +static jstring VMRuntime_vmLibrary(JNIEnv* env, jobject) { + return env->NewStringUTF(kIsDebugBuild ? "libartd.so" : "libart.so"); +} + #if !defined(ART_USE_PORTABLE_COMPILER) static void DisableCheckJniCallback(Thread* t, void*) { t->GetJniEnv()->SetCheckJniEnabled(false); @@ -164,11 +169,11 @@ static void VMRuntime_trimHeap(JNIEnv*, jobject) { uint64_t start_ns = NanoTime(); // Trim the managed heap. - Heap* heap = Runtime::Current()->GetHeap(); - DlMallocSpace* alloc_space = heap->GetAllocSpace(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::space::DlMallocSpace* alloc_space = heap->GetAllocSpace(); size_t alloc_space_size = alloc_space->Size(); float managed_utilization = - static_cast<float>(alloc_space->GetNumBytesAllocated()) / alloc_space_size; + static_cast<float>(alloc_space->GetBytesAllocated()) / alloc_space_size; size_t managed_reclaimed = heap->Trim(); uint64_t gc_heap_end_ns = NanoTime(); @@ -176,7 +181,7 @@ static void VMRuntime_trimHeap(JNIEnv*, jobject) { // Trim the native heap. dlmalloc_trim(0); size_t native_reclaimed = 0; - dlmalloc_inspect_all(MspaceMadviseCallback, &native_reclaimed); + dlmalloc_inspect_all(DlmallocMadviseCallback, &native_reclaimed); uint64_t end_ns = NanoTime(); @@ -208,6 +213,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"), NATIVE_METHOD(VMRuntime, trimHeap, "()V"), NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"), + NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"), }; void register_dalvik_system_VMRuntime(JNIEnv* env) { diff --git a/src/native/java_lang_Runtime.cc b/src/native/java_lang_Runtime.cc index 56a3a06192..e380c17793 100644 --- a/src/native/java_lang_Runtime.cc +++ b/src/native/java_lang_Runtime.cc @@ -18,7 +18,7 @@ #include <limits.h> #include <unistd.h> -#include "heap.h" +#include "gc/heap.h" #include "jni_internal.h" #include "mirror/class_loader.h" #include "runtime.h" diff --git a/src/native/java_lang_System.cc b/src/native/java_lang_System.cc index d8df9d9dae..2462f2fd8e 100644 --- a/src/native/java_lang_System.cc +++ b/src/native/java_lang_System.cc @@ -15,7 +15,7 @@ */ #include "common_throws.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "jni_internal.h" #include "mirror/array.h" #include "mirror/class.h" diff --git a/src/native/java_lang_Thread.cc b/src/native/java_lang_Thread.cc index 7ccfaaa350..8ef190aa3f 100644 --- a/src/native/java_lang_Thread.cc +++ b/src/native/java_lang_Thread.cc @@ -74,6 +74,7 @@ static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean ha case kNative: return kJavaRunnable; case kWaitingForGcToComplete: return kJavaWaiting; case kWaitingPerformingGc: return kJavaWaiting; + case kWaitingForCheckPointsToRun: return kJavaWaiting; case kWaitingForDebuggerSend: return kJavaWaiting; case kWaitingForDebuggerToAttach: return kJavaWaiting; case kWaitingInMainDebuggerLoop: return kJavaWaiting; diff --git a/src/native/sun_misc_Unsafe.cc b/src/native/sun_misc_Unsafe.cc index abb0d5cd5c..eece81a9e8 100644 --- a/src/native/sun_misc_Unsafe.cc +++ b/src/native/sun_misc_Unsafe.cc @@ -15,7 +15,7 @@ */ #include "atomic.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "jni_internal.h" #include "mirror/object.h" #include "mirror/object-inl.h" diff --git a/src/oat.cc b/src/oat.cc index 4eb97f5e41..e606953ed5 100644 --- a/src/oat.cc +++ b/src/oat.cc @@ -22,7 +22,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '0', '5', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '0', '6', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); @@ -57,6 +57,10 @@ OatHeader::OatHeader(InstructionSet instruction_set, UpdateChecksum(image_file_location.data(), image_file_location_size_); executable_offset_ = 0; + interpreter_to_interpreter_entry_offset_ = 0; + interpreter_to_quick_entry_offset_ = 0; + portable_resolution_trampoline_offset_ = 0; + quick_resolution_trampoline_offset_ = 0; } bool OatHeader::IsValid() const { @@ -97,6 +101,92 @@ uint32_t OatHeader::GetExecutableOffset() const { return executable_offset_; } +void OatHeader::SetExecutableOffset(uint32_t executable_offset) { + DCHECK_ALIGNED(executable_offset, kPageSize); + CHECK_GT(executable_offset, sizeof(OatHeader)); + DCHECK(IsValid()); + DCHECK_EQ(executable_offset_, 0U); + + executable_offset_ = executable_offset; + UpdateChecksum(&executable_offset_, sizeof(executable_offset)); +} + +const void* OatHeader::GetInterpreterToInterpreterEntry() const { + return reinterpret_cast<const uint8_t*>(this) + GetInterpreterToInterpreterEntryOffset(); +} + +uint32_t OatHeader::GetInterpreterToInterpreterEntryOffset() const { + DCHECK(IsValid()); + CHECK_GE(interpreter_to_interpreter_entry_offset_, executable_offset_); + return interpreter_to_interpreter_entry_offset_; +} + +void OatHeader::SetInterpreterToInterpreterEntryOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= executable_offset_); + DCHECK(IsValid()); + DCHECK_EQ(interpreter_to_interpreter_entry_offset_, 0U) << offset; + + interpreter_to_interpreter_entry_offset_ = offset; + UpdateChecksum(&interpreter_to_interpreter_entry_offset_, sizeof(offset)); +} + +const void* OatHeader::GetInterpreterToQuickEntry() const { + return reinterpret_cast<const uint8_t*>(this) + GetInterpreterToQuickEntryOffset(); +} + +uint32_t OatHeader::GetInterpreterToQuickEntryOffset() const { + DCHECK(IsValid()); + CHECK_GE(interpreter_to_quick_entry_offset_, interpreter_to_interpreter_entry_offset_); + return interpreter_to_quick_entry_offset_; +} + +void OatHeader::SetInterpreterToQuickEntryOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= interpreter_to_interpreter_entry_offset_); + DCHECK(IsValid()); + DCHECK_EQ(interpreter_to_quick_entry_offset_, 0U) << offset; + + interpreter_to_quick_entry_offset_ = offset; + UpdateChecksum(&interpreter_to_quick_entry_offset_, sizeof(offset)); +} + +const void* OatHeader::GetPortableResolutionTrampoline() const { + return reinterpret_cast<const uint8_t*>(this) + GetPortableResolutionTrampolineOffset(); +} + +uint32_t OatHeader::GetPortableResolutionTrampolineOffset() const { + DCHECK(IsValid()); + CHECK_GE(portable_resolution_trampoline_offset_, interpreter_to_quick_entry_offset_); + return portable_resolution_trampoline_offset_; +} + +void OatHeader::SetPortableResolutionTrampolineOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= interpreter_to_quick_entry_offset_); + DCHECK(IsValid()); + DCHECK_EQ(portable_resolution_trampoline_offset_, 0U) << offset; + + portable_resolution_trampoline_offset_ = offset; + UpdateChecksum(&portable_resolution_trampoline_offset_, sizeof(offset)); +} + +const void* OatHeader::GetQuickResolutionTrampoline() const { + return reinterpret_cast<const uint8_t*>(this) + GetQuickResolutionTrampolineOffset(); +} + +uint32_t OatHeader::GetQuickResolutionTrampolineOffset() const { + DCHECK(IsValid()); + CHECK_GE(quick_resolution_trampoline_offset_, portable_resolution_trampoline_offset_); + return quick_resolution_trampoline_offset_; +} + +void OatHeader::SetQuickResolutionTrampolineOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= portable_resolution_trampoline_offset_); + DCHECK(IsValid()); + DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset; + + quick_resolution_trampoline_offset_ = offset; + UpdateChecksum(&quick_resolution_trampoline_offset_, sizeof(offset)); +} + uint32_t OatHeader::GetImageFileLocationOatChecksum() const { CHECK(IsValid()); return image_file_location_oat_checksum_; @@ -123,16 +213,6 @@ std::string OatHeader::GetImageFileLocation() const { GetImageFileLocationSize()); } -void OatHeader::SetExecutableOffset(uint32_t executable_offset) { - DCHECK_ALIGNED(executable_offset, kPageSize); - CHECK_GT(executable_offset, sizeof(OatHeader)); - DCHECK(IsValid()); - DCHECK_EQ(executable_offset_, 0U); - - executable_offset_ = executable_offset; - UpdateChecksum(&executable_offset_, sizeof(executable_offset)); -} - OatMethodOffsets::OatMethodOffsets() : code_offset_(0), frame_size_in_bytes_(0), @@ -43,8 +43,20 @@ class PACKED(4) OatHeader { return dex_file_count_; } uint32_t GetExecutableOffset() const; - InstructionSet GetInstructionSet() const; void SetExecutableOffset(uint32_t executable_offset); + const void* GetInterpreterToInterpreterEntry() const; + uint32_t GetInterpreterToInterpreterEntryOffset() const; + void SetInterpreterToInterpreterEntryOffset(uint32_t offset); + const void* GetInterpreterToQuickEntry() const; + uint32_t GetInterpreterToQuickEntryOffset() const; + void SetInterpreterToQuickEntryOffset(uint32_t offset); + const void* GetPortableResolutionTrampoline() const; + uint32_t GetPortableResolutionTrampolineOffset() const; + void SetPortableResolutionTrampolineOffset(uint32_t offset); + const void* GetQuickResolutionTrampoline() const; + uint32_t GetQuickResolutionTrampolineOffset() const; + void SetQuickResolutionTrampolineOffset(uint32_t offset); + InstructionSet GetInstructionSet() const; uint32_t GetImageFileLocationOatChecksum() const; uint32_t GetImageFileLocationOatDataBegin() const; uint32_t GetImageFileLocationSize() const; @@ -62,6 +74,10 @@ class PACKED(4) OatHeader { InstructionSet instruction_set_; uint32_t dex_file_count_; uint32_t executable_offset_; + uint32_t interpreter_to_interpreter_entry_offset_; + uint32_t interpreter_to_quick_entry_offset_; + uint32_t portable_resolution_trampoline_offset_; + uint32_t quick_resolution_trampoline_offset_; uint32_t image_file_location_oat_checksum_; uint32_t image_file_location_oat_data_begin_; diff --git a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc index 1a5fe47e58..2e9453ce9c 100644 --- a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc +++ b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc @@ -91,12 +91,26 @@ extern "C" uint64_t art_quick_shl_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t); +// Interpreter entrypoints. +extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + // Intrinsic entrypoints. extern "C" int32_t __memcmp16(void*, void*, int32_t); extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); extern "C" int32_t art_quick_string_compareto(void*, void*); // Invoke entrypoints. +extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); +extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); @@ -187,6 +201,10 @@ void InitEntryPoints(EntryPoints* points) { points->pShrLong = art_quick_shr_long; points->pUshrLong = art_quick_ushr_long; + // Interpreter + points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry; + points->pInterpreterToQuickEntry = artInterpreterToQuickEntry; + // Intrinsics points->pIndexOf = art_quick_indexof; points->pMemcmp16 = __memcmp16; @@ -194,6 +212,8 @@ void InitEntryPoints(EntryPoints* points) { points->pMemcpy = memcpy; // Invocation + points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline; + points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline; points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline; points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; diff --git a/src/oat/runtime/arm/runtime_support_arm.S b/src/oat/runtime/arm/runtime_support_arm.S index 3578ba0d16..f19e8bada0 100644 --- a/src/oat/runtime/arm/runtime_support_arm.S +++ b/src/oat/runtime/arm/runtime_support_arm.S @@ -246,48 +246,6 @@ INVOKE_TRAMPOLINE art_quick_invoke_direct_trampoline_with_access_check, artInvok INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck - /* - * Portable resolution trampoline. - */ - .extern artPortableResolutionTrampoline -ENTRY art_portable_resolution_trampoline - push {r0, r1, r2, r3, lr} @ spill regs - .save {r0, r1, r2, r3, lr} - .pad #20 - .cfi_adjust_cfa_offset 20 - sub sp, #12 @ pad stack pointer to align frame - .pad #12 - .cfi_adjust_cfa_offset 12 - mov r2, r9 @ pass Thread::Current - mov r1, sp @ pass stack pointer - blx artPortableResolutionTrampoline @ (method_idx, sp, Thread*) - mov r12, r0 @ save method code pointer result - add sp, #12 @ remove padding from stack pointer - .cfi_adjust_cfa_offset -12 - pop {r0, r1, r2, r3, lr} @ restore regs - .cfi_adjust_cfa_offset -20 - cmp r12, #0 @ is method code null? - bxne r12 @ if non-null, tail call to method's code - bx lr @ otherwise, return to caller to handle exception -END art_portable_resolution_trampoline - - /* - * Quick resolution trampoline. - */ - .extern artQuickResolutionTrampoline -ENTRY art_quick_resolution_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME @ save callee saves in case allocation triggers GC - mov r2, r9 @ pass Thread::Current - mov r1, sp @ pass stack pointer - blx artQuickResolutionTrampoline @ (method_idx, sp, Thread*) - mov r12, r0 @ save method code pointer result - add sp, #4 @ set up stack pointer - .cfi_adjust_cfa_offset -4 - pop {r0-r3, r5-r8, r10-r11, lr} @ 11 words, r0 will hold method* - .cfi_adjust_cfa_offset -44 - bx r12 @ leaf call to method code -END art_quick_resolution_trampoline - /* * Portable invocation stub. * On entry: diff --git a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc index eb82c42894..8e066118cd 100644 --- a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc +++ b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc @@ -93,12 +93,26 @@ extern "C" uint64_t art_quick_shl_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t); +// Interpreter entrypoints. +extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + // Intrinsic entrypoints. extern "C" int32_t __memcmp16(void*, void*, int32_t); extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); extern "C" int32_t art_quick_string_compareto(void*, void*); // Invoke entrypoints. +extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); +extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); @@ -188,6 +202,10 @@ void InitEntryPoints(EntryPoints* points) { points->pShrLong = art_quick_shr_long; points->pUshrLong = art_quick_ushr_long; + // Interpreter + points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry; + points->pInterpreterToQuickEntry = artInterpreterToQuickEntry; + // Intrinsics points->pIndexOf = art_quick_indexof; points->pMemcmp16 = __memcmp16; @@ -195,6 +213,8 @@ void InitEntryPoints(EntryPoints* points) { points->pMemcpy = memcpy; // Invocation + points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline; + points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline; points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline; points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; diff --git a/src/oat/runtime/mips/runtime_support_mips.S b/src/oat/runtime/mips/runtime_support_mips.S index 2144e349ba..45d583e097 100644 --- a/src/oat/runtime/mips/runtime_support_mips.S +++ b/src/oat/runtime/mips/runtime_support_mips.S @@ -413,71 +413,6 @@ INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvoke INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck /* - * Portable resolution trampoline. - */ - .extern artPortableResolutionTrampoline -ENTRY art_portable_resolution_trampoline - GENERATE_GLOBAL_POINTER - addiu $sp, $sp, -32 # leave room for $a0, $a1, $a2, $a3, and $ra - .cfi_adjust_cfa_offset 32 - sw $ra, 16($sp) - .cfi_rel_offset 31, 16 - sw $a3, 12($sp) - .cfi_rel_offset 7, 12 - sw $a2, 8($sp) - .cfi_rel_offset 6, 8 - sw $a1, 4($sp) - .cfi_rel_offset 5, 4 - sw $a0, 0($sp) - .cfi_rel_offset 4, 0 - move $a2, $s1 # pass Thread::Current() - jal artPortableResolutionTrampoline # (method_idx, sp, Thread*) - move $a1, $sp # pass stack pointer - lw $a0, 0($sp) # restore registers from stack - lw $a1, 4($sp) - lw $a2, 8($sp) - lw $a3, 12($sp) - lw $ra, 16($sp) - beq $v0, $zero, resolve_fail - addiu $sp, $sp, 32 # restore the stack - .cfi_adjust_cfa_offset -32 - jr $t9 # leaf call to method's code - move $t9, $v0 # put method code result in $t9 -resolve_fail: - jr $ra - nop -END art_portable_resolution_trampoline - - /* - * Quick resolution trampoline. - */ - .extern artQuickResolutionTrampoline -ENTRY art_quick_resolution_trampoline - GENERATE_GLOBAL_POINTER - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - move $a2, $s1 # pass Thread::Current() - jal artQuickResolutionTrampoline # (method_idx, sp, Thread*) - move $a1, $sp # pass stack pointer - move $t9, $v0 # put method code result in $t9 - lw $a0, 0($sp) # restore registers from stack - lw $a1, 4($sp) - lw $a2, 8($sp) - lw $a3, 12($sp) - lw $s2, 28($sp) - lw $s3, 32($sp) - lw $s4, 36($sp) - lw $s5, 40($sp) - lw $s6, 44($sp) - lw $s7, 48($sp) - lw $gp, 52($sp) - lw $fp, 56($sp) - lw $ra, 60($sp) - jr $t9 # leaf call to method's code - addiu $sp, $sp, 64 # restore the stack - .cfi_adjust_cfa_offset -64 -END art_quick_resolution_trampoline - - /* * Common invocation stub for portable and quick. * On entry: * a0 = method pointer diff --git a/src/oat/runtime/oat_support_entrypoints.h b/src/oat/runtime/oat_support_entrypoints.h index 72d5348556..c1a2587c45 100644 --- a/src/oat/runtime/oat_support_entrypoints.h +++ b/src/oat/runtime/oat_support_entrypoints.h @@ -17,6 +17,7 @@ #ifndef ART_SRC_OAT_RUNTIME_OAT_SUPPORT_ENTRYPOINTS_H_ #define ART_SRC_OAT_RUNTIME_OAT_SUPPORT_ENTRYPOINTS_H_ +#include "dex_file-inl.h" #include "runtime.h" #define ENTRYPOINT_OFFSET(x) \ @@ -30,6 +31,8 @@ class Class; class Object; } // namespace mirror class DvmDex; +class MethodHelper; +class ShadowFrame; class Thread; struct PACKED(4) EntryPoints { @@ -104,6 +107,14 @@ struct PACKED(4) EntryPoints { uint64_t (*pShrLong)(uint64_t, uint32_t); uint64_t (*pUshrLong)(uint64_t, uint32_t); + // Interpreter + void (*pInterpreterToInterpreterEntry)(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + void (*pInterpreterToQuickEntry)(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + // Intrinsics int32_t (*pIndexOf)(void*, uint32_t, uint32_t, uint32_t); int32_t (*pMemcmp16)(void*, void*, int32_t); @@ -111,6 +122,10 @@ struct PACKED(4) EntryPoints { void* (*pMemcpy)(void*, const void*, size_t); // Invocation + const void* (*pPortableResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*, + mirror::AbstractMethod**, Thread*); + const void* (*pQuickResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*, + mirror::AbstractMethod**, Thread*); void (*pInvokeDirectTrampolineWithAccessCheck)(uint32_t, void*); void (*pInvokeInterfaceTrampoline)(uint32_t, void*); void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*); @@ -131,24 +146,25 @@ struct PACKED(4) EntryPoints { void (*pThrowStackOverflowFromCode)(void*); }; + // JNI entrypoints. extern uint32_t JniMethodStart(Thread* self) - UNLOCK_FUNCTION(Locks::mutator_lock_) __attribute__ ((hot)); + UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self) - UNLOCK_FUNCTION(Locks::mutator_lock_) __attribute__ ((hot)); + UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) __attribute__ ((hot)); + SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) __attribute__ ((hot)); + SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) __attribute__ ((hot)); + SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result, uint32_t saved_local_ref_cookie, jobject locked, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) __attribute__ ((hot)); + SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; // Initialize an entry point data structure. void InitEntryPoints(EntryPoints* points); diff --git a/src/oat/runtime/support_dexcache.cc b/src/oat/runtime/support_dexcache.cc index 3e8ebc6679..0af7a6281d 100644 --- a/src/oat/runtime/support_dexcache.cc +++ b/src/oat/runtime/support_dexcache.cc @@ -15,7 +15,7 @@ */ #include "callee_save_frame.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "class_linker-inl.h" #include "dex_file-inl.h" #include "mirror/abstract_method-inl.h" diff --git a/src/oat/runtime/support_field.cc b/src/oat/runtime/support_field.cc index 5821063cf6..c20326c63e 100644 --- a/src/oat/runtime/support_field.cc +++ b/src/oat/runtime/support_field.cc @@ -34,7 +34,7 @@ extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx, return field->Get32(field->GetDeclaringClass()); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int32_t)); + field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int32_t), true); if (LIKELY(field != NULL)) { return field->Get32(field->GetDeclaringClass()); } @@ -50,7 +50,7 @@ extern "C" uint64_t artGet64StaticFromCode(uint32_t field_idx, return field->Get64(field->GetDeclaringClass()); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int64_t)); + field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int64_t), true); if (LIKELY(field != NULL)) { return field->Get64(field->GetDeclaringClass()); } @@ -67,7 +67,7 @@ extern "C" mirror::Object* artGetObjStaticFromCode(uint32_t field_idx, return field->GetObj(field->GetDeclaringClass()); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticObjectRead, sizeof(mirror::Object*)); + field = FindFieldFromCode(field_idx, referrer, self, StaticObjectRead, sizeof(mirror::Object*), true); if (LIKELY(field != NULL)) { return field->GetObj(field->GetDeclaringClass()); } @@ -83,7 +83,7 @@ extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, mirror::Object* return field->Get32(obj); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int32_t)); + field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int32_t), true); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -104,7 +104,7 @@ extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx, mirror::Object* return field->Get64(obj); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int64_t)); + field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int64_t), true); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -126,7 +126,7 @@ extern "C" mirror::Object* artGetObjInstanceFromCode(uint32_t field_idx, mirror: return field->GetObj(obj); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectRead, sizeof(mirror::Object*)); + field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectRead, sizeof(mirror::Object*), true); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -148,7 +148,7 @@ extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value, return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int32_t)); + field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int32_t), true); if (LIKELY(field != NULL)) { field->Set32(field->GetDeclaringClass(), new_value); return 0; // success @@ -165,7 +165,7 @@ extern "C" int artSet64StaticFromCode(uint32_t field_idx, const mirror::Abstract return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int64_t)); + field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int64_t), true); if (LIKELY(field != NULL)) { field->Set64(field->GetDeclaringClass(), new_value); return 0; // success @@ -186,7 +186,7 @@ extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_v } } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticObjectWrite, sizeof(mirror::Object*)); + field = FindFieldFromCode(field_idx, referrer, self, StaticObjectWrite, sizeof(mirror::Object*), true); if (LIKELY(field != NULL)) { field->SetObj(field->GetDeclaringClass(), new_value); return 0; // success @@ -204,7 +204,7 @@ extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int32_t)); + field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int32_t), true); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -231,7 +231,7 @@ extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, } *sp = callee_save; self->SetTopOfStack(sp, 0); - field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int64_t)); + field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int64_t), true); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -257,7 +257,7 @@ extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectWrite, - sizeof(mirror::Object*)); + sizeof(mirror::Object*), true); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); diff --git a/src/oat/runtime/support_interpreter.cc b/src/oat/runtime/support_interpreter.cc index a5d6fa3c8b..55be54f2c2 100644 --- a/src/oat/runtime/support_interpreter.cc +++ b/src/oat/runtime/support_interpreter.cc @@ -110,12 +110,11 @@ extern "C" uint64_t artInterpreterEntry(mirror::AbstractMethod* method, Thread* return result.GetJ(); } -void artInterpreterToQuickEntry(Thread* self, ShadowFrame* shadow_frame, JValue* result) +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::AbstractMethod* method = shadow_frame->GetMethod(); - MethodHelper mh(method); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); - uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_; ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArray(shadow_frame, arg_offset); diff --git a/src/oat/runtime/support_invoke.cc b/src/oat/runtime/support_invoke.cc index a96555d8ea..6a95f3c8ff 100644 --- a/src/oat/runtime/support_invoke.cc +++ b/src/oat/runtime/support_invoke.cc @@ -17,6 +17,7 @@ #include "callee_save_frame.h" #include "dex_instruction-inl.h" #include "mirror/class-inl.h" +#include "mirror/dex_cache-inl.h" #include "mirror/abstract_method-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc index df2dda2174..71b67d06bb 100644 --- a/src/oat/runtime/support_stubs.cc +++ b/src/oat/runtime/support_stubs.cc @@ -32,6 +32,7 @@ namespace art { // Lazily resolve a method for portable. Called by stub code. extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, mirror::AbstractMethod** called_addr, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -79,6 +80,14 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* c invoke_type = kVirtual; is_range = true; break; + case Instruction::INVOKE_INTERFACE: + invoke_type = kInterface; + is_range = false; + break; + case Instruction::INVOKE_INTERFACE_RANGE: + invoke_type = kInterface; + is_range = true; + break; default: LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(NULL); // Avoid used uninitialized warnings. @@ -87,6 +96,12 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* c } uint32_t dex_method_idx = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c(); called = linker->ResolveMethod(dex_method_idx, caller, invoke_type); + // Refine called method based on receiver. + if (invoke_type == kVirtual) { + called = receiver->GetClass()->FindVirtualMethodForVirtual(called); + } else if (invoke_type == kInterface) { + called = receiver->GetClass()->FindVirtualMethodForInterface(called); + } } else { CHECK(called->IsStatic()) << PrettyMethod(called); invoke_type = kStatic; @@ -129,7 +144,7 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* c // Expect class to at least be initializing. DCHECK(called->GetDeclaringClass()->IsInitializing()); // Don't want infinite recursion. - DCHECK(code != GetResolutionTrampoline()); + DCHECK(code != GetResolutionTrampoline(linker)); // Set up entry into main method *called_addr = called; } @@ -138,6 +153,7 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* c // Lazily resolve a method for quick. Called by stub code. extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, mirror::AbstractMethod** sp, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__arm__) @@ -261,6 +277,14 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* call invoke_type = kVirtual; is_range = true; break; + case Instruction::INVOKE_INTERFACE: + invoke_type = kInterface; + is_range = false; + break; + case Instruction::INVOKE_INTERFACE_RANGE: + invoke_type = kInterface; + is_range = true; + break; default: LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(NULL); // Avoid used uninitialized warnings. @@ -334,6 +358,12 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* call if (LIKELY(!thread->IsExceptionPending())) { // Incompatible class change should have been handled in resolve method. CHECK(!called->CheckIncompatibleClassChange(invoke_type)); + // Refine called method based on receiver. + if (invoke_type == kVirtual) { + called = receiver->GetClass()->FindVirtualMethodForVirtual(called); + } else if (invoke_type == kInterface) { + called = receiver->GetClass()->FindVirtualMethodForInterface(called); + } // Ensure that the called method's class is initialized. mirror::Class* called_class = called->GetDeclaringClass(); linker->EnsureInitialized(called_class, true, true); @@ -363,7 +393,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* call // Expect class to at least be initializing. DCHECK(called->GetDeclaringClass()->IsInitializing()); // Don't want infinite recursion. - DCHECK(code != GetResolutionTrampoline()); + DCHECK(code != GetResolutionTrampoline(linker)); // Set up entry into main method regs[0] = reinterpret_cast<uintptr_t>(called); } diff --git a/src/oat/runtime/support_throw.cc b/src/oat/runtime/support_throw.cc index b8c68a565c..9588698bb2 100644 --- a/src/oat/runtime/support_throw.cc +++ b/src/oat/runtime/support_throw.cc @@ -67,7 +67,7 @@ extern "C" void artThrowDivZeroFromCode(Thread* self, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); - ThrowArithmeticExceptionDivideByZero(self); + ThrowArithmeticExceptionDivideByZero(); self->QuickDeliverException(); } diff --git a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc index 357bbe0819..a90a583e9f 100644 --- a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc +++ b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc @@ -75,6 +75,14 @@ extern "C" uint64_t art_quick_lshl_from_code(uint64_t, uint32_t); extern "C" uint64_t art_quick_lshr_from_code(uint64_t, uint32_t); extern "C" uint64_t art_quick_lushr_from_code(uint64_t, uint32_t); +// Interpreter entrypoints. +extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + // Intrinsic entrypoints. extern "C" int32_t art_quick_memcmp16(void*, void*, int32_t); extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); @@ -82,6 +90,12 @@ extern "C" int32_t art_quick_string_compareto(void*, void*); extern "C" void* art_quick_memcpy(void*, const void*, size_t); // Invoke entrypoints. +extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); +extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); @@ -171,6 +185,10 @@ void InitEntryPoints(EntryPoints* points) { points->pShrLong = art_quick_lshr_from_code; points->pUshrLong = art_quick_lushr_from_code; + // Interpreter + points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry; + points->pInterpreterToQuickEntry = artInterpreterToQuickEntry; + // Intrinsics points->pIndexOf = art_quick_indexof; points->pMemcmp16 = art_quick_memcmp16; @@ -178,6 +196,8 @@ void InitEntryPoints(EntryPoints* points) { points->pMemcpy = art_quick_memcpy; // Invocation + points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline; + points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline; points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline; points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; diff --git a/src/oat/runtime/x86/runtime_support_x86.S b/src/oat/runtime/x86/runtime_support_x86.S index d3a1fb73ff..ee6db0c3f8 100644 --- a/src/oat/runtime/x86/runtime_support_x86.S +++ b/src/oat/runtime/x86/runtime_support_x86.S @@ -301,55 +301,6 @@ INVOKE_TRAMPOLINE art_quick_invoke_direct_trampoline_with_access_check, artInvok INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck - /* - * Portable resolution trampoline. - */ -DEFINE_FUNCTION art_portable_resolution_trampoline - PUSH ebp // stash %ebp - movl %esp, %ebp // save %esp - .cfi_def_cfa_register ebp - subl LITERAL(8), %esp // align stack - movl 8(%ebp), %eax // load the called method* into %eax - leal 8(%ebp), %edx // put the called method* address in %edx - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - PUSH edx // pass called method* address - PUSH eax // pass method* - call SYMBOL(artPortableResolutionTrampoline) // (method_idx, sp, Thread*) - leave // restore the stack and %ebp - .cfi_def_cfa esp, 4 - .cfi_restore ebp - cmpl LITERAL(0), %eax // check if returned method code is null - je resolve_fail // if null, jump to return to handle - jmp *%eax // otherwise, tail call to intended method -resolve_fail: - ret -END_FUNCTION art_portable_resolution_trampoline - - /* - * Quick resolution trampoline. - */ -DEFINE_FUNCTION art_quick_resolution_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - movl %esp, %ecx // save stack pointer - PUSH eax // align stack - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - .cfi_adjust_cfa_offset 4 - PUSH ecx // pass stack pointer - PUSH eax // pass method* - call SYMBOL(artQuickResolutionTrampoline) // (method_idx, sp, Thread*) - movl %eax, %edi // save returned code pointer in %edi - addl LITERAL(16), %esp // pop arguments - .cfi_adjust_cfa_offset -16 - POP eax // restore registers - POP ecx - POP edx - POP ebx - POP ebp - POP esi - xchgl %edi, (%esp) // swap %edi and code pointer - ret // tail call to intended method -END_FUNCTION art_quick_resolution_trampoline - /* * Portable invocation stub. * On entry: diff --git a/src/oat_test.cc b/src/oat_test.cc index dd336d9a9b..29e2891b40 100644 --- a/src/oat_test.cc +++ b/src/oat_test.cc @@ -68,16 +68,16 @@ TEST_F(OatTest, WriteRead) { const bool compile = false; // DISABLED_ due to the time to compile libcore ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - jobject class_loader = NULL; - if (compile) { - // TODO: make selectable + // TODO: make selectable #if defined(ART_USE_PORTABLE_COMPILER) - CompilerBackend compiler_backend = kPortable; + CompilerBackend compiler_backend = kPortable; #else - CompilerBackend compiler_backend = kQuick; + CompilerBackend compiler_backend = kQuick; #endif - compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, 2, false, - NULL, true, true)); + compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, NULL, 2, false, + true, true)); + jobject class_loader = NULL; + if (compile) { compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath()); } @@ -143,7 +143,7 @@ TEST_F(OatTest, WriteRead) { TEST_F(OatTest, OatHeaderSizeCheck) { // If this test is failing and you have to update these constants, // it is time to update OatHeader::kOatVersion - EXPECT_EQ(36U, sizeof(OatHeader)); + EXPECT_EQ(52U, sizeof(OatHeader)); EXPECT_EQ(28U, sizeof(OatMethodOffsets)); } diff --git a/src/oat_writer.cc b/src/oat_writer.cc index 8acbfe9ca5..1d249d674f 100644 --- a/src/oat_writer.cc +++ b/src/oat_writer.cc @@ -22,6 +22,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "dex_file-inl.h" +#include "gc/space/space.h" #include "mirror/abstract_method-inl.h" #include "mirror/array.h" #include "mirror/class_loader.h" @@ -30,7 +31,6 @@ #include "output_stream.h" #include "safe_map.h" #include "scoped_thread_state_change.h" -#include "gc/space.h" #include "verifier/method_verifier.h" namespace art { @@ -54,13 +54,35 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, uint32_t image_file_location_oat_begin, const std::string& image_file_location, const CompilerDriver* compiler) - : compiler_driver_(compiler) { - image_file_location_oat_checksum_ = image_file_location_oat_checksum; - image_file_location_oat_begin_ = image_file_location_oat_begin; - image_file_location_ = image_file_location; - dex_files_ = &dex_files; - oat_header_ = NULL; - executable_offset_padding_length_ = 0; + : compiler_driver_(compiler), + dex_files_(&dex_files), + image_file_location_oat_checksum_(image_file_location_oat_checksum), + image_file_location_oat_begin_(image_file_location_oat_begin), + image_file_location_(image_file_location), + oat_header_(NULL), + size_dex_file_alignment_(0), + size_executable_offset_alignment_(0), + size_oat_header_(0), + size_oat_header_image_file_location_(0), + size_dex_file_(0), + size_interpreter_to_interpreter_entry_(0), + size_interpreter_to_quick_entry_(0), + size_portable_resolution_trampoline_(0), + size_quick_resolution_trampoline_(0), + size_stubs_alignment_(0), + size_code_size_(0), + size_code_(0), + size_code_alignment_(0), + size_mapping_table_(0), + size_vmap_table_(0), + size_gc_map_(0), + size_oat_dex_file_location_size_(0), + size_oat_dex_file_location_data_(0), + size_oat_dex_file_location_checksum_(0), + size_oat_dex_file_offset_(0), + size_oat_dex_file_methods_offsets_(0), + size_oat_class_status_(0), + size_oat_class_method_offsets_(0) { size_t offset = InitOatHeader(); offset = InitOatDexFiles(offset); @@ -70,6 +92,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, offset = InitOatCodeDexFiles(offset); CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); + CHECK(image_file_location.empty() == compiler->IsImage()); } OatWriter::~OatWriter() { @@ -106,7 +129,9 @@ size_t OatWriter::InitDexFiles(size_t offset) { // calculate the offsets within OatDexFiles to the DexFiles for (size_t i = 0; i != dex_files_->size(); ++i) { // dex files are required to be 4 byte aligned + size_t original_offset = offset; offset = RoundUp(offset, 4); + size_dex_file_alignment_ += offset - original_offset; // set offset in OatDexFile to DexFile oat_dex_files_[i]->dex_file_offset_ = offset; @@ -162,7 +187,33 @@ size_t OatWriter::InitOatCode(size_t offset) { // required to be on a new page boundary offset = RoundUp(offset, kPageSize); oat_header_->SetExecutableOffset(offset); - executable_offset_padding_length_ = offset - old_offset; + size_executable_offset_alignment_ = offset - old_offset; + if (compiler_driver_->IsImage()) { + InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); + oat_header_->SetInterpreterToInterpreterEntryOffset(offset); + interpreter_to_interpreter_entry_.reset(compiler_driver_->CreateInterpreterToInterpreterEntry()); + offset += interpreter_to_interpreter_entry_->size(); + + offset = CompiledCode::AlignCode(offset, instruction_set); + oat_header_->SetInterpreterToQuickEntryOffset(offset); + interpreter_to_quick_entry_.reset(compiler_driver_->CreateInterpreterToQuickEntry()); + offset += interpreter_to_quick_entry_->size(); + + offset = CompiledCode::AlignCode(offset, instruction_set); + oat_header_->SetPortableResolutionTrampolineOffset(offset); + portable_resolution_trampoline_.reset(compiler_driver_->CreatePortableResolutionTrampoline()); + offset += portable_resolution_trampoline_->size(); + + offset = CompiledCode::AlignCode(offset, instruction_set); + oat_header_->SetQuickResolutionTrampolineOffset(offset); + quick_resolution_trampoline_.reset(compiler_driver_->CreateQuickResolutionTrampoline()); + offset += quick_resolution_trampoline_->size(); + } else { + oat_header_->SetInterpreterToInterpreterEntryOffset(0); + oat_header_->SetInterpreterToQuickEntryOffset(0); + oat_header_->SetPortableResolutionTrampolineOffset(0); + oat_header_->SetQuickResolutionTrampolineOffset(0); + } return offset; } @@ -389,11 +440,13 @@ bool OatWriter::Write(OutputStream& out) { PLOG(ERROR) << "Failed to write oat header to " << out.GetLocation(); return false; } + size_oat_header_ += sizeof(*oat_header_); if (!out.WriteFully(image_file_location_.data(), image_file_location_.size())) { PLOG(ERROR) << "Failed to write oat header image file location to " << out.GetLocation(); return false; } + size_oat_header_image_file_location_ += image_file_location_.size(); if (!WriteTables(out)) { LOG(ERROR) << "Failed to write oat tables to " << out.GetLocation(); @@ -412,12 +465,47 @@ bool OatWriter::Write(OutputStream& out) { return false; } + if (kIsDebugBuild) { + uint32_t size_total = 0; + #define DO_STAT(x) \ + LOG(INFO) << #x "=" << PrettySize(x) << " (" << x << "B)"; \ + size_total += x; + + DO_STAT(size_dex_file_alignment_); + DO_STAT(size_executable_offset_alignment_); + DO_STAT(size_oat_header_); + DO_STAT(size_oat_header_image_file_location_); + DO_STAT(size_dex_file_); + DO_STAT(size_interpreter_to_interpreter_entry_); + DO_STAT(size_interpreter_to_quick_entry_); + DO_STAT(size_portable_resolution_trampoline_); + DO_STAT(size_quick_resolution_trampoline_); + DO_STAT(size_stubs_alignment_); + DO_STAT(size_code_size_); + DO_STAT(size_code_); + DO_STAT(size_code_alignment_); + DO_STAT(size_mapping_table_); + DO_STAT(size_vmap_table_); + DO_STAT(size_gc_map_); + DO_STAT(size_oat_dex_file_location_size_); + DO_STAT(size_oat_dex_file_location_data_); + DO_STAT(size_oat_dex_file_location_checksum_); + DO_STAT(size_oat_dex_file_offset_); + DO_STAT(size_oat_dex_file_methods_offsets_); + DO_STAT(size_oat_class_status_); + DO_STAT(size_oat_class_method_offsets_); + #undef DO_STAT + + LOG(INFO) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \ + CHECK_EQ(size_total, static_cast<uint32_t>(out.Seek(0, kSeekCurrent))); + } + return true; } bool OatWriter::WriteTables(OutputStream& out) { for (size_t i = 0; i != oat_dex_files_.size(); ++i) { - if (!oat_dex_files_[i]->Write(out)) { + if (!oat_dex_files_[i]->Write(this, out)) { PLOG(ERROR) << "Failed to write oat dex information to " << out.GetLocation(); return false; } @@ -436,9 +524,10 @@ bool OatWriter::WriteTables(OutputStream& out) { PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation() << " to " << out.GetLocation(); return false; } + size_dex_file_ += dex_file->GetHeader().file_size_; } for (size_t i = 0; i != oat_classes_.size(); ++i) { - if (!oat_classes_[i]->Write(out)) { + if (!oat_classes_[i]->Write(this, out)) { PLOG(ERROR) << "Failed to write oat methods information to " << out.GetLocation(); return false; } @@ -448,13 +537,59 @@ bool OatWriter::WriteTables(OutputStream& out) { size_t OatWriter::WriteCode(OutputStream& out) { uint32_t offset = oat_header_->GetExecutableOffset(); - off_t new_offset = out.Seek(executable_offset_padding_length_, kSeekCurrent); + off_t new_offset = out.Seek(size_executable_offset_alignment_, kSeekCurrent); if (static_cast<uint32_t>(new_offset) != offset) { PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset << " Expected: " << offset << " File: " << out.GetLocation(); return 0; } DCHECK_OFFSET(); + if (compiler_driver_->IsImage()) { + InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); + if (!out.WriteFully(&(*interpreter_to_interpreter_entry_)[0], interpreter_to_interpreter_entry_->size())) { + PLOG(ERROR) << "Failed to write interpreter to interpreter entry to " << out.GetLocation(); + return false; + } + size_interpreter_to_interpreter_entry_ += interpreter_to_interpreter_entry_->size(); + offset += interpreter_to_interpreter_entry_->size(); + DCHECK_OFFSET(); + + uint32_t aligned_offset = CompiledCode::AlignCode(offset, instruction_set); + uint32_t alignment_padding = aligned_offset - offset; + out.Seek(alignment_padding, kSeekCurrent); + size_stubs_alignment_ += alignment_padding; + if (!out.WriteFully(&(*interpreter_to_quick_entry_)[0], interpreter_to_quick_entry_->size())) { + PLOG(ERROR) << "Failed to write interpreter to quick entry to " << out.GetLocation(); + return false; + } + size_interpreter_to_quick_entry_ += interpreter_to_quick_entry_->size(); + offset += alignment_padding + interpreter_to_quick_entry_->size(); + DCHECK_OFFSET(); + + aligned_offset = CompiledCode::AlignCode(offset, instruction_set); + alignment_padding = aligned_offset - offset; + out.Seek(alignment_padding, kSeekCurrent); + size_stubs_alignment_ += alignment_padding; + if (!out.WriteFully(&(*portable_resolution_trampoline_)[0], portable_resolution_trampoline_->size())) { + PLOG(ERROR) << "Failed to write portable resolution trampoline to " << out.GetLocation(); + return false; + } + size_portable_resolution_trampoline_ += portable_resolution_trampoline_->size(); + offset += alignment_padding + portable_resolution_trampoline_->size(); + DCHECK_OFFSET(); + + aligned_offset = CompiledCode::AlignCode(offset, instruction_set); + alignment_padding = aligned_offset - offset; + out.Seek(alignment_padding, kSeekCurrent); + size_stubs_alignment_ += alignment_padding; + if (!out.WriteFully(&(*quick_resolution_trampoline_)[0], quick_resolution_trampoline_->size())) { + PLOG(ERROR) << "Failed to write quick resolution trampoline to " << out.GetLocation(); + return false; + } + size_quick_resolution_trampoline_ += quick_resolution_trampoline_->size(); + offset += alignment_padding + quick_resolution_trampoline_->size(); + DCHECK_OFFSET(); + } return offset; } @@ -547,6 +682,7 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c uint32_t aligned_code_delta = aligned_offset - offset; if (aligned_code_delta != 0) { off_t new_offset = out.Seek(aligned_code_delta, kSeekCurrent); + size_code_alignment_ += aligned_code_delta; if (static_cast<uint32_t>(new_offset) != aligned_offset) { PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset << " Expected: " << aligned_offset << " File: " << out.GetLocation(); @@ -572,12 +708,14 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c ReportWriteFailure("method code size", method_idx, dex_file, out); return 0; } + size_code_size_ += sizeof(code_size); offset += sizeof(code_size); DCHECK_OFFSET(); if (!out.WriteFully(&code[0], code_size)) { ReportWriteFailure("method code", method_idx, dex_file, out); return 0; } + size_code_ += code_size; offset += code_size; } DCHECK_OFFSET(); @@ -602,6 +740,7 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c ReportWriteFailure("mapping table", method_idx, dex_file, out); return 0; } + size_mapping_table_ += mapping_table_size; offset += mapping_table_size; } DCHECK_OFFSET(); @@ -625,6 +764,7 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c ReportWriteFailure("vmap table", method_idx, dex_file, out); return 0; } + size_vmap_table_ += vmap_table_size; offset += vmap_table_size; } DCHECK_OFFSET(); @@ -648,6 +788,7 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c ReportWriteFailure("GC map", method_idx, dex_file, out); return 0; } + size_gc_map_ += gc_map_size; offset += gc_map_size; } DCHECK_OFFSET(); @@ -683,29 +824,35 @@ void OatWriter::OatDexFile::UpdateChecksum(OatHeader& oat_header) const { sizeof(methods_offsets_[0]) * methods_offsets_.size()); } -bool OatWriter::OatDexFile::Write(OutputStream& out) const { +bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream& out) const { DCHECK_OFFSET_(); if (!out.WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) { PLOG(ERROR) << "Failed to write dex file location length to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_); if (!out.WriteFully(dex_file_location_data_, dex_file_location_size_)) { PLOG(ERROR) << "Failed to write dex file location data to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_; if (!out.WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) { PLOG(ERROR) << "Failed to write dex file location checksum to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_); if (!out.WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) { PLOG(ERROR) << "Failed to write dex file offset to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_); if (!out.WriteFully(&methods_offsets_[0], sizeof(methods_offsets_[0]) * methods_offsets_.size())) { PLOG(ERROR) << "Failed to write methods offsets to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_methods_offsets_ += + sizeof(methods_offsets_[0]) * methods_offsets_.size(); return true; } @@ -736,12 +883,13 @@ void OatWriter::OatClass::UpdateChecksum(OatHeader& oat_header) const { sizeof(method_offsets_[0]) * method_offsets_.size()); } -bool OatWriter::OatClass::Write(OutputStream& out) const { +bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream& out) const { DCHECK_OFFSET_(); if (!out.WriteFully(&status_, sizeof(status_))) { PLOG(ERROR) << "Failed to write class status to " << out.GetLocation(); return false; } + oat_writer->size_oat_class_status_ += sizeof(status_); DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(0)), out.Seek(0, kSeekCurrent)); if (!out.WriteFully(&method_offsets_[0], @@ -749,6 +897,7 @@ bool OatWriter::OatClass::Write(OutputStream& out) const { PLOG(ERROR) << "Failed to write method offsets to " << out.GetLocation(); return false; } + oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size(); DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())), out.Seek(0, kSeekCurrent)); return true; diff --git a/src/oat_writer.h b/src/oat_writer.h index e1d76f459f..b201d6b4ee 100644 --- a/src/oat_writer.h +++ b/src/oat_writer.h @@ -83,7 +83,8 @@ class OatWriter { size_t InitOatDexFiles(size_t offset); size_t InitDexFiles(size_t offset); size_t InitOatClasses(size_t offset); - size_t InitOatCode(size_t offset); + size_t InitOatCode(size_t offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t InitOatCodeDexFiles(size_t offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t InitOatCodeDexFile(size_t offset, @@ -120,7 +121,7 @@ class OatWriter { explicit OatDexFile(size_t offset, const DexFile& dex_file); size_t SizeOf() const; void UpdateChecksum(OatHeader& oat_header) const; - bool Write(OutputStream& out) const; + bool Write(OatWriter* oat_writer, OutputStream& out) const; // Offset of start of OatDexFile from beginning of OatHeader. It is // used to validate file position when writing. @@ -144,7 +145,7 @@ class OatWriter { size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const; size_t SizeOf() const; void UpdateChecksum(OatHeader& oat_header) const; - bool Write(OutputStream& out) const; + bool Write(OatWriter* oat_writer, OutputStream& out) const; // Offset of start of OatClass from beginning of OatHeader. It is // used to validate file position when writing. For Portable, it @@ -175,7 +176,35 @@ class OatWriter { OatHeader* oat_header_; std::vector<OatDexFile*> oat_dex_files_; std::vector<OatClass*> oat_classes_; - uint32_t executable_offset_padding_length_; + UniquePtr<const std::vector<uint8_t> > interpreter_to_interpreter_entry_; + UniquePtr<const std::vector<uint8_t> > interpreter_to_quick_entry_; + UniquePtr<const std::vector<uint8_t> > portable_resolution_trampoline_; + UniquePtr<const std::vector<uint8_t> > quick_resolution_trampoline_; + + // output stats + uint32_t size_dex_file_alignment_; + uint32_t size_executable_offset_alignment_; + uint32_t size_oat_header_; + uint32_t size_oat_header_image_file_location_; + uint32_t size_dex_file_; + uint32_t size_interpreter_to_interpreter_entry_; + uint32_t size_interpreter_to_quick_entry_; + uint32_t size_portable_resolution_trampoline_; + uint32_t size_quick_resolution_trampoline_; + uint32_t size_stubs_alignment_; + uint32_t size_code_size_; + uint32_t size_code_; + uint32_t size_code_alignment_; + uint32_t size_mapping_table_; + uint32_t size_vmap_table_; + uint32_t size_gc_map_; + uint32_t size_oat_dex_file_location_size_; + uint32_t size_oat_dex_file_location_data_; + uint32_t size_oat_dex_file_location_checksum_; + uint32_t size_oat_dex_file_offset_; + uint32_t size_oat_dex_file_methods_offsets_; + uint32_t size_oat_class_status_; + uint32_t size_oat_class_method_offsets_; template <class T> struct MapCompare { public: diff --git a/src/oatdump.cc b/src/oatdump.cc index 7a99f8dc0e..f9caa9d127 100644 --- a/src/oatdump.cc +++ b/src/oatdump.cc @@ -30,8 +30,9 @@ #include "dex_instruction.h" #include "disassembler.h" #include "gc_map.h" -#include "gc/large_object_space.h" -#include "gc/space.h" +#include "gc/space/image_space.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" #include "image.h" #include "indenter.h" #include "mirror/abstract_method-inl.h" @@ -679,7 +680,7 @@ class OatDumper { class ImageDumper { public: explicit ImageDumper(std::ostream* os, const std::string& image_filename, - const std::string& host_prefix, Space& image_space, + const std::string& host_prefix, gc::space::ImageSpace& image_space, const ImageHeader& image_header) : os_(os), image_filename_(image_filename), host_prefix_(host_prefix), image_space_(image_space), image_header_(image_header) {} @@ -763,8 +764,8 @@ class ImageDumper { os << "OBJECTS:\n" << std::flush; // Loop through all the image spaces and dump their objects. - Heap* heap = Runtime::Current()->GetHeap(); - const Spaces& spaces = heap->GetSpaces(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); Thread* self = Thread::Current(); { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); @@ -777,10 +778,11 @@ class ImageDumper { os_ = &indent_os; ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); // TODO: C++0x auto - for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) { - Space* space = *it; + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::Space* space = *it; if (space->IsImageSpace()) { - ImageSpace* image_space = space->AsImageSpace(); + gc::space::ImageSpace* image_space = space->AsImageSpace(); image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this); indent_os << "\n"; } @@ -853,7 +855,13 @@ class ImageDumper { if (value == NULL) { os << StringPrintf("null %s\n", PrettyDescriptor(descriptor).c_str()); } else { - PrettyObjectValue(os, fh.GetType(), value); + // Grab the field type without causing resolution. + mirror::Class* field_type = fh.GetType(false); + if (field_type != NULL) { + PrettyObjectValue(os, field_type, value); + } else { + os << StringPrintf("%p %s\n", value, PrettyDescriptor(descriptor).c_str()); + } } } } @@ -880,7 +888,7 @@ class ImageDumper { const void* GetOatCodeBegin(mirror::AbstractMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const void* code = m->GetEntryPointFromCompiledCode(); - if (code == GetResolutionTrampoline()) { + if (code == GetResolutionTrampoline(Runtime::Current()->GetClassLinker())) { code = oat_dumper_->GetOatCode(m); } if (oat_dumper_->GetInstructionSet() == kThumb2) { @@ -1337,7 +1345,7 @@ class ImageDumper { std::ostream* os_; const std::string image_filename_; const std::string host_prefix_; - Space& image_space_; + gc::space::ImageSpace& image_space_; const ImageHeader& image_header_; DISALLOW_COPY_AND_ASSIGN(ImageDumper); @@ -1448,8 +1456,8 @@ static int oatdump(int argc, char** argv) { Thread::Current()->TransitionFromRunnableToSuspended(kNative); ScopedObjectAccess soa(Thread::Current()); - Heap* heap = Runtime::Current()->GetHeap(); - ImageSpace* image_space = heap->GetImageSpace(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::space::ImageSpace* image_space = heap->GetImageSpace(); CHECK(image_space != NULL); const ImageHeader& image_header = image_space->GetImageHeader(); if (!image_header.IsValid()) { diff --git a/src/object_utils.h b/src/object_utils.h index 6c6f60b6c3..6a0742557d 100644 --- a/src/object_utils.h +++ b/src/object_utils.h @@ -282,13 +282,13 @@ class FieldHelper { return field_index == 0 ? "interfaces" : "throws"; } } - mirror::Class* GetType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t field_index = field_->GetDexFieldIndex(); if (!field_->GetDeclaringClass()->IsProxyClass()) { const DexFile& dex_file = GetDexFile(); const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); mirror::Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_); - if (type == NULL) { + if (resolve && (type == NULL)) { type = GetClassLinker()->ResolveType(field_id.type_idx_, field_); CHECK(type != NULL || Thread::Current()->IsExceptionPending()); } diff --git a/src/output_stream_test.cc b/src/output_stream_test.cc index 0e02825ff8..c9e0edefcd 100644 --- a/src/output_stream_test.cc +++ b/src/output_stream_test.cc @@ -25,7 +25,7 @@ class OutputStreamTest : public CommonTest { protected: void CheckOffset(off_t expected) { off_t actual = output_stream_->Seek(0, kSeekCurrent); - CHECK_EQ(expected, actual); + EXPECT_EQ(expected, actual); } void SetOutputStream(OutputStream& output_stream) { @@ -33,16 +33,16 @@ class OutputStreamTest : public CommonTest { } void GenerateTestOutput() { - CHECK_EQ(3, output_stream_->Seek(3, kSeekCurrent)); + EXPECT_EQ(3, output_stream_->Seek(3, kSeekCurrent)); CheckOffset(3); - CHECK_EQ(2, output_stream_->Seek(2, kSeekSet)); + EXPECT_EQ(2, output_stream_->Seek(2, kSeekSet)); CheckOffset(2); uint8_t buf[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - CHECK(output_stream_->WriteFully(buf, 2)); + EXPECT_TRUE(output_stream_->WriteFully(buf, 2)); CheckOffset(4); - CHECK_EQ(6, output_stream_->Seek(2, kSeekEnd)); + EXPECT_EQ(6, output_stream_->Seek(2, kSeekEnd)); CheckOffset(6); - CHECK(output_stream_->WriteFully(buf, 4)); + EXPECT_TRUE(output_stream_->WriteFully(buf, 4)); CheckOffset(10); } @@ -50,8 +50,8 @@ class OutputStreamTest : public CommonTest { uint8_t expected[] = { 0, 0, 1, 2, 0, 0, 1, 2, 3, 4 }; - CHECK_EQ(sizeof(expected), actual.size()); - CHECK_EQ(0, memcmp(expected, &actual[0], actual.size())); + EXPECT_EQ(sizeof(expected), actual.size()); + EXPECT_EQ(0, memcmp(expected, &actual[0], actual.size())); } OutputStream* output_stream_; @@ -63,10 +63,10 @@ TEST_F(OutputStreamTest, File) { SetOutputStream(output_stream); GenerateTestOutput(); UniquePtr<File> in(OS::OpenFile(tmp.GetFilename().c_str(), false)); - CHECK(in.get() != NULL); + EXPECT_TRUE(in.get() != NULL); std::vector<uint8_t> actual(in->GetLength()); bool readSuccess = in->ReadFully(&actual[0], actual.size()); - CHECK(readSuccess); + EXPECT_TRUE(readSuccess); CheckTestOutput(actual); } diff --git a/src/runtime.cc b/src/runtime.cc index 2fc104ad8e..3a528a125e 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -34,8 +34,9 @@ #include "constants_mips.h" #include "constants_x86.h" #include "debugger.h" -#include "gc/card_table-inl.h" -#include "heap.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/heap.h" +#include "gc/space/space.h" #include "image.h" #include "instrumentation.h" #include "intern_table.h" @@ -55,7 +56,6 @@ #include "signal_catcher.h" #include "signal_set.h" #include "sirt_ref.h" -#include "gc/space.h" #include "thread.h" #include "thread_list.h" #include "trace.h" @@ -210,29 +210,18 @@ void Runtime::Abort() { LOG(INTERNAL_FATAL) << "Unexpectedly returned from abort hook!"; } -#if defined(__BIONIC__) - // TODO: finish merging patches to fix abort(3) in bionic, then lose this! - // Bionic doesn't implement POSIX semantics for abort(3) in a multi-threaded - // process, so if we call abort(3) on a device, all threads in the process - // receive SIGABRT. debuggerd dumps the stack trace of the main - // thread, whether or not that was the thread that failed. By - // stuffing a value into a bogus address, we cause a segmentation - // fault in the current thread, and get a useful log from debuggerd. - // We can also trivially tell the difference between a crash and - // a deliberate abort by looking at the fault address. - *reinterpret_cast<char*>(0xdeadd00d) = 38; -#elif defined(__APPLE__) - // TODO: check that this actually gives good stack traces on the Mac! - pthread_kill(pthread_self(), SIGABRT); -#else +#if defined(__GLIBC__) // TODO: we ought to be able to use pthread_kill(3) here (or abort(3), // which POSIX defines in terms of raise(3), which POSIX defines in terms // of pthread_kill(3)). On Linux, though, libcorkscrew can't unwind through // libpthread, which means the stacks we dump would be useless. Calling // tgkill(2) directly avoids that. syscall(__NR_tgkill, getpid(), GetTid(), SIGABRT); - // TODO: LLVM installs it's own SIGABRT handler so exit to be safe... Can we disable that? + // TODO: LLVM installs it's own SIGABRT handler so exit to be safe... Can we disable that in LLVM? + // If not, we could use sigaction(3) before calling tgkill(2) and lose this call to exit(3). exit(1); +#else + abort(); #endif // notreached } @@ -343,11 +332,11 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b // -Xcheck:jni is off by default for regular builds but on by default in debug builds. parsed->check_jni_ = kIsDebugBuild; - parsed->heap_initial_size_ = Heap::kDefaultInitialSize; - parsed->heap_maximum_size_ = Heap::kDefaultMaximumSize; - parsed->heap_min_free_ = Heap::kDefaultMinFree; - parsed->heap_max_free_ = Heap::kDefaultMaxFree; - parsed->heap_target_utilization_ = Heap::kDefaultTargetUtilization; + parsed->heap_initial_size_ = gc::Heap::kDefaultInitialSize; + parsed->heap_maximum_size_ = gc::Heap::kDefaultMaximumSize; + parsed->heap_min_free_ = gc::Heap::kDefaultMinFree; + parsed->heap_max_free_ = gc::Heap::kDefaultMaxFree; + parsed->heap_target_utilization_ = gc::Heap::kDefaultTargetUtilization; parsed->heap_growth_limit_ = 0; // 0 means no growth limit. parsed->stack_size_ = 0; // 0 means default. @@ -368,6 +357,7 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->small_mode_method_threshold_ = Runtime::kDefaultSmallModeMethodThreshold; parsed->small_mode_method_dex_size_limit_ = Runtime::kDefaultSmallModeMethodDexSizeLimit; + parsed->sea_ir_mode_ = false; // gLogVerbosity.class_linker = true; // TODO: don't check this in! // gLogVerbosity.compiler = true; // TODO: don't check this in! // gLogVerbosity.heap = true; // TODO: don't check this in! @@ -577,6 +567,8 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b Trace::SetDefaultClockSource(kProfilerClockSourceDual); } else if (option == "-small") { parsed->small_mode_ = true; + }else if (option == "-sea_ir") { + parsed->sea_ir_mode_ = true; } else if (StartsWith(option, "-small-mode-methods-max:")) { parsed->small_mode_method_threshold_ = ParseIntegerOrDie(option); } else if (StartsWith(option, "-small-mode-methods-size-max:")) { @@ -815,6 +807,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { small_mode_method_threshold_ = options->small_mode_method_threshold_; small_mode_method_dex_size_limit_ = options->small_mode_method_dex_size_limit_; + sea_ir_mode_ = options->sea_ir_mode_; vfprintf_ = options->hook_vfprintf_; exit_ = options->hook_exit_; abort_ = options->hook_abort_; @@ -831,14 +824,14 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { GetInstrumentation()->ForceInterpretOnly(); } - heap_ = new Heap(options->heap_initial_size_, - options->heap_growth_limit_, - options->heap_min_free_, - options->heap_max_free_, - options->heap_target_utilization_, - options->heap_maximum_size_, - options->image_, - options->is_concurrent_gc_enabled_); + heap_ = new gc::Heap(options->heap_initial_size_, + options->heap_growth_limit_, + options->heap_min_free_, + options->heap_max_free_, + options->heap_target_utilization_, + options->heap_maximum_size_, + options->image_, + options->is_concurrent_gc_enabled_); BlockSignals(); InitPlatformSignalHandlers(); @@ -860,8 +853,8 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { // Now we're attached, we can take the heap lock and validate the heap. GetHeap()->EnableObjectValidation(); - CHECK_GE(GetHeap()->GetSpaces().size(), 1U); - if (GetHeap()->GetSpaces()[0]->IsImageSpace()) { + CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); + if (GetHeap()->GetContinuousSpaces()[0]->IsImageSpace()) { class_linker_ = ClassLinker::CreateFromImage(intern_table_); } else { CHECK(options->boot_class_path_ != NULL); @@ -1073,12 +1066,13 @@ void Runtime::DetachCurrentThread() { thread_list_->Unregister(self); } -void Runtime::VisitConcurrentRoots(RootVisitor* visitor, void* arg) { - if (intern_table_->IsDirty()) { - intern_table_->VisitRoots(visitor, arg); +void Runtime::VisitConcurrentRoots(RootVisitor* visitor, void* arg, bool only_dirty, + bool clean_dirty) { + if (!only_dirty || intern_table_->IsDirty()) { + intern_table_->VisitRoots(visitor, arg, clean_dirty); } - if (class_linker_->IsDirty()) { - class_linker_->VisitRoots(visitor, arg); + if (!only_dirty || class_linker_->IsDirty()) { + class_linker_->VisitRoots(visitor, arg, clean_dirty); } } @@ -1098,15 +1092,8 @@ void Runtime::VisitNonConcurrentRoots(RootVisitor* visitor, void* arg) { VisitNonThreadRoots(visitor, arg); } -void Runtime::DirtyRoots() { - CHECK(intern_table_ != NULL); - intern_table_->Dirty(); - CHECK(class_linker_ != NULL); - class_linker_->Dirty(); -} - -void Runtime::VisitRoots(RootVisitor* visitor, void* arg) { - VisitConcurrentRoots(visitor, arg); +void Runtime::VisitRoots(RootVisitor* visitor, void* arg, bool only_dirty, bool clean_dirty) { + VisitConcurrentRoots(visitor, arg, only_dirty, clean_dirty); VisitNonConcurrentRoots(visitor, arg); } @@ -1119,7 +1106,9 @@ mirror::AbstractMethod* Runtime::CreateResolutionMethod() { // TODO: use a special method for resolution method saves method->SetDexMethodIndex(DexFile::kDexNoIndex16); // When compiling, the code pointer will get set later when the image is loaded. - method->SetEntryPointFromCompiledCode(Runtime::Current()->IsCompiler() ? NULL : GetResolutionTrampoline()); + Runtime* r = Runtime::Current(); + ClassLinker* cl = r->GetClassLinker(); + method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetResolutionTrampoline(cl)); return method.get(); } diff --git a/src/runtime.h b/src/runtime.h index 6f03fe0d68..0b893a341d 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -27,8 +27,8 @@ #include "base/macros.h" #include "base/stringpiece.h" +#include "gc/heap.h" #include "globals.h" -#include "heap.h" #include "instruction_set.h" #include "instrumentation.h" #include "jobject_comparator.h" @@ -39,17 +39,19 @@ namespace art { +namespace gc { + class Heap; +} namespace mirror { -class AbstractMethod; -class ClassLoader; -template<class T> class PrimitiveArray; -typedef PrimitiveArray<int8_t> ByteArray; -class String; -class Throwable; + class AbstractMethod; + class ClassLoader; + template<class T> class PrimitiveArray; + typedef PrimitiveArray<int8_t> ByteArray; + class String; + class Throwable; } // namespace mirror class ClassLinker; class DexFile; -class Heap; class InternTable; struct JavaVMExt; class MonitorList; @@ -63,11 +65,13 @@ class Runtime { // In small mode, apps with fewer than this number of methods will be compiled // anyways. + // TODO: come up with a reasonable default. static const size_t kDefaultSmallModeMethodThreshold = 0; // In small mode, methods smaller than this dex op count limit will get compiled // anyways. - static const size_t kDefaultSmallModeMethodDexSizeLimit = 0; + // TODO: come up with a reasonable default. + static const size_t kDefaultSmallModeMethodDexSizeLimit = 300; class ParsedOptions { public: @@ -104,9 +108,12 @@ class Runtime { void (*hook_abort_)(); std::vector<std::string> properties_; bool small_mode_; + size_t small_mode_method_threshold_; size_t small_mode_method_dex_size_limit_; + bool sea_ir_mode_; + private: ParsedOptions() {} }; @@ -127,6 +134,14 @@ class Runtime { return is_concurrent_gc_enabled_; } + bool IsSeaIRMode() const { + return sea_ir_mode_; + } + + void SetSeaIRMode(bool sea_ir_mode) { + sea_ir_mode_ = sea_ir_mode; + } + bool IsSmallMode() const { return small_mode_; } @@ -222,7 +237,7 @@ class Runtime { return default_stack_size_; } - Heap* GetHeap() const { + gc::Heap* GetHeap() const { return heap_; } @@ -254,14 +269,13 @@ class Runtime { return "2.0.0"; } - // Force all the roots which can be marked concurrently to be dirty. - void DirtyRoots(); - - // Visit all the roots. - void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Visit all the roots. If only_dirty is true then non-dirty roots won't be visited. If + // clean_dirty is true then dirty roots will be marked as non-dirty after visiting. + void VisitRoots(RootVisitor* visitor, void* arg, bool only_dirty, bool clean_dirty) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Visit all of the roots we can do safely do concurrently. - void VisitConcurrentRoots(RootVisitor* visitor, void* arg); + void VisitConcurrentRoots(RootVisitor* visitor, void* arg, bool only_dirty, bool clean_dirty); // Visit all of the non thread roots, we can do this with mutators unpaused. void VisitNonThreadRoots(RootVisitor* visitor, void* arg); @@ -371,6 +385,8 @@ class Runtime { size_t small_mode_method_threshold_; size_t small_mode_method_dex_size_limit_; + bool sea_ir_mode_; + // The host prefix is used during cross compilation. It is removed // from the start of host paths such as: // $ANDROID_PRODUCT_OUT/system/framework/boot.oat @@ -390,7 +406,7 @@ class Runtime { // The default stack size for managed threads created by the runtime. size_t default_stack_size_; - Heap* heap_; + gc::Heap* heap_; MonitorList* monitor_list_; diff --git a/src/runtime_support.cc b/src/runtime_support.cc index 9242c8790b..c933621981 100644 --- a/src/runtime_support.cc +++ b/src/runtime_support.cc @@ -18,7 +18,7 @@ #include "class_linker-inl.h" #include "dex_file-inl.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "mirror/abstract_method-inl.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" @@ -142,7 +142,8 @@ mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::AbstractMet } mirror::Field* FindFieldFromCode(uint32_t field_idx, const mirror::AbstractMethod* referrer, - Thread* self, FindFieldType type, size_t expected_size) { + Thread* self, FindFieldType type, size_t expected_size, + bool access_check) { bool is_primitive; bool is_set; bool is_static; @@ -162,12 +163,13 @@ mirror::Field* FindFieldFromCode(uint32_t field_idx, const mirror::AbstractMetho if (UNLIKELY(resolved_field == NULL)) { DCHECK(self->IsExceptionPending()); // Throw exception and unwind. return NULL; // Failure. - } else { + } + mirror::Class* fields_class = resolved_field->GetDeclaringClass(); + if (access_check) { if (UNLIKELY(resolved_field->IsStatic() != is_static)) { ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer); return NULL; } - mirror::Class* fields_class = resolved_field->GetDeclaringClass(); mirror::Class* referring_class = referrer->GetDeclaringClass(); if (UNLIKELY(!referring_class->CanAccess(fields_class) || !referring_class->CanAccessMember(fields_class, @@ -203,23 +205,24 @@ mirror::Field* FindFieldFromCode(uint32_t field_idx, const mirror::AbstractMetho is_primitive ? "primitive" : "non-primitive", PrettyField(resolved_field, true).c_str()); return NULL; // failure - } else if (!is_static) { - // instance fields must be being accessed on an initialized class - return resolved_field; - } else { - // If the class is initialized we're done. - if (fields_class->IsInitialized()) { - return resolved_field; - } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true, true)) { - // Otherwise let's ensure the class is initialized before resolving the field. - return resolved_field; - } else { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind - return NULL; // failure - } } } } + if (!is_static) { + // instance fields must be being accessed on an initialized class + return resolved_field; + } else { + // If the class is initialized we're done. + if (fields_class->IsInitialized()) { + return resolved_field; + } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true, true)) { + // Otherwise let's ensure the class is initialized before resolving the field. + return resolved_field; + } else { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind + return NULL; // failure + } + } } // Slow path method resolution diff --git a/src/runtime_support.h b/src/runtime_support.h index 5fc8da53b6..0cb82a5466 100644 --- a/src/runtime_support.h +++ b/src/runtime_support.h @@ -34,14 +34,12 @@ extern "C" void art_interpreter_invoke_handler(); extern "C" void art_jni_dlsym_lookup_stub(); extern "C" void art_portable_abstract_method_error_stub(); extern "C" void art_portable_proxy_invoke_handler(); -extern "C" void art_portable_resolution_trampoline(); extern "C" void art_quick_abstract_method_error_stub(); extern "C" void art_quick_deoptimize(); extern "C" void art_quick_instrumentation_entry_from_code(void*); extern "C" void art_quick_instrumentation_exit_from_code(); extern "C" void art_quick_interpreter_entry(void*); extern "C" void art_quick_proxy_invoke_handler(); -extern "C" void art_quick_resolution_trampoline(); extern "C" void art_work_around_app_jni_bugs(); extern "C" double art_l2d(int64_t l); @@ -146,7 +144,8 @@ enum FindFieldType { // Slow field find that can initialize classes and may throw exceptions. extern mirror::Field* FindFieldFromCode(uint32_t field_idx, const mirror::AbstractMethod* referrer, - Thread* self, FindFieldType type, size_t expected_size) + Thread* self, FindFieldType type, size_t expected_size, + bool access_check) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Fast path field resolution that can't initialize classes or throw exceptions. @@ -373,22 +372,20 @@ static inline void* GetInterpreterEntryPoint() { return reinterpret_cast<void*>(art_quick_interpreter_entry); } -// Return address of portable resolution trampoline stub. -static inline void* GetPortableResolutionTrampoline() { - return reinterpret_cast<void*>(art_portable_resolution_trampoline); +static inline const void* GetPortableResolutionTrampoline(ClassLinker* class_linker) { + return class_linker->GetPortableResolutionTrampoline(); } -// Return address of quick resolution trampoline stub. -static inline void* GetQuickResolutionTrampoline() { - return reinterpret_cast<void*>(art_quick_resolution_trampoline); +static inline const void* GetQuickResolutionTrampoline(ClassLinker* class_linker) { + return class_linker->GetQuickResolutionTrampoline(); } // Return address of resolution trampoline stub for defined compiler. -static inline void* GetResolutionTrampoline() { +static inline const void* GetResolutionTrampoline(ClassLinker* class_linker) { #if defined(ART_USE_PORTABLE_COMPILER) - return GetPortableResolutionTrampoline(); + return GetPortableResolutionTrampoline(class_linker); #else - return GetQuickResolutionTrampoline(); + return GetQuickResolutionTrampoline(class_linker); #endif } diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc index c021dd1d2c..a630db810a 100644 --- a/src/signal_catcher.cc +++ b/src/signal_catcher.cc @@ -27,7 +27,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" -#include "heap.h" +#include "gc/heap.h" #include "os.h" #include "runtime.h" #include "scoped_thread_state_change.h" diff --git a/src/stack.cc b/src/stack.cc index 8690a36387..8672975453 100644 --- a/src/stack.cc +++ b/src/stack.cc @@ -42,6 +42,15 @@ mirror::Object* ShadowFrame::GetThisObject() const { } } +mirror::Object* ShadowFrame::GetThisObject(uint16_t num_ins) const { + mirror::AbstractMethod* m = GetMethod(); + if (m->IsStatic()) { + return NULL; + } else { + return GetVRegReference(NumberOfVRegs() - num_ins); + } +} + ThrowLocation ShadowFrame::GetCurrentLocationForThrow() const { return ThrowLocation(GetThisObject(), GetMethod(), GetDexPC()); } diff --git a/src/stack.h b/src/stack.h index 1b4d285216..fbfacb1733 100644 --- a/src/stack.h +++ b/src/stack.h @@ -202,6 +202,8 @@ class ShadowFrame { mirror::Object* GetThisObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* GetThisObject(uint16_t num_ins) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ThrowLocation GetCurrentLocationForThrow() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetMethod(mirror::AbstractMethod* method) { @@ -254,13 +256,9 @@ class ShadowFrame { CHECK_LT(num_vregs, static_cast<uint32_t>(kHasReferenceArray)); number_of_vregs_ |= kHasReferenceArray; #endif - for (size_t i = 0; i < num_vregs; ++i) { - SetVRegReference(i, NULL); - } + memset(vregs_, 0, num_vregs * (sizeof(uint32_t) + sizeof(mirror::Object*))); } else { - for (size_t i = 0; i < num_vregs; ++i) { - SetVReg(i, 0); - } + memset(vregs_, 0, num_vregs * sizeof(uint32_t)); } } diff --git a/src/thread-inl.h b/src/thread-inl.h index 6c1ae59b60..2fc5987306 100644 --- a/src/thread-inl.h +++ b/src/thread-inl.h @@ -125,7 +125,7 @@ inline ThreadState Thread::TransitionFromSuspendedToRunnable() { } inline void Thread::VerifyStack() { - Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = Runtime::Current()->GetHeap(); if (heap->IsObjectValidationEnabled()) { VerifyStackImpl(); } diff --git a/src/thread.cc b/src/thread.cc index c5bfb20ddb..d6bd8a45a6 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -14,8 +14,11 @@ * limitations under the License. */ +#define ATRACE_TAG ATRACE_TAG_DALVIK + #include "thread.h" +#include <cutils/trace.h> #include <pthread.h> #include <signal.h> #include <sys/resource.h> @@ -35,8 +38,9 @@ #include "debugger.h" #include "dex_file-inl.h" #include "gc_map.h" -#include "gc/card_table-inl.h" -#include "heap.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/heap.h" +#include "gc/space/space.h" #include "invoke_arg_array_builder.h" #include "jni_internal.h" #include "mirror/abstract_method-inl.h" @@ -53,8 +57,8 @@ #include "runtime_support.h" #include "scoped_thread_state_change.h" #include "ScopedLocalRef.h" +#include "ScopedUtfChars.h" #include "sirt_ref.h" -#include "gc/space.h" #include "stack.h" #include "stack_indirect_reference_table.h" #include "thread-inl.h" @@ -169,7 +173,7 @@ void* Thread::CreateCallback(void* arg) { Thread* Thread::FromManagedThread(const ScopedObjectAccessUnchecked& soa, mirror::Object* thread_peer) { - mirror::Field* f = soa.DecodeField(WellKnownClasses::java_lang_Thread_vmData); + mirror::Field* f = soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer); Thread* result = reinterpret_cast<Thread*>(static_cast<uintptr_t>(f->GetInt(thread_peer))); // Sanity check that if we have a result it is either suspended or we hold the thread_list_lock_ // to stop it from going away. @@ -275,9 +279,9 @@ void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_siz child_thread->jpeer_ = env->NewGlobalRef(java_peer); stack_size = FixStackSize(stack_size); - // Thread.start is synchronized, so we know that vmData is 0, and know that we're not racing to + // Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing to // assign it. - env->SetIntField(java_peer, WellKnownClasses::java_lang_Thread_vmData, + env->SetIntField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, reinterpret_cast<jint>(child_thread)); pthread_t new_pthread; @@ -300,7 +304,7 @@ void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_siz delete child_thread; child_thread = NULL; // TODO: remove from thread group? - env->SetIntField(java_peer, WellKnownClasses::java_lang_Thread_vmData, 0); + env->SetIntField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0); { std::string msg(StringPrintf("pthread_create (%s stack) failed: %s", PrettySize(stack_size).c_str(), strerror(pthread_create_result))); @@ -405,7 +409,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) Thread* self = this; DCHECK_EQ(self, Thread::Current()); - jni_env_->SetIntField(peer.get(), WellKnownClasses::java_lang_Thread_vmData, + jni_env_->SetIntField(peer.get(), WellKnownClasses::java_lang_Thread_nativePeer, reinterpret_cast<jint>(self)); ScopedObjectAccess soa(self); @@ -573,6 +577,13 @@ void Thread::ModifySuspendCount(Thread* self, int delta, bool for_debugger) { } } +void Thread::RunCheckpointFunction() { + CHECK(checkpoint_function_ != NULL); + ATRACE_BEGIN("Checkpoint function"); + checkpoint_function_->Run(this); + ATRACE_END(); +} + bool Thread::RequestCheckpoint(Closure* function) { CHECK(!ReadFlag(kCheckpointRequest)) << "Already have a pending checkpoint request"; checkpoint_function_ = function; @@ -588,10 +599,12 @@ bool Thread::RequestCheckpoint(Closure* function) { void Thread::FullSuspendCheck() { VLOG(threads) << this << " self-suspending"; + ATRACE_BEGIN("Full suspend check"); // Make thread appear suspended to other threads, release mutator_lock_. TransitionFromRunnableToSuspended(kSuspended); // Transition back to runnable noting requests to suspend, re-acquire share on mutator_lock_. TransitionFromSuspendedToRunnable(); + ATRACE_END(); VLOG(threads) << this << " self-reviving"; } @@ -605,10 +618,22 @@ Thread* Thread::SuspendForDebugger(jobject peer, bool request_suspension, bool* Thread* thread; { ScopedObjectAccess soa(Thread::Current()); - MutexLock mu(soa.Self(), *Locks::thread_list_lock_); + Thread* self = soa.Self(); + MutexLock mu(self, *Locks::thread_list_lock_); thread = Thread::FromManagedThread(soa, peer); if (thread == NULL) { - LOG(WARNING) << "No such thread for suspend: " << peer; + JNIEnv* env = self->GetJniEnv(); + ScopedLocalRef<jstring> scoped_name_string(env, + (jstring)env->GetObjectField(peer, + WellKnownClasses::java_lang_Thread_name)); + ScopedUtfChars scoped_name_chars(env,scoped_name_string.get()); + if (scoped_name_chars.c_str() == NULL) { + LOG(WARNING) << "No such thread for suspend: " << peer; + env->ExceptionClear(); + } else { + LOG(WARNING) << "No such thread for suspend: " << peer << ":" << scoped_name_chars.c_str(); + } + return NULL; } { @@ -865,9 +890,10 @@ void Thread::DumpStack(std::ostream& os) const { // TODO: we call this code when dying but may not have suspended the thread ourself. The // IsSuspended check is therefore racy with the use for dumping (normally we inhibit // the race with the thread_suspend_count_lock_). - if (this == Thread::Current() || IsSuspended()) { + bool dump_for_abort = (gAborting > 0); + if (this == Thread::Current() || IsSuspended() || dump_for_abort) { // If we're currently in native code, dump that stack before dumping the managed stack. - if (ShouldShowNativeStack(this)) { + if (dump_for_abort || ShouldShowNativeStack(this)) { DumpKernelStack(os, GetTid(), " kernel: ", false); DumpNativeStack(os, GetTid(), " native: ", false); } @@ -1013,8 +1039,8 @@ void Thread::Destroy() { HandleUncaughtExceptions(soa); RemoveFromThreadGroup(soa); - // this.vmData = 0; - soa.DecodeField(WellKnownClasses::java_lang_Thread_vmData)->SetInt(opeer_, 0); + // this.nativePeer = 0; + soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetInt(opeer_, 0); Dbg::PostThreadDeath(self); // Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone @@ -1643,10 +1669,14 @@ static const EntryPointInfo gThreadEntryPointInfo[] = { ENTRY_POINT_INFO(pShlLong), ENTRY_POINT_INFO(pShrLong), ENTRY_POINT_INFO(pUshrLong), + ENTRY_POINT_INFO(pInterpreterToInterpreterEntry), + ENTRY_POINT_INFO(pInterpreterToQuickEntry), ENTRY_POINT_INFO(pIndexOf), ENTRY_POINT_INFO(pMemcmp16), ENTRY_POINT_INFO(pStringCompareTo), ENTRY_POINT_INFO(pMemcpy), + ENTRY_POINT_INFO(pPortableResolutionTrampolineFromCode), + ENTRY_POINT_INFO(pQuickResolutionTrampolineFromCode), ENTRY_POINT_INFO(pInvokeDirectTrampolineWithAccessCheck), ENTRY_POINT_INFO(pInvokeInterfaceTrampoline), ENTRY_POINT_INFO(pInvokeInterfaceTrampolineWithAccessCheck), @@ -2180,7 +2210,7 @@ void Thread::VisitRoots(RootVisitor* visitor, void* arg) { } static void VerifyObject(const mirror::Object* root, void* arg) { - Heap* heap = reinterpret_cast<Heap*>(arg); + gc::Heap* heap = reinterpret_cast<gc::Heap*>(arg); heap->VerifyObject(root); } diff --git a/src/thread.h b/src/thread.h index 24987cd441..0daf763359 100644 --- a/src/thread.h +++ b/src/thread.h @@ -554,10 +554,7 @@ class PACKED(4) Thread { held_mutexes_[level] = mutex; } - void RunCheckpointFunction() { - CHECK(checkpoint_function_ != NULL); - checkpoint_function_->Run(this); - } + void RunCheckpointFunction(); bool ReadFlag(ThreadFlag flag) const { return (state_and_flags_.as_struct.flags & flag) != 0; diff --git a/src/thread_list.cc b/src/thread_list.cc index ebb63ddc1f..eacd848171 100644 --- a/src/thread_list.cc +++ b/src/thread_list.cc @@ -466,10 +466,8 @@ void ThreadList::WaitForOtherNonDaemonThreadsToExit() { all_threads_are_daemons = true; MutexLock mu(self, *Locks::thread_list_lock_); for (It it = list_.begin(), end = list_.end(); it != end; ++it) { - // TODO: there's a race here with thread exit that's being worked around by checking if the - // thread has a peer. Thread* thread = *it; - if (thread != self && thread->HasPeer() && !thread->IsDaemon()) { + if (thread != self && !thread->IsDaemon()) { all_threads_are_daemons = false; break; } diff --git a/src/thread_pool.cc b/src/thread_pool.cc index 370e4bcc20..f0f6f1844d 100644 --- a/src/thread_pool.cc +++ b/src/thread_pool.cc @@ -154,17 +154,22 @@ Task* ThreadPool::TryGetTaskLocked(Thread* self) { return NULL; } -void ThreadPool::Wait(Thread* self, bool do_work) { - Task* task = NULL; - while ((task = TryGetTask(self)) != NULL) { - task->Run(self); - task->Finalize(); +void ThreadPool::Wait(Thread* self, bool do_work, bool may_hold_locks) { + if (do_work) { + Task* task = NULL; + while ((task = TryGetTask(self)) != NULL) { + task->Run(self); + task->Finalize(); + } } - // Wait until each thread is waiting and the task list is empty. MutexLock mu(self, task_queue_lock_); while (!shutting_down_ && (waiting_count_ != GetThreadCount() || !tasks_.empty())) { - completion_condition_.Wait(self); + if (!may_hold_locks) { + completion_condition_.Wait(self); + } else { + completion_condition_.WaitHoldingLocks(self); + } } } diff --git a/src/thread_pool.h b/src/thread_pool.h index 18af97dfa3..814e654ad7 100644 --- a/src/thread_pool.h +++ b/src/thread_pool.h @@ -80,7 +80,7 @@ class ThreadPool { virtual ~ThreadPool(); // Wait for all tasks currently on queue to get completed. - void Wait(Thread* self, bool do_work = true); + void Wait(Thread* self, bool do_work, bool may_hold_locks); size_t GetTaskCount(Thread* self); diff --git a/src/thread_pool_test.cc b/src/thread_pool_test.cc index e056935319..e2a32f510d 100644 --- a/src/thread_pool_test.cc +++ b/src/thread_pool_test.cc @@ -68,7 +68,7 @@ TEST_F(ThreadPoolTest, CheckRun) { } thread_pool.StartWorkers(self); // Wait for tasks to complete. - thread_pool.Wait(self); + thread_pool.Wait(self, true, false); // Make sure that we finished all the work. EXPECT_EQ(num_tasks, count); } @@ -137,7 +137,7 @@ TEST_F(ThreadPoolTest, RecursiveTest) { static const int depth = 8; thread_pool.AddTask(self, new TreeTask(&thread_pool, &count, depth)); thread_pool.StartWorkers(self); - thread_pool.Wait(self); + thread_pool.Wait(self, true, false); EXPECT_EQ((1 << depth) - 1, count); } diff --git a/src/thread_state.h b/src/thread_state.h index 7c4a16f914..52f092efa0 100644 --- a/src/thread_state.h +++ b/src/thread_state.h @@ -28,6 +28,7 @@ enum ThreadState { kBlocked, // BLOCKED TS_MONITOR blocked on a monitor kWaiting, // WAITING TS_WAIT in Object.wait() kWaitingForGcToComplete, // WAITING TS_WAIT blocked waiting for GC + kWaitingForCheckPointsToRun, // WAITING TS_WAIT GC waiting for checkpoints to run kWaitingPerformingGc, // WAITING TS_WAIT performing GC kWaitingForDebuggerSend, // WAITING TS_WAIT blocked waiting for events to be sent kWaitingForDebuggerToAttach, // WAITING TS_WAIT blocked waiting for debugger to attach diff --git a/src/utf.cc b/src/utf.cc index 8d3547e70c..1add7d9a68 100644 --- a/src/utf.cc +++ b/src/utf.cc @@ -119,6 +119,23 @@ int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, } } +int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2) { + for (;;) { + if (*utf8_1 == '\0') { + return (*utf8_2 == '\0') ? 0 : -1; + } else if (*utf8_2 == '\0') { + return 1; + } + + int c1 = GetUtf16FromUtf8(&utf8_1); + int c2 = *utf8_2; + + if (c1 != c2) { + return c1 > c2 ? 1 : -1; + } + } +} + size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) { size_t result = 0; while (char_count--) { @@ -56,6 +56,12 @@ void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, const char* utf8_in); int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, const char* utf8_2); /* + * Compare a modified UTF-8 string with a UTF-16 string as code point values in a non-locale + * sensitive manner. + */ +int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2); + +/* * Convert from UTF-16 to Modified UTF-8. Note that the output is _not_ * NUL-terminated. You probably need to call CountUtf8Bytes before calling * this anyway, so if you want a NUL-terminated string, you know where to diff --git a/src/vector_output_stream.cc b/src/vector_output_stream.cc index 96154ee92b..e5ff729036 100644 --- a/src/vector_output_stream.cc +++ b/src/vector_output_stream.cc @@ -16,8 +16,6 @@ #include "vector_output_stream.h" -#include <string.h> - #include "base/logging.h" namespace art { @@ -25,14 +23,6 @@ namespace art { VectorOutputStream::VectorOutputStream(const std::string& location, std::vector<uint8_t>& vector) : OutputStream(location), offset_(vector.size()), vector_(vector) {} -bool VectorOutputStream::WriteFully(const void* buffer, int64_t byte_count) { - off_t new_offset = offset_ + byte_count; - EnsureCapacity(new_offset); - memcpy(&vector_[offset_], buffer, byte_count); - offset_ = new_offset; - return true; -} - off_t VectorOutputStream::Seek(off_t offset, Whence whence) { CHECK(whence == kSeekSet || whence == kSeekCurrent || whence == kSeekEnd) << whence; off_t new_offset = 0; @@ -55,10 +45,4 @@ off_t VectorOutputStream::Seek(off_t offset, Whence whence) { return offset_; } -void VectorOutputStream::EnsureCapacity(off_t new_offset) { - if (new_offset > static_cast<off_t>(vector_.size())) { - vector_.resize(new_offset); - } -} - } // namespace art diff --git a/src/vector_output_stream.h b/src/vector_output_stream.h index a99128e6f3..3546c8d577 100644 --- a/src/vector_output_stream.h +++ b/src/vector_output_stream.h @@ -20,6 +20,7 @@ #include "output_stream.h" #include <string> +#include <string.h> #include <vector> namespace art { @@ -30,12 +31,28 @@ class VectorOutputStream : public OutputStream { virtual ~VectorOutputStream() {} - virtual bool WriteFully(const void* buffer, int64_t byte_count); + bool WriteFully(const void* buffer, int64_t byte_count) { + if (static_cast<size_t>(offset_) == vector_.size()) { + const uint8_t* start = reinterpret_cast<const uint8_t*>(buffer); + vector_.insert(vector_.end(), &start[0], &start[byte_count]); + offset_ += byte_count; + } else { + off_t new_offset = offset_ + byte_count; + EnsureCapacity(new_offset); + memcpy(&vector_[offset_], buffer, byte_count); + offset_ = new_offset; + } + return true; + } - virtual off_t Seek(off_t offset, Whence whence); + off_t Seek(off_t offset, Whence whence); private: - void EnsureCapacity(off_t new_offset); + void EnsureCapacity(off_t new_offset) { + if (new_offset > static_cast<off_t>(vector_.size())) { + vector_.resize(new_offset); + } + } off_t offset_; std::vector<uint8_t>& vector_; diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc index 2eb0c203ea..74a79e0ae2 100644 --- a/src/verifier/method_verifier.cc +++ b/src/verifier/method_verifier.cc @@ -19,24 +19,26 @@ #include <iostream> #include "base/logging.h" +#include "base/mutex-inl.h" #include "base/stringpiece.h" #include "class_linker.h" #include "compiler/driver/compiler_driver.h" #include "dex_file-inl.h" -#include "dex_instruction.h" +#include "dex_instruction-inl.h" #include "dex_instruction_visitor.h" -#include "gc/card_table-inl.h" +#include "gc/accounting/card_table-inl.h" #include "indenter.h" #include "intern_table.h" #include "leb128.h" #include "mirror/abstract_method-inl.h" #include "mirror/class.h" #include "mirror/class-inl.h" -#include "mirror/dex_cache.h" +#include "mirror/dex_cache-inl.h" #include "mirror/field-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "object_utils.h" +#include "register_line-inl.h" #include "runtime.h" #include "verifier/dex_gc_map.h" @@ -266,13 +268,14 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca : reg_types_(can_load_classes), work_insn_idx_(-1), dex_method_idx_(dex_method_idx), - foo_method_(method), + mirror_method_(method), method_access_flags_(method_access_flags), dex_file_(dex_file), dex_cache_(dex_cache), class_loader_(class_loader), class_def_idx_(class_def_idx), code_item_(code_item), + declaring_class_(NULL), interesting_dex_pc_(-1), monitor_enter_dex_pcs_(NULL), have_pending_hard_failure_(false), @@ -305,6 +308,63 @@ void MethodVerifier::FindLocksAtDexPc() { Verify(); } +mirror::Field* MethodVerifier::FindAccessedFieldAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) { + MethodHelper mh(m); + MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), + mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(), + m, m->GetAccessFlags(), false, true); + return verifier.FindAccessedFieldAtDexPc(dex_pc); +} + +mirror::Field* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) { + CHECK(code_item_ != NULL); // This only makes sense for methods with code. + + // Strictly speaking, we ought to be able to get away with doing a subset of the full method + // verification. In practice, the phase we want relies on data structures set up by all the + // earlier passes, so we just run the full method verification and bail out early when we've + // got what we wanted. + bool success = Verify(); + if (!success) { + return NULL; + } + RegisterLine* register_line = reg_table_.GetLine(dex_pc); + if (register_line == NULL) { + return NULL; + } + const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc); + return GetQuickFieldAccess(inst, register_line); +} + +mirror::AbstractMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) { + MethodHelper mh(m); + MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), + mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(), + m, m->GetAccessFlags(), false, true); + return verifier.FindInvokedMethodAtDexPc(dex_pc); +} + +mirror::AbstractMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { + CHECK(code_item_ != NULL); // This only makes sense for methods with code. + + // Strictly speaking, we ought to be able to get away with doing a subset of the full method + // verification. In practice, the phase we want relies on data structures set up by all the + // earlier passes, so we just run the full method verification and bail out early when we've + // got what we wanted. + bool success = Verify(); + if (!success) { + return NULL; + } + RegisterLine* register_line = reg_table_.GetLine(dex_pc); + if (register_line == NULL) { + return NULL; + } + const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc); + const bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); + return GetQuickInvokedMethod(inst, register_line, is_range); +} + bool MethodVerifier::Verify() { // If there aren't any instructions, make sure that's expected, then exit successfully. if (code_item_ == NULL) { @@ -895,6 +955,7 @@ bool MethodVerifier::CheckVarArgRangeRegs(uint32_t vA, uint32_t vC) { static const std::vector<uint8_t>* CreateLengthPrefixedDexGcMap(const std::vector<uint8_t>& gc_map) { std::vector<uint8_t>* length_prefixed_gc_map = new std::vector<uint8_t>; + length_prefixed_gc_map->reserve(gc_map.size() + 4); length_prefixed_gc_map->push_back((gc_map.size() & 0xff000000) >> 24); length_prefixed_gc_map->push_back((gc_map.size() & 0x00ff0000) >> 16); length_prefixed_gc_map->push_back((gc_map.size() & 0x0000ff00) >> 8); @@ -949,15 +1010,20 @@ bool MethodVerifier::VerifyCodeFlow() { DCHECK_NE(failures_.size(), 0U); return false; // Not a real failure, but a failure to encode } -#ifndef NDEBUG - VerifyGcMap(*map); -#endif + if (kIsDebugBuild) { + VerifyGcMap(*map); + } const std::vector<uint8_t>* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get())); verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map); - MethodVerifier::PcToConreteMethod* pc_to_conrete_method = GenerateDevirtMap(); - if(pc_to_conrete_method != NULL ) { - SetDevirtMap(ref, pc_to_conrete_method); + MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet(); + if(method_to_safe_casts != NULL ) { + SetSafeCastMap(ref, method_to_safe_casts); + } + + MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(); + if(pc_to_concrete_method != NULL ) { + SetDevirtMap(ref, pc_to_concrete_method); } return true; } @@ -1244,6 +1310,11 @@ bool MethodVerifier::CodeFlowVerifyMethod() { if (dead_start >= 0) { LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start) << "-" << reinterpret_cast<void*>(insn_idx - 1); } + // To dump the state of the verify after a method, do something like: + // if (PrettyMethod(dex_method_idx_, *dex_file_) == + // "boolean java.lang.String.equals(java.lang.Object)") { + // LOG(INFO) << info_messages_.str(); + // } } return true; } @@ -1280,7 +1351,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ const uint16_t* insns = code_item_->insns_ + work_insn_idx_; const Instruction* inst = Instruction::At(insns); - DecodedInstruction dec_insn(inst); int opcode_flags = Instruction::FlagsOf(inst->Opcode()); int32_t branch_target = 0; @@ -1306,32 +1376,51 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { #endif } - switch (dec_insn.opcode) { + + // We need to ensure the work line is consistent while performing validation. When we spot a + // peephole pattern we compute a new line for either the fallthrough instruction or the + // branch target. + UniquePtr<RegisterLine> branch_line; + UniquePtr<RegisterLine> fallthrough_line; + + switch (inst->Opcode()) { case Instruction::NOP: /* * A "pure" NOP has no effect on anything. Data tables start with * a signature that looks like a NOP; if we see one of these in * the course of executing code then we have a problem. */ - if (dec_insn.vA != 0) { + if (inst->VRegA_10x() != 0) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "encountered data table in instruction stream"; } break; case Instruction::MOVE: + work_line_->CopyRegister1(inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategory1nr); + break; case Instruction::MOVE_FROM16: + work_line_->CopyRegister1(inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategory1nr); + break; case Instruction::MOVE_16: - work_line_->CopyRegister1(dec_insn.vA, dec_insn.vB, kTypeCategory1nr); + work_line_->CopyRegister1(inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategory1nr); break; case Instruction::MOVE_WIDE: + work_line_->CopyRegister2(inst->VRegA_12x(), inst->VRegB_12x()); + break; case Instruction::MOVE_WIDE_FROM16: + work_line_->CopyRegister2(inst->VRegA_22x(), inst->VRegB_22x()); + break; case Instruction::MOVE_WIDE_16: - work_line_->CopyRegister2(dec_insn.vA, dec_insn.vB); + work_line_->CopyRegister2(inst->VRegA_32x(), inst->VRegB_32x()); break; case Instruction::MOVE_OBJECT: + work_line_->CopyRegister1(inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategoryRef); + break; case Instruction::MOVE_OBJECT_FROM16: + work_line_->CopyRegister1(inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategoryRef); + break; case Instruction::MOVE_OBJECT_16: - work_line_->CopyRegister1(dec_insn.vA, dec_insn.vB, kTypeCategoryRef); + work_line_->CopyRegister1(inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategoryRef); break; /* @@ -1346,13 +1435,13 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * easier to read in some cases.) */ case Instruction::MOVE_RESULT: - work_line_->CopyResultRegister1(dec_insn.vA, false); + work_line_->CopyResultRegister1(inst->VRegA_11x(), false); break; case Instruction::MOVE_RESULT_WIDE: - work_line_->CopyResultRegister2(dec_insn.vA); + work_line_->CopyResultRegister2(inst->VRegA_11x()); break; case Instruction::MOVE_RESULT_OBJECT: - work_line_->CopyResultRegister1(dec_insn.vA, true); + work_line_->CopyResultRegister1(inst->VRegA_11x(), true); break; case Instruction::MOVE_EXCEPTION: { @@ -1361,7 +1450,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * that as part of extracting the exception type from the catch block list. */ const RegType& res_type = GetCaughtExceptionType(); - work_line_->SetRegisterType(dec_insn.vA, res_type); + work_line_->SetRegisterType(inst->VRegA_11x(), res_type); break; } case Instruction::RETURN_VOID: @@ -1380,16 +1469,17 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { // Compilers may generate synthetic functions that write byte values into boolean fields. // Also, it may use integer values for boolean, byte, short, and character return types. - const RegType& src_type = work_line_->GetRegisterType(dec_insn.vA); + const uint32_t vregA = inst->VRegA_11x(); + const RegType& src_type = work_line_->GetRegisterType(vregA); bool use_src = ((return_type.IsBoolean() && src_type.IsByte()) || ((return_type.IsBoolean() || return_type.IsByte() || return_type.IsShort() || return_type.IsChar()) && src_type.IsInteger())); /* check the register contents */ bool success = - work_line_->VerifyRegisterType(dec_insn.vA, use_src ? src_type : return_type); + work_line_->VerifyRegisterType(vregA, use_src ? src_type : return_type); if (!success) { - AppendToLastFailMessage(StringPrintf(" return-1nr on invalid register v%d", dec_insn.vA)); + AppendToLastFailMessage(StringPrintf(" return-1nr on invalid register v%d", vregA)); } } } @@ -1402,9 +1492,10 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-wide not expected"; } else { /* check the register contents */ - bool success = work_line_->VerifyRegisterType(dec_insn.vA, return_type); + const uint32_t vregA = inst->VRegA_11x(); + bool success = work_line_->VerifyRegisterType(vregA, return_type); if (!success) { - AppendToLastFailMessage(StringPrintf(" return-wide on invalid register v%d", dec_insn.vA)); + AppendToLastFailMessage(StringPrintf(" return-wide on invalid register v%d", vregA)); } } } @@ -1418,7 +1509,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { /* return_type is the *expected* return type, not register value */ DCHECK(!return_type.IsZero()); DCHECK(!return_type.IsUninitializedReference()); - const RegType& reg_type = work_line_->GetRegisterType(dec_insn.vA); + const uint32_t vregA = inst->VRegA_11x(); + const RegType& reg_type = work_line_->GetRegisterType(vregA); // Disallow returning uninitialized values and verify that the reference in vAA is an // instance of the "return_type" if (reg_type.IsUninitializedTypes()) { @@ -1432,66 +1524,71 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; /* could be boolean, int, float, or a null reference */ - case Instruction::CONST_4: - work_line_->SetRegisterType(dec_insn.vA, - reg_types_.FromCat1Const(static_cast<int32_t>(dec_insn.vB << 28) >> 28, true)); + case Instruction::CONST_4: { + int32_t val = static_cast<int32_t>(inst->VRegB_11n() << 28) >> 28; + work_line_->SetRegisterType(inst->VRegA_11n(), reg_types_.FromCat1Const(val, true)); break; - case Instruction::CONST_16: - work_line_->SetRegisterType(dec_insn.vA, - reg_types_.FromCat1Const(static_cast<int16_t>(dec_insn.vB), true)); + } + case Instruction::CONST_16: { + int16_t val = static_cast<int16_t>(inst->VRegB_21s()); + work_line_->SetRegisterType(inst->VRegA_21s(), reg_types_.FromCat1Const(val, true)); break; + } case Instruction::CONST: - work_line_->SetRegisterType(dec_insn.vA, reg_types_.FromCat1Const(dec_insn.vB, true)); + work_line_->SetRegisterType(inst->VRegA_31i(), + reg_types_.FromCat1Const(inst->VRegB_31i(), true)); break; case Instruction::CONST_HIGH16: - work_line_->SetRegisterType(dec_insn.vA, - reg_types_.FromCat1Const(dec_insn.vB << 16, true)); + work_line_->SetRegisterType(inst->VRegA_21h(), + reg_types_.FromCat1Const(inst->VRegB_21h() << 16, true)); break; /* could be long or double; resolved upon use */ case Instruction::CONST_WIDE_16: { - int64_t val = static_cast<int16_t>(dec_insn.vB); + int64_t val = static_cast<int16_t>(inst->VRegB_21s()); const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true); const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true); - work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi); + work_line_->SetRegisterTypeWide(inst->VRegA_21s(), lo, hi); break; } case Instruction::CONST_WIDE_32: { - int64_t val = static_cast<int32_t>(dec_insn.vB); + int64_t val = static_cast<int32_t>(inst->VRegB_31i()); const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true); const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true); - work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi); + work_line_->SetRegisterTypeWide(inst->VRegA_31i(), lo, hi); break; } case Instruction::CONST_WIDE: { - int64_t val = dec_insn.vB_wide; + int64_t val = inst->VRegB_51l(); const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true); const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true); - work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi); + work_line_->SetRegisterTypeWide(inst->VRegA_51l(), lo, hi); break; } case Instruction::CONST_WIDE_HIGH16: { - int64_t val = static_cast<uint64_t>(dec_insn.vB) << 48; + int64_t val = static_cast<uint64_t>(inst->VRegB_21h()) << 48; const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true); const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true); - work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi); + work_line_->SetRegisterTypeWide(inst->VRegA_21h(), lo, hi); break; } case Instruction::CONST_STRING: + work_line_->SetRegisterType(inst->VRegA_21c(), reg_types_.JavaLangString()); + break; case Instruction::CONST_STRING_JUMBO: - work_line_->SetRegisterType(dec_insn.vA, reg_types_.JavaLangString()); + work_line_->SetRegisterType(inst->VRegA_31c(), reg_types_.JavaLangString()); break; case Instruction::CONST_CLASS: { // Get type from instruction if unresolved then we need an access check // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved - const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB); + const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c()); // Register holds class, ie its type is class, on error it will hold Conflict. - work_line_->SetRegisterType(dec_insn.vA, + work_line_->SetRegisterType(inst->VRegA_21c(), res_type.IsConflict() ? res_type : reg_types_.JavaLangClass(true)); break; } case Instruction::MONITOR_ENTER: - work_line_->PushMonitor(dec_insn.vA, work_insn_idx_); + work_line_->PushMonitor(inst->VRegA_11x(), work_insn_idx_); break; case Instruction::MONITOR_EXIT: /* @@ -1515,7 +1612,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * "live" so we still need to check it. */ opcode_flags &= ~Instruction::kThrow; - work_line_->PopMonitor(dec_insn.vA); + work_line_->PopMonitor(inst->VRegA_11x()); break; case Instruction::CHECK_CAST: @@ -1527,45 +1624,53 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * If it fails, an exception is thrown, which we deal with later by ignoring the update to * dec_insn.vA when branching to a handler. */ - bool is_checkcast = dec_insn.opcode == Instruction::CHECK_CAST; - const RegType& res_type = - ResolveClassAndCheckAccess(is_checkcast ? dec_insn.vB : dec_insn.vC); + const bool is_checkcast = (inst->Opcode() == Instruction::CHECK_CAST); + const uint32_t type_idx = (is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c(); + const RegType& res_type = ResolveClassAndCheckAccess(type_idx); if (res_type.IsConflict()) { DCHECK_NE(failures_.size(), 0U); if (!is_checkcast) { - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Boolean()); + work_line_->SetRegisterType(inst->VRegA_22c(), reg_types_.Boolean()); } break; // bad class } // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved - const RegType& orig_type = - work_line_->GetRegisterType(is_checkcast ? dec_insn.vA : dec_insn.vB); + uint32_t orig_type_reg = (is_checkcast) ? inst->VRegA_21c() : inst->VRegB_22c(); + const RegType& orig_type = work_line_->GetRegisterType(orig_type_reg); if (!res_type.IsNonZeroReferenceTypes()) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on unexpected class " << res_type; + if (is_checkcast) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on unexpected class " << res_type; + } else { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "instance-of on unexpected class " << res_type; + } } else if (!orig_type.IsReferenceTypes()) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on non-reference in v" << dec_insn.vA; + if (is_checkcast) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on non-reference in v" << orig_type_reg; + } else { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "instance-of on non-reference in v" << orig_type_reg; + } } else { if (is_checkcast) { - work_line_->SetRegisterType(dec_insn.vA, res_type); + work_line_->SetRegisterType(inst->VRegA_21c(), res_type); } else { - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Boolean()); + work_line_->SetRegisterType(inst->VRegA_22c(), reg_types_.Boolean()); } } break; } case Instruction::ARRAY_LENGTH: { - const RegType& res_type = work_line_->GetRegisterType(dec_insn.vB); + const RegType& res_type = work_line_->GetRegisterType(inst->VRegB_12x()); if (res_type.IsReferenceTypes()) { if (!res_type.IsArrayTypes() && !res_type.IsZero()) { // ie not an array or null Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type; } else { - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Integer()); + work_line_->SetRegisterType(inst->VRegA_12x(), reg_types_.Integer()); } } break; } case Instruction::NEW_INSTANCE: { - const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB); + const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c()); if (res_type.IsConflict()) { DCHECK_NE(failures_.size(), 0U); break; // bad class @@ -1582,55 +1687,55 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // initialized must be marked invalid. work_line_->MarkUninitRefsAsInvalid(uninit_type); // add the new uninitialized reference to the register state - work_line_->SetRegisterType(dec_insn.vA, uninit_type); + work_line_->SetRegisterType(inst->VRegA_21c(), uninit_type); break; } case Instruction::NEW_ARRAY: - VerifyNewArray(dec_insn, false, false); + VerifyNewArray(inst, false, false); break; case Instruction::FILLED_NEW_ARRAY: - VerifyNewArray(dec_insn, true, false); + VerifyNewArray(inst, true, false); just_set_result = true; // Filled new array sets result register break; case Instruction::FILLED_NEW_ARRAY_RANGE: - VerifyNewArray(dec_insn, true, true); + VerifyNewArray(inst, true, true); just_set_result = true; // Filled new array range sets result register break; case Instruction::CMPL_FLOAT: case Instruction::CMPG_FLOAT: - if (!work_line_->VerifyRegisterType(dec_insn.vB, reg_types_.Float())) { + if (!work_line_->VerifyRegisterType(inst->VRegB_23x(), reg_types_.Float())) { break; } - if (!work_line_->VerifyRegisterType(dec_insn.vC, reg_types_.Float())) { + if (!work_line_->VerifyRegisterType(inst->VRegC_23x(), reg_types_.Float())) { break; } - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Integer()); + work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer()); break; case Instruction::CMPL_DOUBLE: case Instruction::CMPG_DOUBLE: - if (!work_line_->VerifyRegisterTypeWide(dec_insn.vB, reg_types_.DoubleLo(), + if (!work_line_->VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.DoubleLo(), reg_types_.DoubleHi())) { break; } - if (!work_line_->VerifyRegisterTypeWide(dec_insn.vC, reg_types_.DoubleLo(), + if (!work_line_->VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.DoubleLo(), reg_types_.DoubleHi())) { break; } - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Integer()); + work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer()); break; case Instruction::CMP_LONG: - if (!work_line_->VerifyRegisterTypeWide(dec_insn.vB, reg_types_.LongLo(), + if (!work_line_->VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.LongLo(), reg_types_.LongHi())) { break; } - if (!work_line_->VerifyRegisterTypeWide(dec_insn.vC, reg_types_.LongLo(), + if (!work_line_->VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.LongLo(), reg_types_.LongHi())) { break; } - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Integer()); + work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer()); break; case Instruction::THROW: { - const RegType& res_type = work_line_->GetRegisterType(dec_insn.vA); + const RegType& res_type = work_line_->GetRegisterType(inst->VRegA_11x()); if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "thrown class " << res_type << " not instanceof Throwable"; } @@ -1645,12 +1750,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::PACKED_SWITCH: case Instruction::SPARSE_SWITCH: /* verify that vAA is an integer, or can be converted to one */ - work_line_->VerifyRegisterType(dec_insn.vA, reg_types_.Integer()); + work_line_->VerifyRegisterType(inst->VRegA_31t(), reg_types_.Integer()); break; case Instruction::FILL_ARRAY_DATA: { /* Similar to the verification done for APUT */ - const RegType& array_type = work_line_->GetRegisterType(dec_insn.vA); + const RegType& array_type = work_line_->GetRegisterType(inst->VRegA_31t()); /* array_type can be null if the reg type is Zero */ if (!array_type.IsZero()) { if (!array_type.IsArrayTypes()) { @@ -1683,8 +1788,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } case Instruction::IF_EQ: case Instruction::IF_NE: { - const RegType& reg_type1 = work_line_->GetRegisterType(dec_insn.vA); - const RegType& reg_type2 = work_line_->GetRegisterType(dec_insn.vB); + const RegType& reg_type1 = work_line_->GetRegisterType(inst->VRegA_22t()); + const RegType& reg_type2 = work_line_->GetRegisterType(inst->VRegB_22t()); bool mismatch = false; if (reg_type1.IsZero()) { // zero then integral or reference expected mismatch = !reg_type2.IsReferenceTypes() && !reg_type2.IsIntegralTypes(); @@ -1703,8 +1808,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::IF_GE: case Instruction::IF_GT: case Instruction::IF_LE: { - const RegType& reg_type1 = work_line_->GetRegisterType(dec_insn.vA); - const RegType& reg_type2 = work_line_->GetRegisterType(dec_insn.vB); + const RegType& reg_type1 = work_line_->GetRegisterType(inst->VRegA_22t()); + const RegType& reg_type2 = work_line_->GetRegisterType(inst->VRegB_22t()); if (!reg_type1.IsIntegralTypes() || !reg_type2.IsIntegralTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "args to 'if' (" << reg_type1 << "," << reg_type2 << ") must be integral"; @@ -1713,17 +1818,94 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } case Instruction::IF_EQZ: case Instruction::IF_NEZ: { - const RegType& reg_type = work_line_->GetRegisterType(dec_insn.vA); + const RegType& reg_type = work_line_->GetRegisterType(inst->VRegA_21t()); if (!reg_type.IsReferenceTypes() && !reg_type.IsIntegralTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type << " unexpected as arg to if-eqz/if-nez"; } + + // Find previous instruction - its existence is a precondition to peephole optimization. + uint32_t instance_of_idx = 0; + if (0 != work_insn_idx_) { + instance_of_idx = work_insn_idx_ - 1; + while(0 != instance_of_idx && !insn_flags_[instance_of_idx].IsOpcode()) { + instance_of_idx--; + } + CHECK(insn_flags_[instance_of_idx].IsOpcode()); + } else { + break; + } + + const Instruction* instance_of_inst = Instruction::At(code_item_->insns_ + instance_of_idx); + + /* Check for peep-hole pattern of: + * ...; + * instance-of vX, vY, T; + * ifXXX vX, label ; + * ...; + * label: + * ...; + * and sharpen the type of vY to be type T. + * Note, this pattern can't be if: + * - if there are other branches to this branch, + * - when vX == vY. + */ + if (!CurrentInsnFlags()->IsBranchTarget() && + (Instruction::INSTANCE_OF == instance_of_inst->Opcode()) && + (inst->VRegA_21t() == instance_of_inst->VRegA_22c()) && + (instance_of_inst->VRegA_22c() != instance_of_inst->VRegB_22c())) { + // Check that the we are not attempting conversion to interface types, + // which is not done because of the multiple inheritance implications. + const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c()); + + if(!cast_type.IsUnresolvedTypes() && !cast_type.GetClass()->IsInterface()) { + RegisterLine* update_line = new RegisterLine(code_item_->registers_size_, this); + if (inst->Opcode() == Instruction::IF_EQZ) { + fallthrough_line.reset(update_line); + } else { + branch_line.reset(update_line); + } + update_line->CopyFromLine(work_line_.get()); + update_line->SetRegisterType(instance_of_inst->VRegB_22c(), cast_type); + if (!insn_flags_[instance_of_idx].IsBranchTarget() && 0 != instance_of_idx) { + // See if instance-of was preceded by a move-object operation, common due to the small + // register encoding space of instance-of, and propagate type information to the source + // of the move-object. + uint32_t move_idx = instance_of_idx - 1; + while(0 != move_idx && !insn_flags_[move_idx].IsOpcode()) { + move_idx--; + } + CHECK(insn_flags_[move_idx].IsOpcode()); + const Instruction* move_inst = Instruction::At(code_item_->insns_ + move_idx); + switch (move_inst->Opcode()) { + case Instruction::MOVE_OBJECT: + if (move_inst->VRegA_12x() == instance_of_inst->VRegB_22c()) { + update_line->SetRegisterType(move_inst->VRegB_12x(), cast_type); + } + break; + case Instruction::MOVE_OBJECT_FROM16: + if (move_inst->VRegA_22x() == instance_of_inst->VRegB_22c()) { + update_line->SetRegisterType(move_inst->VRegB_22x(), cast_type); + } + break; + case Instruction::MOVE_OBJECT_16: + if (move_inst->VRegA_32x() == instance_of_inst->VRegB_22c()) { + update_line->SetRegisterType(move_inst->VRegB_32x(), cast_type); + } + break; + default: + break; + } + } + } + } + break; } case Instruction::IF_LTZ: case Instruction::IF_GEZ: case Instruction::IF_GTZ: case Instruction::IF_LEZ: { - const RegType& reg_type = work_line_->GetRegisterType(dec_insn.vA); + const RegType& reg_type = work_line_->GetRegisterType(inst->VRegA_21t()); if (!reg_type.IsIntegralTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type << " unexpected as arg to if-ltz/if-gez/if-gtz/if-lez"; @@ -1731,150 +1913,150 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } case Instruction::AGET_BOOLEAN: - VerifyAGet(dec_insn, reg_types_.Boolean(), true); + VerifyAGet(inst, reg_types_.Boolean(), true); break; case Instruction::AGET_BYTE: - VerifyAGet(dec_insn, reg_types_.Byte(), true); + VerifyAGet(inst, reg_types_.Byte(), true); break; case Instruction::AGET_CHAR: - VerifyAGet(dec_insn, reg_types_.Char(), true); + VerifyAGet(inst, reg_types_.Char(), true); break; case Instruction::AGET_SHORT: - VerifyAGet(dec_insn, reg_types_.Short(), true); + VerifyAGet(inst, reg_types_.Short(), true); break; case Instruction::AGET: - VerifyAGet(dec_insn, reg_types_.Integer(), true); + VerifyAGet(inst, reg_types_.Integer(), true); break; case Instruction::AGET_WIDE: - VerifyAGet(dec_insn, reg_types_.LongLo(), true); + VerifyAGet(inst, reg_types_.LongLo(), true); break; case Instruction::AGET_OBJECT: - VerifyAGet(dec_insn, reg_types_.JavaLangObject(false), false); + VerifyAGet(inst, reg_types_.JavaLangObject(false), false); break; case Instruction::APUT_BOOLEAN: - VerifyAPut(dec_insn, reg_types_.Boolean(), true); + VerifyAPut(inst, reg_types_.Boolean(), true); break; case Instruction::APUT_BYTE: - VerifyAPut(dec_insn, reg_types_.Byte(), true); + VerifyAPut(inst, reg_types_.Byte(), true); break; case Instruction::APUT_CHAR: - VerifyAPut(dec_insn, reg_types_.Char(), true); + VerifyAPut(inst, reg_types_.Char(), true); break; case Instruction::APUT_SHORT: - VerifyAPut(dec_insn, reg_types_.Short(), true); + VerifyAPut(inst, reg_types_.Short(), true); break; case Instruction::APUT: - VerifyAPut(dec_insn, reg_types_.Integer(), true); + VerifyAPut(inst, reg_types_.Integer(), true); break; case Instruction::APUT_WIDE: - VerifyAPut(dec_insn, reg_types_.LongLo(), true); + VerifyAPut(inst, reg_types_.LongLo(), true); break; case Instruction::APUT_OBJECT: - VerifyAPut(dec_insn, reg_types_.JavaLangObject(false), false); + VerifyAPut(inst, reg_types_.JavaLangObject(false), false); break; case Instruction::IGET_BOOLEAN: - VerifyISGet(dec_insn, reg_types_.Boolean(), true, false); + VerifyISGet(inst, reg_types_.Boolean(), true, false); break; case Instruction::IGET_BYTE: - VerifyISGet(dec_insn, reg_types_.Byte(), true, false); + VerifyISGet(inst, reg_types_.Byte(), true, false); break; case Instruction::IGET_CHAR: - VerifyISGet(dec_insn, reg_types_.Char(), true, false); + VerifyISGet(inst, reg_types_.Char(), true, false); break; case Instruction::IGET_SHORT: - VerifyISGet(dec_insn, reg_types_.Short(), true, false); + VerifyISGet(inst, reg_types_.Short(), true, false); break; case Instruction::IGET: - VerifyISGet(dec_insn, reg_types_.Integer(), true, false); + VerifyISGet(inst, reg_types_.Integer(), true, false); break; case Instruction::IGET_WIDE: - VerifyISGet(dec_insn, reg_types_.LongLo(), true, false); + VerifyISGet(inst, reg_types_.LongLo(), true, false); break; case Instruction::IGET_OBJECT: - VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, false); + VerifyISGet(inst, reg_types_.JavaLangObject(false), false, false); break; case Instruction::IPUT_BOOLEAN: - VerifyISPut(dec_insn, reg_types_.Boolean(), true, false); + VerifyISPut(inst, reg_types_.Boolean(), true, false); break; case Instruction::IPUT_BYTE: - VerifyISPut(dec_insn, reg_types_.Byte(), true, false); + VerifyISPut(inst, reg_types_.Byte(), true, false); break; case Instruction::IPUT_CHAR: - VerifyISPut(dec_insn, reg_types_.Char(), true, false); + VerifyISPut(inst, reg_types_.Char(), true, false); break; case Instruction::IPUT_SHORT: - VerifyISPut(dec_insn, reg_types_.Short(), true, false); + VerifyISPut(inst, reg_types_.Short(), true, false); break; case Instruction::IPUT: - VerifyISPut(dec_insn, reg_types_.Integer(), true, false); + VerifyISPut(inst, reg_types_.Integer(), true, false); break; case Instruction::IPUT_WIDE: - VerifyISPut(dec_insn, reg_types_.LongLo(), true, false); + VerifyISPut(inst, reg_types_.LongLo(), true, false); break; case Instruction::IPUT_OBJECT: - VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, false); + VerifyISPut(inst, reg_types_.JavaLangObject(false), false, false); break; case Instruction::SGET_BOOLEAN: - VerifyISGet(dec_insn, reg_types_.Boolean(), true, true); + VerifyISGet(inst, reg_types_.Boolean(), true, true); break; case Instruction::SGET_BYTE: - VerifyISGet(dec_insn, reg_types_.Byte(), true, true); + VerifyISGet(inst, reg_types_.Byte(), true, true); break; case Instruction::SGET_CHAR: - VerifyISGet(dec_insn, reg_types_.Char(), true, true); + VerifyISGet(inst, reg_types_.Char(), true, true); break; case Instruction::SGET_SHORT: - VerifyISGet(dec_insn, reg_types_.Short(), true, true); + VerifyISGet(inst, reg_types_.Short(), true, true); break; case Instruction::SGET: - VerifyISGet(dec_insn, reg_types_.Integer(), true, true); + VerifyISGet(inst, reg_types_.Integer(), true, true); break; case Instruction::SGET_WIDE: - VerifyISGet(dec_insn, reg_types_.LongLo(), true, true); + VerifyISGet(inst, reg_types_.LongLo(), true, true); break; case Instruction::SGET_OBJECT: - VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, true); + VerifyISGet(inst, reg_types_.JavaLangObject(false), false, true); break; case Instruction::SPUT_BOOLEAN: - VerifyISPut(dec_insn, reg_types_.Boolean(), true, true); + VerifyISPut(inst, reg_types_.Boolean(), true, true); break; case Instruction::SPUT_BYTE: - VerifyISPut(dec_insn, reg_types_.Byte(), true, true); + VerifyISPut(inst, reg_types_.Byte(), true, true); break; case Instruction::SPUT_CHAR: - VerifyISPut(dec_insn, reg_types_.Char(), true, true); + VerifyISPut(inst, reg_types_.Char(), true, true); break; case Instruction::SPUT_SHORT: - VerifyISPut(dec_insn, reg_types_.Short(), true, true); + VerifyISPut(inst, reg_types_.Short(), true, true); break; case Instruction::SPUT: - VerifyISPut(dec_insn, reg_types_.Integer(), true, true); + VerifyISPut(inst, reg_types_.Integer(), true, true); break; case Instruction::SPUT_WIDE: - VerifyISPut(dec_insn, reg_types_.LongLo(), true, true); + VerifyISPut(inst, reg_types_.LongLo(), true, true); break; case Instruction::SPUT_OBJECT: - VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, true); + VerifyISPut(inst, reg_types_.JavaLangObject(false), false, true); break; case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_VIRTUAL_RANGE: case Instruction::INVOKE_SUPER: case Instruction::INVOKE_SUPER_RANGE: { - bool is_range = (dec_insn.opcode == Instruction::INVOKE_VIRTUAL_RANGE || - dec_insn.opcode == Instruction::INVOKE_SUPER_RANGE); - bool is_super = (dec_insn.opcode == Instruction::INVOKE_SUPER || - dec_insn.opcode == Instruction::INVOKE_SUPER_RANGE); - mirror::AbstractMethod* called_method = VerifyInvocationArgs(dec_insn, METHOD_VIRTUAL, + bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE || + inst->Opcode() == Instruction::INVOKE_SUPER_RANGE); + bool is_super = (inst->Opcode() == Instruction::INVOKE_SUPER || + inst->Opcode() == Instruction::INVOKE_SUPER_RANGE); + mirror::AbstractMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL, is_range, is_super); const char* descriptor; if (called_method == NULL) { - uint32_t method_idx = dec_insn.vB; + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; descriptor = dex_file_->StringByTypeIdx(return_type_idx); @@ -1892,13 +2074,13 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: { - bool is_range = (dec_insn.opcode == Instruction::INVOKE_DIRECT_RANGE); - mirror::AbstractMethod* called_method = VerifyInvocationArgs(dec_insn, METHOD_DIRECT, + bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); + mirror::AbstractMethod* called_method = VerifyInvocationArgs(inst, METHOD_DIRECT, is_range, false); const char* return_type_descriptor; bool is_constructor; if (called_method == NULL) { - uint32_t method_idx = dec_insn.vB; + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); is_constructor = StringPiece(dex_file_->GetMethodName(method_id)) == "<init>"; uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; @@ -1915,7 +2097,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * allowing the latter only if the "this" argument is the same as the "this" argument to * this method (which implies that we're in a constructor ourselves). */ - const RegType& this_type = work_line_->GetInvocationThis(dec_insn); + const RegType& this_type = work_line_->GetInvocationThis(inst, is_range); if (this_type.IsConflict()) // failure. break; @@ -1959,11 +2141,11 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } case Instruction::INVOKE_STATIC: case Instruction::INVOKE_STATIC_RANGE: { - bool is_range = (dec_insn.opcode == Instruction::INVOKE_STATIC_RANGE); - mirror::AbstractMethod* called_method = VerifyInvocationArgs(dec_insn, METHOD_STATIC, is_range, false); + bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); + mirror::AbstractMethod* called_method = VerifyInvocationArgs(inst, METHOD_STATIC, is_range, false); const char* descriptor; if (called_method == NULL) { - uint32_t method_idx = dec_insn.vB; + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; descriptor = dex_file_->StringByTypeIdx(return_type_idx); @@ -1981,8 +2163,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: { - bool is_range = (dec_insn.opcode == Instruction::INVOKE_INTERFACE_RANGE); - mirror::AbstractMethod* abs_method = VerifyInvocationArgs(dec_insn, METHOD_INTERFACE, is_range, false); + bool is_range = (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE); + mirror::AbstractMethod* abs_method = VerifyInvocationArgs(inst, METHOD_INTERFACE, is_range, false); if (abs_method != NULL) { mirror::Class* called_interface = abs_method->GetDeclaringClass(); if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) { @@ -1994,7 +2176,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { /* Get the type of the "this" arg, which should either be a sub-interface of called * interface or Object (see comments in RegType::JoinClass). */ - const RegType& this_type = work_line_->GetInvocationThis(dec_insn); + const RegType& this_type = work_line_->GetInvocationThis(inst, is_range); if (this_type.IsZero()) { /* null pointer always passes (and always fails at runtime) */ } else { @@ -2017,7 +2199,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ const char* descriptor; if (abs_method == NULL) { - uint32_t method_idx = dec_insn.vB; + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; descriptor = dex_file_->StringByTypeIdx(return_type_idx); @@ -2035,74 +2217,74 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } case Instruction::NEG_INT: case Instruction::NOT_INT: - work_line_->CheckUnaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer()); + work_line_->CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Integer()); break; case Instruction::NEG_LONG: case Instruction::NOT_LONG: - work_line_->CheckUnaryOpWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(), + work_line_->CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.LongLo(), reg_types_.LongHi()); break; case Instruction::NEG_FLOAT: - work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Float()); + work_line_->CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Float()); break; case Instruction::NEG_DOUBLE: - work_line_->CheckUnaryOpWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(), + work_line_->CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(), reg_types_.DoubleLo(), reg_types_.DoubleHi()); break; case Instruction::INT_TO_LONG: - work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(), + work_line_->CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.Integer()); break; case Instruction::INT_TO_FLOAT: - work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Integer()); + work_line_->CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Integer()); break; case Instruction::INT_TO_DOUBLE: - work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(), + work_line_->CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(), reg_types_.Integer()); break; case Instruction::LONG_TO_INT: - work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Integer(), + work_line_->CheckUnaryOpFromWide(inst, reg_types_.Integer(), reg_types_.LongLo(), reg_types_.LongHi()); break; case Instruction::LONG_TO_FLOAT: - work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Float(), + work_line_->CheckUnaryOpFromWide(inst, reg_types_.Float(), reg_types_.LongLo(), reg_types_.LongHi()); break; case Instruction::LONG_TO_DOUBLE: - work_line_->CheckUnaryOpWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(), + work_line_->CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(), reg_types_.LongLo(), reg_types_.LongHi()); break; case Instruction::FLOAT_TO_INT: - work_line_->CheckUnaryOp(dec_insn, reg_types_.Integer(), reg_types_.Float()); + work_line_->CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Float()); break; case Instruction::FLOAT_TO_LONG: - work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(), + work_line_->CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.Float()); break; case Instruction::FLOAT_TO_DOUBLE: - work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(), + work_line_->CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(), reg_types_.Float()); break; case Instruction::DOUBLE_TO_INT: - work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Integer(), + work_line_->CheckUnaryOpFromWide(inst, reg_types_.Integer(), reg_types_.DoubleLo(), reg_types_.DoubleHi()); break; case Instruction::DOUBLE_TO_LONG: - work_line_->CheckUnaryOpWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(), + work_line_->CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.DoubleLo(), reg_types_.DoubleHi()); break; case Instruction::DOUBLE_TO_FLOAT: - work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Float(), + work_line_->CheckUnaryOpFromWide(inst, reg_types_.Float(), reg_types_.DoubleLo(), reg_types_.DoubleHi()); break; case Instruction::INT_TO_BYTE: - work_line_->CheckUnaryOp(dec_insn, reg_types_.Byte(), reg_types_.Integer()); + work_line_->CheckUnaryOp(inst, reg_types_.Byte(), reg_types_.Integer()); break; case Instruction::INT_TO_CHAR: - work_line_->CheckUnaryOp(dec_insn, reg_types_.Char(), reg_types_.Integer()); + work_line_->CheckUnaryOp(inst, reg_types_.Char(), reg_types_.Integer()); break; case Instruction::INT_TO_SHORT: - work_line_->CheckUnaryOp(dec_insn, reg_types_.Short(), reg_types_.Integer()); + work_line_->CheckUnaryOp(inst, reg_types_.Short(), reg_types_.Integer()); break; case Instruction::ADD_INT: @@ -2113,13 +2295,13 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::SHL_INT: case Instruction::SHR_INT: case Instruction::USHR_INT: - work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), + work_line_->CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false); break; case Instruction::AND_INT: case Instruction::OR_INT: case Instruction::XOR_INT: - work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), + work_line_->CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), true); break; case Instruction::ADD_LONG: @@ -2130,7 +2312,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::AND_LONG: case Instruction::OR_LONG: case Instruction::XOR_LONG: - work_line_->CheckBinaryOpWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(), + work_line_->CheckBinaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.LongLo(), reg_types_.LongHi()); break; @@ -2138,7 +2320,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::SHR_LONG: case Instruction::USHR_LONG: /* shift distance is Int, making these different from other binary operations */ - work_line_->CheckBinaryOpWideShift(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(), + work_line_->CheckBinaryOpWideShift(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.Integer()); break; case Instruction::ADD_FLOAT: @@ -2146,14 +2328,14 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::MUL_FLOAT: case Instruction::DIV_FLOAT: case Instruction::REM_FLOAT: - work_line_->CheckBinaryOp(dec_insn, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false); + work_line_->CheckBinaryOp(inst, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false); break; case Instruction::ADD_DOUBLE: case Instruction::SUB_DOUBLE: case Instruction::MUL_DOUBLE: case Instruction::DIV_DOUBLE: case Instruction::REM_DOUBLE: - work_line_->CheckBinaryOpWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(), + work_line_->CheckBinaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(), reg_types_.DoubleLo(), reg_types_.DoubleHi(), reg_types_.DoubleLo(), reg_types_.DoubleHi()); break; @@ -2164,15 +2346,15 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::SHL_INT_2ADDR: case Instruction::SHR_INT_2ADDR: case Instruction::USHR_INT_2ADDR: - work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false); + work_line_->CheckBinaryOp2addr(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false); break; case Instruction::AND_INT_2ADDR: case Instruction::OR_INT_2ADDR: case Instruction::XOR_INT_2ADDR: - work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), true); + work_line_->CheckBinaryOp2addr(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), true); break; case Instruction::DIV_INT_2ADDR: - work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false); + work_line_->CheckBinaryOp2addr(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false); break; case Instruction::ADD_LONG_2ADDR: case Instruction::SUB_LONG_2ADDR: @@ -2182,14 +2364,14 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::AND_LONG_2ADDR: case Instruction::OR_LONG_2ADDR: case Instruction::XOR_LONG_2ADDR: - work_line_->CheckBinaryOp2addrWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(), + work_line_->CheckBinaryOp2addrWide(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.LongLo(), reg_types_.LongHi()); break; case Instruction::SHL_LONG_2ADDR: case Instruction::SHR_LONG_2ADDR: case Instruction::USHR_LONG_2ADDR: - work_line_->CheckBinaryOp2addrWideShift(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(), + work_line_->CheckBinaryOp2addrWideShift(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.Integer()); break; case Instruction::ADD_FLOAT_2ADDR: @@ -2197,14 +2379,14 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::MUL_FLOAT_2ADDR: case Instruction::DIV_FLOAT_2ADDR: case Instruction::REM_FLOAT_2ADDR: - work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false); + work_line_->CheckBinaryOp2addr(inst, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false); break; case Instruction::ADD_DOUBLE_2ADDR: case Instruction::SUB_DOUBLE_2ADDR: case Instruction::MUL_DOUBLE_2ADDR: case Instruction::DIV_DOUBLE_2ADDR: case Instruction::REM_DOUBLE_2ADDR: - work_line_->CheckBinaryOp2addrWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(), + work_line_->CheckBinaryOp2addrWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(), reg_types_.DoubleLo(), reg_types_.DoubleHi(), reg_types_.DoubleLo(), reg_types_.DoubleHi()); break; @@ -2213,12 +2395,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::MUL_INT_LIT16: case Instruction::DIV_INT_LIT16: case Instruction::REM_INT_LIT16: - work_line_->CheckLiteralOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), false); + work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, true); break; case Instruction::AND_INT_LIT16: case Instruction::OR_INT_LIT16: case Instruction::XOR_INT_LIT16: - work_line_->CheckLiteralOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), true); + work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, true); break; case Instruction::ADD_INT_LIT8: case Instruction::RSUB_INT_LIT8: @@ -2228,18 +2410,71 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::SHL_INT_LIT8: case Instruction::SHR_INT_LIT8: case Instruction::USHR_INT_LIT8: - work_line_->CheckLiteralOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), false); + work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, false); break; case Instruction::AND_INT_LIT8: case Instruction::OR_INT_LIT8: case Instruction::XOR_INT_LIT8: - work_line_->CheckLiteralOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), true); + work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, false); + break; + + // Special instructions. + // + // Note: the following instructions encode offsets derived from class linking. + // As such they use Class*/Field*/AbstractMethod* as these offsets only have + // meaning if the class linking and resolution were successful. + case Instruction::IGET_QUICK: + VerifyIGetQuick(inst, reg_types_.Integer(), true); + break; + case Instruction::IGET_WIDE_QUICK: + VerifyIGetQuick(inst, reg_types_.LongLo(), true); + break; + case Instruction::IGET_OBJECT_QUICK: + VerifyIGetQuick(inst, reg_types_.JavaLangObject(false), false); + break; + case Instruction::IPUT_QUICK: + VerifyIPutQuick(inst, reg_types_.Integer(), true); + break; + case Instruction::IPUT_WIDE_QUICK: + VerifyIPutQuick(inst, reg_types_.LongLo(), true); + break; + case Instruction::IPUT_OBJECT_QUICK: + VerifyIPutQuick(inst, reg_types_.JavaLangObject(false), false); + break; + case Instruction::INVOKE_VIRTUAL_QUICK: + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); + mirror::AbstractMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range); + if (called_method != NULL) { + const char* descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + if (!return_type.IsLowHalf()) { + work_line_->SetResultRegisterType(return_type); + } else { + work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); + } + just_set_result = true; + } break; + } /* These should never appear during verification. */ + case Instruction::UNUSED_3E: + case Instruction::UNUSED_3F: + case Instruction::UNUSED_40: + case Instruction::UNUSED_41: + case Instruction::UNUSED_42: + case Instruction::UNUSED_43: + case Instruction::UNUSED_73: + case Instruction::UNUSED_79: + case Instruction::UNUSED_7A: + case Instruction::UNUSED_EB: + case Instruction::UNUSED_EC: case Instruction::UNUSED_ED: case Instruction::UNUSED_EE: case Instruction::UNUSED_EF: + case Instruction::UNUSED_F0: + case Instruction::UNUSED_F1: case Instruction::UNUSED_F2: case Instruction::UNUSED_F3: case Instruction::UNUSED_F4: @@ -2250,30 +2485,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::UNUSED_F9: case Instruction::UNUSED_FA: case Instruction::UNUSED_FB: - case Instruction::UNUSED_F0: - case Instruction::UNUSED_F1: - case Instruction::UNUSED_E3: - case Instruction::UNUSED_E8: - case Instruction::UNUSED_E7: - case Instruction::UNUSED_E4: - case Instruction::UNUSED_E9: case Instruction::UNUSED_FC: - case Instruction::UNUSED_E5: - case Instruction::UNUSED_EA: case Instruction::UNUSED_FD: - case Instruction::UNUSED_E6: - case Instruction::UNUSED_EB: case Instruction::UNUSED_FE: - case Instruction::UNUSED_3E: - case Instruction::UNUSED_3F: - case Instruction::UNUSED_40: - case Instruction::UNUSED_41: - case Instruction::UNUSED_42: - case Instruction::UNUSED_43: - case Instruction::UNUSED_73: - case Instruction::UNUSED_79: - case Instruction::UNUSED_7A: - case Instruction::UNUSED_EC: case Instruction::UNUSED_FF: Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_); break; @@ -2305,33 +2519,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_line_->SetResultTypeToUnknown(); } - /* Handle "continue". Tag the next consecutive instruction. */ - if ((opcode_flags & Instruction::kContinue) != 0) { - uint32_t next_insn_idx = work_insn_idx_ + CurrentInsnFlags()->GetLengthInCodeUnits(); - if (next_insn_idx >= code_item_->insns_size_in_code_units_) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Execution can walk off end of code area"; - return false; - } - // The only way to get to a move-exception instruction is to get thrown there. Make sure the - // next instruction isn't one. - if (!CheckNotMoveException(code_item_->insns_, next_insn_idx)) { - return false; - } - RegisterLine* next_line = reg_table_.GetLine(next_insn_idx); - if (next_line != NULL) { - // Merge registers into what we have for the next instruction, and set the "changed" flag if - // needed. - if (!UpdateRegisters(next_insn_idx, work_line_.get())) { - return false; - } - } else { - /* - * We're not recording register data for the next instruction, so we don't know what the prior - * state was. We have to assume that something has changed and re-evaluate it. - */ - insn_flags_[next_insn_idx].SetChanged(); - } - } + /* * Handle "branch". Tag the branch target. @@ -2357,8 +2545,14 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { return false; } /* update branch target, set "changed" if appropriate */ - if (!UpdateRegisters(work_insn_idx_ + branch_target, work_line_.get())) { - return false; + if (NULL != branch_line.get()) { + if (!UpdateRegisters(work_insn_idx_ + branch_target, branch_line.get())) { + return false; + } + } else { + if (!UpdateRegisters(work_insn_idx_ + branch_target, work_line_.get())) { + return false; + } } } @@ -2433,7 +2627,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * monitor-enter and the monitor stack was empty, we don't need a catch-all (if it throws, * it will do so before grabbing the lock). */ - if (dec_insn.opcode != Instruction::MONITOR_ENTER || work_line_->MonitorStackDepth() != 1) { + if (inst->Opcode() != Instruction::MONITOR_ENTER || work_line_->MonitorStackDepth() != 1) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected to be within a catch-all for an instruction where a monitor is held"; return false; @@ -2441,6 +2635,42 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } } + /* Handle "continue". Tag the next consecutive instruction. + * Note: Keep the code handling "continue" case below the "branch" and "switch" cases, + * because it changes work_line_ when performing peephole optimization + * and this change should not be used in those cases. + */ + if ((opcode_flags & Instruction::kContinue) != 0) { + uint32_t next_insn_idx = work_insn_idx_ + CurrentInsnFlags()->GetLengthInCodeUnits(); + if (next_insn_idx >= code_item_->insns_size_in_code_units_) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Execution can walk off end of code area"; + return false; + } + // The only way to get to a move-exception instruction is to get thrown there. Make sure the + // next instruction isn't one. + if (!CheckNotMoveException(code_item_->insns_, next_insn_idx)) { + return false; + } + if (NULL != fallthrough_line.get()) { + // Make workline consistent with fallthrough computed from peephole optimization. + work_line_->CopyFromLine(fallthrough_line.get()); + } + RegisterLine* next_line = reg_table_.GetLine(next_insn_idx); + if (next_line != NULL) { + // Merge registers into what we have for the next instruction, + // and set the "changed" flag if needed. + if (!UpdateRegisters(next_insn_idx, work_line_.get())) { + return false; + } + } else { + /* + * We're not recording register data for the next instruction, so we don't know what the + * prior state was. We have to assume that something has changed and re-evaluate it. + */ + insn_flags_[next_insn_idx].SetChanged(); + } + } + /* If we're returning from the method, make sure monitor stack is empty. */ if ((opcode_flags & Instruction::kReturn) != 0) { if (!work_line_->VerifyMonitorStackEmpty()) { @@ -2472,7 +2702,8 @@ const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) { const RegType& referrer = GetDeclaringClass(); mirror::Class* klass = dex_cache_->GetResolvedType(class_idx); const RegType& result = - klass != NULL ? reg_types_.FromClass(klass, klass->IsFinal()) + klass != NULL ? reg_types_.FromClass(descriptor, klass, + klass->CannotBeAssignedFromOtherTypes()) : reg_types_.FromDescriptor(class_loader_, descriptor, false); if (result.IsConflict()) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor @@ -2625,12 +2856,14 @@ mirror::AbstractMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex return res_method; } -mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstruction& dec_insn, - MethodType method_type, bool is_range, +mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst, + MethodType method_type, + bool is_range, bool is_super) { // Resolve the method. This could be an abstract or concrete method depending on what sort of call // we're making. - mirror::AbstractMethod* res_method = ResolveMethodAndCheckAccess(dec_insn.vB, method_type); + const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + mirror::AbstractMethod* res_method = ResolveMethodAndCheckAccess(method_idx, method_type); if (res_method == NULL) { // error or class is unresolved return NULL; } @@ -2658,9 +2891,9 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstru } } // We use vAA as our expected arg count, rather than res_method->insSize, because we need to - // match the call to the signature. Also, we might might be calling through an abstract method + // match the call to the signature. Also, we might be calling through an abstract method // definition (which doesn't have register count values). - size_t expected_args = dec_insn.vA; + const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); /* caught by static verifier */ DCHECK(is_range || expected_args <= 5); if (expected_args > code_item_->outs_size_) { @@ -2676,7 +2909,7 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstru */ size_t actual_args = 0; if (!res_method->IsStatic()) { - const RegType& actual_arg_type = work_line_->GetInvocationThis(dec_insn); + const RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range); if (actual_arg_type.IsConflict()) { // GetInvocationThis failed. return NULL; } @@ -2686,7 +2919,9 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstru } if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) { mirror::Class* klass = res_method->GetDeclaringClass(); - const RegType& res_method_class = reg_types_.FromClass(klass, klass->IsFinal()); + const RegType& res_method_class = + reg_types_.FromClass(ClassHelper(klass).GetDescriptor(), klass, + klass->CannotBeAssignedFromOtherTypes()); if (!res_method_class.IsAssignableFrom(actual_arg_type)) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type << "' not instance of '" << res_method_class << "'"; @@ -2702,6 +2937,10 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstru MethodHelper mh(res_method); const DexFile::TypeList* params = mh.GetParameterTypeList(); size_t params_size = params == NULL ? 0 : params->Size(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } for (size_t param_index = 0; param_index < params_size; param_index++) { if (actual_args >= expected_args) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" << PrettyMethod(res_method) @@ -2717,7 +2956,7 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstru return NULL; } const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); - uint32_t get_reg = is_range ? dec_insn.vC + actual_args : dec_insn.arg[actual_args]; + uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; if (!work_line_->VerifyRegisterType(get_reg, reg_type)) { return res_method; } @@ -2732,9 +2971,142 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstru } } -void MethodVerifier::VerifyNewArray(const DecodedInstruction& dec_insn, bool is_filled, - bool is_range) { - const RegType& res_type = ResolveClassAndCheckAccess(is_filled ? dec_insn.vB : dec_insn.vC); +mirror::AbstractMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, + RegisterLine* reg_line, + bool is_range) { + DCHECK(inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK || + inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); + const RegType& actual_arg_type = reg_line->GetInvocationThis(inst, is_range); + if (actual_arg_type.IsConflict()) { // GetInvocationThis failed. + return NULL; + } + mirror::Class* this_class = NULL; + if (!actual_arg_type.IsUnresolvedTypes()) { + this_class = actual_arg_type.GetClass(); + } else { + const std::string& descriptor(actual_arg_type.GetDescriptor()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + this_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + if (this_class == NULL) { + Thread::Current()->ClearException(); + // Look for a system class + this_class = class_linker->FindClass(descriptor.c_str(), NULL); + } + } + if (this_class == NULL) { + return NULL; + } + mirror::ObjectArray<mirror::AbstractMethod>* vtable = this_class->GetVTable(); + CHECK(vtable != NULL); + uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + CHECK(vtable_index < vtable->GetLength()); + mirror::AbstractMethod* res_method = vtable->Get(vtable_index); + CHECK(!Thread::Current()->IsExceptionPending()); + return res_method; +} + +mirror::AbstractMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, + bool is_range) { + DCHECK(Runtime::Current()->IsStarted()); + mirror::AbstractMethod* res_method = GetQuickInvokedMethod(inst, work_line_.get(), + is_range); + if (res_method == NULL) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name(); + return NULL; + } + CHECK(!res_method->IsDirect() && !res_method->IsStatic()); + + // We use vAA as our expected arg count, rather than res_method->insSize, because we need to + // match the call to the signature. Also, we might be calling through an abstract method + // definition (which doesn't have register count values). + const RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range); + if (actual_arg_type.IsConflict()) { // GetInvocationThis failed. + return NULL; + } + const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); + /* caught by static verifier */ + DCHECK(is_range || expected_args <= 5); + if (expected_args > code_item_->outs_size_) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args + << ") exceeds outsSize (" << code_item_->outs_size_ << ")"; + return NULL; + } + + /* + * Check the "this" argument, which must be an instance of the class that declared the method. + * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a + * rigorous check here (which is okay since we have to do it at runtime). + */ + if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; + return NULL; + } + if (!actual_arg_type.IsZero()) { + mirror::Class* klass = res_method->GetDeclaringClass(); + const RegType& res_method_class = + reg_types_.FromClass(ClassHelper(klass).GetDescriptor(), klass, + klass->CannotBeAssignedFromOtherTypes()); + if (!res_method_class.IsAssignableFrom(actual_arg_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type + << "' not instance of '" << res_method_class << "'"; + return NULL; + } + } + /* + * Process the target method's signature. This signature may or may not + * have been verified, so we can't assume it's properly formed. + */ + MethodHelper mh(res_method); + const DexFile::TypeList* params = mh.GetParameterTypeList(); + size_t params_size = params == NULL ? 0 : params->Size(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } + size_t actual_args = 1; + for (size_t param_index = 0; param_index < params_size; param_index++) { + if (actual_args >= expected_args) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" << PrettyMethod(res_method) + << "'. Expected " << expected_args << " arguments, processing argument " << actual_args + << " (where longs/doubles count twice)."; + return NULL; + } + const char* descriptor = + mh.GetTypeDescriptorFromTypeIdx(params->GetTypeItem(param_index).type_idx_); + if (descriptor == NULL) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method) + << " missing signature component"; + return NULL; + } + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; + if (!work_line_->VerifyRegisterType(get_reg, reg_type)) { + return res_method; + } + actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1; + } + if (actual_args != expected_args) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method) + << " expected " << expected_args << " arguments, found " << actual_args; + return NULL; + } else { + return res_method; + } +} + +void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range) { + uint32_t type_idx; + if (!is_filled) { + DCHECK_EQ(inst->Opcode(), Instruction::NEW_ARRAY); + type_idx = inst->VRegC_22c(); + } else if (!is_range) { + DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY); + type_idx = inst->VRegB_35c(); + } else { + DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY_RANGE); + type_idx = inst->VRegB_3rc(); + } + const RegType& res_type = ResolveClassAndCheckAccess(type_idx); if (res_type.IsConflict()) { // bad class DCHECK_NE(failures_.size(), 0U); } else { @@ -2743,43 +3115,49 @@ void MethodVerifier::VerifyNewArray(const DecodedInstruction& dec_insn, bool is_ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "new-array on non-array class " << res_type; } else if (!is_filled) { /* make sure "size" register is valid type */ - work_line_->VerifyRegisterType(dec_insn.vB, reg_types_.Integer()); + work_line_->VerifyRegisterType(inst->VRegB_22c(), reg_types_.Integer()); /* set register type to array class */ - work_line_->SetRegisterType(dec_insn.vA, res_type); + const RegType& precise_type = reg_types_.FromUninitialized(res_type); + work_line_->SetRegisterType(inst->VRegA_22c(), precise_type); } else { // Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of // the list and fail. It's legal, if silly, for arg_count to be zero. const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_); - uint32_t arg_count = dec_insn.vA; + uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } for (size_t ui = 0; ui < arg_count; ui++) { - uint32_t get_reg = is_range ? dec_insn.vC + ui : dec_insn.arg[ui]; + uint32_t get_reg = is_range ? inst->VRegC_3rc() + ui : arg[ui]; if (!work_line_->VerifyRegisterType(get_reg, expected_type)) { work_line_->SetResultRegisterType(reg_types_.Conflict()); return; } } // filled-array result goes into "result" register - work_line_->SetResultRegisterType(res_type); + const RegType& precise_type = reg_types_.FromUninitialized(res_type); + work_line_->SetResultRegisterType(precise_type); } } } -void MethodVerifier::VerifyAGet(const DecodedInstruction& dec_insn, +void MethodVerifier::VerifyAGet(const Instruction* inst, const RegType& insn_type, bool is_primitive) { - const RegType& index_type = work_line_->GetRegisterType(dec_insn.vC); + const RegType& index_type = work_line_->GetRegisterType(inst->VRegC_23x()); if (!index_type.IsArrayIndexTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")"; } else { - const RegType& array_type = work_line_->GetRegisterType(dec_insn.vB); + const RegType& array_type = work_line_->GetRegisterType(inst->VRegB_23x()); if (array_type.IsZero()) { // Null array class; this code path will fail at runtime. Infer a merge-able type from the // instruction type. TODO: have a proper notion of bottom here. if (!is_primitive || insn_type.IsCategory1Types()) { // Reference or category 1 - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Zero()); + work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Zero()); } else { // Category 2 - work_line_->SetRegisterTypeWide(dec_insn.vA, reg_types_.FromCat2ConstLo(0, false), + work_line_->SetRegisterTypeWide(inst->VRegA_23x(), reg_types_.FromCat2ConstLo(0, false), reg_types_.FromCat2ConstHi(0, false)); } } else if (!array_type.IsArrayTypes()) { @@ -2803,9 +3181,9 @@ void MethodVerifier::VerifyAGet(const DecodedInstruction& dec_insn, // instruction, which can't differentiate object types and ints from floats, longs from // doubles. if (!component_type.IsLowHalf()) { - work_line_->SetRegisterType(dec_insn.vA, component_type); + work_line_->SetRegisterType(inst->VRegA_23x(), component_type); } else { - work_line_->SetRegisterTypeWide(dec_insn.vA, component_type, + work_line_->SetRegisterTypeWide(inst->VRegA_23x(), component_type, component_type.HighHalf(®_types_)); } } @@ -2813,13 +3191,13 @@ void MethodVerifier::VerifyAGet(const DecodedInstruction& dec_insn, } } -void MethodVerifier::VerifyAPut(const DecodedInstruction& dec_insn, +void MethodVerifier::VerifyAPut(const Instruction* inst, const RegType& insn_type, bool is_primitive) { - const RegType& index_type = work_line_->GetRegisterType(dec_insn.vC); + const RegType& index_type = work_line_->GetRegisterType(inst->VRegC_23x()); if (!index_type.IsArrayIndexTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")"; } else { - const RegType& array_type = work_line_->GetRegisterType(dec_insn.vB); + const RegType& array_type = work_line_->GetRegisterType(inst->VRegB_23x()); if (array_type.IsZero()) { // Null array type; this code path will fail at runtime. Infer a merge-able type from the // instruction type. @@ -2843,7 +3221,7 @@ void MethodVerifier::VerifyAPut(const DecodedInstruction& dec_insn, // The instruction agrees with the type of array, confirm the value to be stored does too // Note: we use the instruction type (rather than the component type) for aput-object as // incompatible classes will be caught at runtime as an array store exception - work_line_->VerifyRegisterType(dec_insn.vA, is_primitive ? component_type : insn_type); + work_line_->VerifyRegisterType(inst->VRegA_23x(), is_primitive ? component_type : insn_type); } } } @@ -2865,7 +3243,7 @@ mirror::Field* MethodVerifier::GetStaticField(int field_idx) { mirror::Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_, class_loader_); if (field == NULL) { - LOG(INFO) << "unable to resolve static field " << field_idx << " (" + LOG(INFO) << "Unable to resolve static field " << field_idx << " (" << dex_file_->GetFieldName(field_id) << ") in " << dex_file_->GetFieldDeclaringClassDescriptor(field_id); DCHECK(Thread::Current()->IsExceptionPending()); @@ -2900,7 +3278,7 @@ mirror::Field* MethodVerifier::GetInstanceField(const RegType& obj_type, int fie mirror::Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_, class_loader_); if (field == NULL) { - LOG(INFO) << "unable to resolve instance field " << field_idx << " (" + LOG(INFO) << "Unable to resolve instance field " << field_idx << " (" << dex_file_->GetFieldName(field_id) << ") in " << dex_file_->GetFieldDeclaringClassDescriptor(field_id); DCHECK(Thread::Current()->IsExceptionPending()); @@ -2920,7 +3298,9 @@ mirror::Field* MethodVerifier::GetInstanceField(const RegType& obj_type, int fie return field; } else { mirror::Class* klass = field->GetDeclaringClass(); - const RegType& field_klass = reg_types_.FromClass(klass, klass->IsFinal()); + const RegType& field_klass = + reg_types_.FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id), + klass, klass->CannotBeAssignedFromOtherTypes()); if (obj_type.IsUninitializedTypes() && (!IsConstructor() || GetDeclaringClass().Equals(obj_type) || !field_klass.Equals(GetDeclaringClass()))) { @@ -2943,14 +3323,14 @@ mirror::Field* MethodVerifier::GetInstanceField(const RegType& obj_type, int fie } } -void MethodVerifier::VerifyISGet(const DecodedInstruction& dec_insn, - const RegType& insn_type, bool is_primitive, bool is_static) { - uint32_t field_idx = is_static ? dec_insn.vB : dec_insn.vC; +void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_type, + bool is_primitive, bool is_static) { + uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); mirror::Field* field; if (is_static) { field = GetStaticField(field_idx); } else { - const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB); + const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); field = GetInstanceField(object_type, field_idx); } const char* descriptor; @@ -2964,6 +3344,7 @@ void MethodVerifier::VerifyISGet(const DecodedInstruction& dec_insn, loader = class_loader_; } const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); + const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); if (is_primitive) { if (field_type.Equals(insn_type) || (field_type.IsFloat() && insn_type.IsIntegralTypes()) || @@ -2985,25 +3366,25 @@ void MethodVerifier::VerifyISGet(const DecodedInstruction& dec_insn, << " to be compatible with type '" << insn_type << "' but found type '" << field_type << "' in get-object"; - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Conflict()); + work_line_->SetRegisterType(vregA, reg_types_.Conflict()); return; } } if (!field_type.IsLowHalf()) { - work_line_->SetRegisterType(dec_insn.vA, field_type); + work_line_->SetRegisterType(vregA, field_type); } else { - work_line_->SetRegisterTypeWide(dec_insn.vA, field_type, field_type.HighHalf(®_types_)); + work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(®_types_)); } } -void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn, - const RegType& insn_type, bool is_primitive, bool is_static) { - uint32_t field_idx = is_static ? dec_insn.vB : dec_insn.vC; +void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_type, + bool is_primitive, bool is_static) { + uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); mirror::Field* field; if (is_static) { field = GetStaticField(field_idx); } else { - const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB); + const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); field = GetInstanceField(object_type, field_idx); } const char* descriptor; @@ -3024,11 +3405,12 @@ void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn, return; } } + const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); if (is_primitive) { // Primitive field assignability rules are weaker than regular assignability rules bool instruction_compatible; bool value_compatible; - const RegType& value_type = work_line_->GetRegisterType(dec_insn.vA); + const RegType& value_type = work_line_->GetRegisterType(vregA); if (field_type.IsIntegralTypes()) { instruction_compatible = insn_type.IsIntegralTypes(); value_compatible = value_type.IsIntegralTypes(); @@ -3056,7 +3438,7 @@ void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn, return; } if (!value_compatible) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << dec_insn.vA + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA << " of type " << value_type << " but expected " << field_type << " for store to " << PrettyField(field) << " in put"; @@ -3070,7 +3452,176 @@ void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn, << "' in put-object"; return; } - work_line_->VerifyRegisterType(dec_insn.vA, field_type); + work_line_->VerifyRegisterType(vregA, field_type); + } +} + +// Look for an instance field with this offset. +// TODO: we may speed up the search if offsets are sorted by doing a quick search. +static mirror::Field* FindInstanceFieldWithOffset(const mirror::Class* klass, + uint32_t field_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const mirror::ObjectArray<mirror::Field>* instance_fields = klass->GetIFields(); + if (instance_fields != NULL) { + for (int32_t i = 0, e = instance_fields->GetLength(); i < e; ++i) { + mirror::Field* field = instance_fields->Get(i); + if (field->GetOffset().Uint32Value() == field_offset) { + return field; + } + } + } + // We did not find field in class: look into superclass. + if (klass->GetSuperClass() != NULL) { + return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset); + } else { + return NULL; + } +} + +// Returns the access field of a quick field access (iget/iput-quick) or NULL +// if it cannot be found. +mirror::Field* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, + RegisterLine* reg_line) { + DCHECK(inst->Opcode() == Instruction::IGET_QUICK || + inst->Opcode() == Instruction::IGET_WIDE_QUICK || + inst->Opcode() == Instruction::IGET_OBJECT_QUICK || + inst->Opcode() == Instruction::IPUT_QUICK || + inst->Opcode() == Instruction::IPUT_WIDE_QUICK || + inst->Opcode() == Instruction::IPUT_OBJECT_QUICK); + const RegType& object_type = reg_line->GetRegisterType(inst->VRegB_22c()); + mirror::Class* object_class = NULL; + if (!object_type.IsUnresolvedTypes()) { + object_class = object_type.GetClass(); + } else { + // We need to resolve the class from its descriptor. + const std::string& descriptor(object_type.GetDescriptor()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + object_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + if (object_class == NULL) { + Thread::Current()->ClearException(); + // Look for a system class + object_class = class_linker->FindClass(descriptor.c_str(), NULL); + } + } + if (object_class == NULL) { + // Failed to get the Class* from reg type. + LOG(WARNING) << "Failed to get Class* from " << object_type; + return NULL; + } + uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c()); + return FindInstanceFieldWithOffset(object_class, field_offset); +} + +void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) { + DCHECK(Runtime::Current()->IsStarted()); + mirror::Field* field = GetQuickFieldAccess(inst, work_line_.get()); + if (field == NULL) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); + return; + } + const char* descriptor = FieldHelper(field).GetTypeDescriptor(); + mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader(); + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); + const uint32_t vregA = inst->VRegA_22c(); + if (is_primitive) { + if (field_type.Equals(insn_type) || + (field_type.IsFloat() && insn_type.IsIntegralTypes()) || + (field_type.IsDouble() && insn_type.IsLongTypes())) { + // expected that read is of the correct primitive type or that int reads are reading + // floats or long reads are reading doubles + } else { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << field_type << "' in get"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << field_type + << "' in get-object"; + work_line_->SetRegisterType(vregA, reg_types_.Conflict()); + return; + } + } + if (!field_type.IsLowHalf()) { + work_line_->SetRegisterType(vregA, field_type); + } else { + work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(®_types_)); + } +} + +void MethodVerifier::VerifyIPutQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) { + DCHECK(Runtime::Current()->IsStarted()); + mirror::Field* field = GetQuickFieldAccess(inst, work_line_.get()); + if (field == NULL) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); + return; + } + const char* descriptor = FieldHelper(field).GetTypeDescriptor(); + mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader(); + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); + if (field != NULL) { + if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { + Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) + << " from other class " << GetDeclaringClass(); + return; + } + } + const uint32_t vregA = inst->VRegA_22c(); + if (is_primitive) { + // Primitive field assignability rules are weaker than regular assignability rules + bool instruction_compatible; + bool value_compatible; + const RegType& value_type = work_line_->GetRegisterType(vregA); + if (field_type.IsIntegralTypes()) { + instruction_compatible = insn_type.IsIntegralTypes(); + value_compatible = value_type.IsIntegralTypes(); + } else if (field_type.IsFloat()) { + instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int + value_compatible = value_type.IsFloatTypes(); + } else if (field_type.IsLong()) { + instruction_compatible = insn_type.IsLong(); + value_compatible = value_type.IsLongTypes(); + } else if (field_type.IsDouble()) { + instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long + value_compatible = value_type.IsDoubleTypes(); + } else { + instruction_compatible = false; // reference field with primitive store + value_compatible = false; // unused + } + if (!instruction_compatible) { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << field_type + << "' in put"; + return; + } + if (!value_compatible) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA + << " of type " << value_type + << " but expected " << field_type + << " for store to " << PrettyField(field) << " in put"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << field_type + << "' in put-object"; + return; + } + work_line_->VerifyRegisterType(vregA, field_type); } } @@ -3128,14 +3679,18 @@ const RegType& MethodVerifier::GetMethodReturnType() { } const RegType& MethodVerifier::GetDeclaringClass() { - if (foo_method_ != NULL) { - mirror::Class* klass = foo_method_->GetDeclaringClass(); - return reg_types_.FromClass(klass, klass->IsFinal()); - } else { + if (declaring_class_ == NULL) { const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); - return reg_types_.FromDescriptor(class_loader_, descriptor, false); + if (mirror_method_ != NULL) { + mirror::Class* klass = mirror_method_->GetDeclaringClass(); + declaring_class_ = ®_types_.FromClass(descriptor, klass, + klass->CannotBeAssignedFromOtherTypes()); + } else { + declaring_class_ = ®_types_.FromDescriptor(class_loader_, descriptor, false); + } } + return *declaring_class_; } void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, @@ -3160,7 +3715,39 @@ void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bit *log2_max_gc_pc = i; } -MethodVerifier::PcToConreteMethod* MethodVerifier::GenerateDevirtMap() { +MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() { + /* + * Walks over the method code and adds any cast instructions in which + * the type cast is implicit to a set, which is used in the code generation + * to elide these casts. + */ + if (!failure_messages_.empty()) { + return NULL; + } + UniquePtr<MethodSafeCastSet> mscs; + const Instruction* inst = Instruction::At(code_item_->insns_); + const Instruction* end = Instruction::At(code_item_->insns_ + + code_item_->insns_size_in_code_units_); + + for (; inst < end; inst = inst->Next()) { + if (Instruction::CHECK_CAST != inst->Opcode()) { + continue; + } + uint32_t dex_pc = inst->GetDexPc(code_item_->insns_); + RegisterLine* line = reg_table_.GetLine(dex_pc); + const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c())); + const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c()); + if (cast_type.IsStrictlyAssignableFrom(reg_type)) { + if (mscs.get() == NULL) { + mscs.reset(new MethodSafeCastSet()); + } + mscs->insert(dex_pc); + } + } + return mscs.release(); +} + +MethodVerifier::PcToConcreteMethodMap* MethodVerifier::GenerateDevirtMap() { // It is risky to rely on reg_types for sharpening in cases of soft // verification, we might end up sharpening to a wrong implementation. Just abort. @@ -3168,39 +3755,43 @@ MethodVerifier::PcToConreteMethod* MethodVerifier::GenerateDevirtMap() { return NULL; } - PcToConreteMethod* pc_to_concrete_method = new PcToConreteMethod(); - uint32_t dex_pc = 0; + UniquePtr<PcToConcreteMethodMap> pc_to_concrete_method_map; const uint16_t* insns = code_item_->insns_ ; const Instruction* inst = Instruction::At(insns); + const Instruction* end = Instruction::At(insns + code_item_->insns_size_in_code_units_); - for (; dex_pc < code_item_->insns_size_in_code_units_; - dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits(), inst = inst->Next()) { - + for (; inst < end; inst = inst->Next()) { bool is_virtual = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) || (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) || (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE); - if(!(is_interface || is_virtual)) - continue; - - // Check if vC ("this" pointer in the instruction) has a precise type. + if(!is_interface && !is_virtual) { + continue; + } + // Get reg type for register holding the reference to the object that will be dispatched upon. + uint32_t dex_pc = inst->GetDexPc(insns); RegisterLine* line = reg_table_.GetLine(dex_pc); - DecodedInstruction dec_insn(inst); - const RegType& reg_type(line->GetRegisterType(dec_insn.vC)); + bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) || + (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE); + const RegType& + reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c())); - if (!reg_type.IsPreciseReference()) { - continue; + if (!reg_type.HasClass()) { + // We will compute devirtualization information only when we know the Class of the reg type. + continue; } - - CHECK(!(reg_type.GetClass()->IsInterface())); - // If the class is an array class, it can be both Abstract and final and so - // the reg_type will be created as precise. - CHECK(!(reg_type.GetClass()->IsAbstract()) || reg_type.GetClass()->IsArrayClass()); - // Find the abstract method. - // vB has the method index. - mirror::AbstractMethod* abstract_method = NULL ; - abstract_method = dex_cache_->GetResolvedMethod(dec_insn.vB); + mirror::Class* reg_class = reg_type.GetClass(); + if (reg_class->IsInterface()) { + // We can't devirtualize when the known type of the register is an interface. + continue; + } + if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) { + // We can't devirtualize abstract classes except on arrays of abstract classes. + continue; + } + mirror::AbstractMethod* abstract_method = + dex_cache_->GetResolvedMethod(is_range ? inst->VRegB_3rc() : inst->VRegB_35c()); if(abstract_method == NULL) { // If the method is not found in the cache this means that it was never found // by ResolveMethodAndCheckAccess() called when verifying invoke_*. @@ -3214,28 +3805,24 @@ MethodVerifier::PcToConreteMethod* MethodVerifier::GenerateDevirtMap() { if (is_virtual) { concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method); } - - if(concrete_method == NULL) { - // In cases where concrete_method is not found continue to the next invoke instead - // of crashing. + if (concrete_method == NULL || concrete_method->IsAbstract()) { + // In cases where concrete_method is not found, or is abstract, continue to the next invoke. continue; } - - CHECK(!concrete_method->IsAbstract()) << PrettyMethod(concrete_method); - // Build method reference. - CompilerDriver::MethodReference concrete_ref( - concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(), - concrete_method->GetDexMethodIndex()); - // Now Save the current PC and the concrete method reference to be used - // in compiler driver. - pc_to_concrete_method->Put(dex_pc, concrete_ref ); + if (reg_type.IsPreciseReference() || concrete_method->IsFinal() || + concrete_method->GetDeclaringClass()->IsFinal()) { + // If we knew exactly the class being dispatched upon, or if the target method cannot be + // overridden record the target to be used in the compiler driver. + if (pc_to_concrete_method_map.get() == NULL) { + pc_to_concrete_method_map.reset(new PcToConcreteMethodMap()); + } + CompilerDriver::MethodReference concrete_ref( + concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(), + concrete_method->GetDexMethodIndex()); + pc_to_concrete_method_map->Put(dex_pc, concrete_ref); } - - if (pc_to_concrete_method->size() == 0) { - delete pc_to_concrete_method; - return NULL ; } - return pc_to_concrete_method; + return pc_to_concrete_method_map.release(); } const std::vector<uint8_t>* MethodVerifier::GenerateGcMap() { @@ -3276,6 +3863,7 @@ const std::vector<uint8_t>* MethodVerifier::GenerateGcMap() { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Failed to encode GC map (size=" << table_size << ")"; return NULL; } + table->reserve(table_size); // Write table header table->push_back(format | ((ref_bitmap_bytes >> DexPcToReferenceMap::kRegMapFormatShift) & ~DexPcToReferenceMap::kRegMapFormatMask)); @@ -3326,9 +3914,10 @@ void MethodVerifier::VerifyGcMap(const std::vector<uint8_t>& data) { } } -void MethodVerifier::SetDexGcMap(CompilerDriver::MethodReference ref, const std::vector<uint8_t>& gc_map) { +void MethodVerifier::SetDexGcMap(CompilerDriver::MethodReference ref, + const std::vector<uint8_t>& gc_map) { { - MutexLock mu(Thread::Current(), *dex_gc_maps_lock_); + WriterMutexLock mu(Thread::Current(), *dex_gc_maps_lock_); DexGcMapTable::iterator it = dex_gc_maps_->find(ref); if (it != dex_gc_maps_->end()) { delete it->second; @@ -3336,42 +3925,69 @@ void MethodVerifier::SetDexGcMap(CompilerDriver::MethodReference ref, const std: } dex_gc_maps_->Put(ref, &gc_map); } - CHECK(GetDexGcMap(ref) != NULL); + DCHECK(GetDexGcMap(ref) != NULL); } -void MethodVerifier::SetDevirtMap(CompilerDriver::MethodReference ref, const PcToConreteMethod* devirt_map) { - MutexLock mu(Thread::Current(), *devirt_maps_lock_); - DevirtualizationMapTable::iterator it = devirt_maps_->find(ref); - if (it != devirt_maps_->end()) { +void MethodVerifier::SetSafeCastMap(CompilerDriver::MethodReference ref, + const MethodSafeCastSet* cast_set) { + MutexLock mu(Thread::Current(), *safecast_map_lock_); + SafeCastMap::iterator it = safecast_map_->find(ref); + if (it != safecast_map_->end()) { delete it->second; - devirt_maps_->erase(it); + safecast_map_->erase(it); } - devirt_maps_->Put(ref, devirt_map); - CHECK(devirt_maps_->find(ref) != devirt_maps_->end()); + safecast_map_->Put(ref, cast_set); + CHECK(safecast_map_->find(ref) != safecast_map_->end()); +} + +bool MethodVerifier::IsSafeCast(CompilerDriver::MethodReference ref, uint32_t pc) { + MutexLock mu(Thread::Current(), *safecast_map_lock_); + SafeCastMap::const_iterator it = safecast_map_->find(ref); + if (it == safecast_map_->end()) { + return false; + } + + // Look up the cast address in the set of safe casts + MethodVerifier::MethodSafeCastSet::const_iterator cast_it = it->second->find(pc); + return cast_it != it->second->end(); } const std::vector<uint8_t>* MethodVerifier::GetDexGcMap(CompilerDriver::MethodReference ref) { - MutexLock mu(Thread::Current(), *dex_gc_maps_lock_); + ReaderMutexLock mu(Thread::Current(), *dex_gc_maps_lock_); DexGcMapTable::const_iterator it = dex_gc_maps_->find(ref); if (it == dex_gc_maps_->end()) { - LOG(WARNING) << "Didn't find GC map for: " << PrettyMethod(ref.second, *ref.first); + LOG(WARNING) << "Didn't find GC map for: " << PrettyMethod(ref.dex_method_index, *ref.dex_file); return NULL; } CHECK(it->second != NULL); return it->second; } -const CompilerDriver::MethodReference* MethodVerifier::GetDevirtMap(CompilerDriver::MethodReference ref, uint32_t pc) { - MutexLock mu(Thread::Current(), *devirt_maps_lock_); +void MethodVerifier::SetDevirtMap(CompilerDriver::MethodReference ref, + const PcToConcreteMethodMap* devirt_map) { + WriterMutexLock mu(Thread::Current(), *devirt_maps_lock_); + DevirtualizationMapTable::iterator it = devirt_maps_->find(ref); + if (it != devirt_maps_->end()) { + delete it->second; + devirt_maps_->erase(it); + } + + devirt_maps_->Put(ref, devirt_map); + CHECK(devirt_maps_->find(ref) != devirt_maps_->end()); +} + +const CompilerDriver::MethodReference* MethodVerifier::GetDevirtMap(const CompilerDriver::MethodReference& ref, + uint32_t dex_pc) { + ReaderMutexLock mu(Thread::Current(), *devirt_maps_lock_); DevirtualizationMapTable::const_iterator it = devirt_maps_->find(ref); if (it == devirt_maps_->end()) { return NULL; } // Look up the PC in the map, get the concrete method to execute and return its reference. - MethodVerifier::PcToConreteMethod::const_iterator pc_to_concrete_method = it->second->find(pc); + MethodVerifier::PcToConcreteMethodMap::const_iterator pc_to_concrete_method = it->second->find(dex_pc); if(pc_to_concrete_method != it->second->end()) { return &(pc_to_concrete_method->second); } else { @@ -3423,26 +4039,36 @@ std::vector<int32_t> MethodVerifier::DescribeVRegs(uint32_t dex_pc) { return result; } -Mutex* MethodVerifier::dex_gc_maps_lock_ = NULL; +ReaderWriterMutex* MethodVerifier::dex_gc_maps_lock_ = NULL; MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL; -Mutex* MethodVerifier::devirt_maps_lock_ = NULL; +Mutex* MethodVerifier::safecast_map_lock_ = NULL; +MethodVerifier::SafeCastMap* MethodVerifier::safecast_map_ = NULL; + +ReaderWriterMutex* MethodVerifier::devirt_maps_lock_ = NULL; MethodVerifier::DevirtualizationMapTable* MethodVerifier::devirt_maps_ = NULL; Mutex* MethodVerifier::rejected_classes_lock_ = NULL; MethodVerifier::RejectedClassesTable* MethodVerifier::rejected_classes_ = NULL; void MethodVerifier::Init() { - dex_gc_maps_lock_ = new Mutex("verifier GC maps lock"); + dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock"); Thread* self = Thread::Current(); { - MutexLock mu(self, *dex_gc_maps_lock_); + WriterMutexLock mu(self, *dex_gc_maps_lock_); dex_gc_maps_ = new MethodVerifier::DexGcMapTable; } - devirt_maps_lock_ = new Mutex("verifier Devirtualization lock"); + safecast_map_lock_ = new Mutex("verifier Cast Elision lock"); + { + MutexLock mu(self, *safecast_map_lock_); + safecast_map_ = new MethodVerifier::SafeCastMap(); + } + + devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock"); + { - MutexLock mu(self, *devirt_maps_lock_); + WriterMutexLock mu(self, *devirt_maps_lock_); devirt_maps_ = new MethodVerifier::DevirtualizationMapTable(); } @@ -3457,7 +4083,7 @@ void MethodVerifier::Init() { void MethodVerifier::Shutdown() { Thread* self = Thread::Current(); { - MutexLock mu(self, *dex_gc_maps_lock_); + WriterMutexLock mu(self, *dex_gc_maps_lock_); STLDeleteValues(dex_gc_maps_); delete dex_gc_maps_; dex_gc_maps_ = NULL; @@ -3466,7 +4092,7 @@ void MethodVerifier::Shutdown() { dex_gc_maps_lock_ = NULL; { - MutexLock mu(self, *devirt_maps_lock_); + WriterMutexLock mu(self, *devirt_maps_lock_); STLDeleteValues(devirt_maps_); delete devirt_maps_; devirt_maps_ = NULL; diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h index ab7e3cc8dc..e90f9d95b0 100644 --- a/src/verifier/method_verifier.h +++ b/src/verifier/method_verifier.h @@ -187,15 +187,33 @@ class MethodVerifier { static const std::vector<uint8_t>* GetDexGcMap(CompilerDriver::MethodReference ref) LOCKS_EXCLUDED(dex_gc_maps_lock_); - static const CompilerDriver::MethodReference* GetDevirtMap(CompilerDriver::MethodReference ref, uint32_t pc) + static const CompilerDriver::MethodReference* GetDevirtMap(const CompilerDriver::MethodReference& ref, + uint32_t dex_pc) LOCKS_EXCLUDED(devirt_maps_lock_); + // Returns true if the cast can statically be verified to be redundant + // by using the check-cast elision peephole optimization in the verifier + static bool IsSafeCast(CompilerDriver::MethodReference ref, uint32_t pc) + LOCKS_EXCLUDED(safecast_map_lock_); + // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding - // to the locks held at 'dex_pc' in 'm'. + // to the locks held at 'dex_pc' in method 'm'. static void FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc, std::vector<uint32_t>& monitor_enter_dex_pcs) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Returns the accessed field corresponding to the quick instruction's field + // offset at 'dex_pc' in method 'm'. + static mirror::Field* FindAccessedFieldAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Returns the invoked method corresponding to the quick instruction's vtable + // index at 'dex_pc' in method 'm'. + static mirror::AbstractMethod* FindInvokedMethodAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void Shutdown(); @@ -248,6 +266,12 @@ class MethodVerifier { void FindLocksAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Field* FindAccessedFieldAtDexPc(uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::AbstractMethod* FindInvokedMethodAtDexPc(uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + /* * Compute the width of the instruction at each address in the instruction stream, and store it in * insn_flags_. Addresses that are in the middle of an instruction, or that are part of switch @@ -447,19 +471,18 @@ class MethodVerifier { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Perform verification of a new array instruction - void VerifyNewArray(const DecodedInstruction& dec_insn, bool is_filled, - bool is_range) + void VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Perform verification of an aget instruction. The destination register's type will be set to // be that of component type of the array unless the array type is unknown, in which case a // bottom type inferred from the type of instruction is used. is_primitive is false for an // aget-object. - void VerifyAGet(const DecodedInstruction& insn, const RegType& insn_type, + void VerifyAGet(const Instruction* inst, const RegType& insn_type, bool is_primitive) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Perform verification of an aput instruction. - void VerifyAPut(const DecodedInstruction& insn, const RegType& insn_type, + void VerifyAPut(const Instruction* inst, const RegType& insn_type, bool is_primitive) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Lookup instance field and fail for resolution violations @@ -470,15 +493,30 @@ class MethodVerifier { mirror::Field* GetStaticField(int field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Perform verification of an iget or sget instruction. - void VerifyISGet(const DecodedInstruction& insn, const RegType& insn_type, + void VerifyISGet(const Instruction* inst, const RegType& insn_type, bool is_primitive, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Perform verification of an iput or sput instruction. - void VerifyISPut(const DecodedInstruction& insn, const RegType& insn_type, + void VerifyISPut(const Instruction* inst, const RegType& insn_type, bool is_primitive, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Returns the access field of a quick field access (iget/iput-quick) or NULL + // if it cannot be found. + mirror::Field* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Perform verification of an iget-quick instruction. + void VerifyIGetQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Perform verification of an iput-quick instruction. + void VerifyIPutQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Resolves a class based on an index and performs access checks to ensure the referrer can // access the resolved class. const RegType& ResolveClassAndCheckAccess(uint32_t class_idx) @@ -522,10 +560,20 @@ class MethodVerifier { * Returns the resolved method on success, NULL on failure (with *failure * set appropriately). */ - mirror::AbstractMethod* VerifyInvocationArgs(const DecodedInstruction& dec_insn, - MethodType method_type, bool is_range, bool is_super) + mirror::AbstractMethod* VerifyInvocationArgs(const Instruction* inst, + MethodType method_type, + bool is_range, bool is_super) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::AbstractMethod* GetQuickInvokedMethod(const Instruction* inst, + RegisterLine* reg_line, + bool is_range) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::AbstractMethod* VerifyInvokeVirtualQuickArgs(const Instruction* inst, + bool is_range) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + /* * Verify that the target instruction is not "move-exception". It's important that the only way * to execute a move-exception is as the first instruction of an exception handler. @@ -574,23 +622,36 @@ class MethodVerifier { InstructionFlags* CurrentInsnFlags(); // All the GC maps that the verifier has created - typedef SafeMap<const CompilerDriver::MethodReference, const std::vector<uint8_t>*> DexGcMapTable; - static Mutex* dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + typedef SafeMap<const CompilerDriver::MethodReference, const std::vector<uint8_t>*, + CompilerDriver::MethodReferenceComparator> DexGcMapTable; + static ReaderWriterMutex* dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; static DexGcMapTable* dex_gc_maps_ GUARDED_BY(dex_gc_maps_lock_); static void SetDexGcMap(CompilerDriver::MethodReference ref, const std::vector<uint8_t>& dex_gc_map) LOCKS_EXCLUDED(dex_gc_maps_lock_); + // Cast elision types. + typedef std::set<uint32_t> MethodSafeCastSet; + typedef SafeMap<const CompilerDriver::MethodReference, const MethodSafeCastSet*, + CompilerDriver::MethodReferenceComparator> SafeCastMap; + MethodVerifier::MethodSafeCastSet* GenerateSafeCastSet() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void SetSafeCastMap(CompilerDriver::MethodReference ref, const MethodSafeCastSet* mscs); + LOCKS_EXCLUDED(safecast_map_lock_); + static Mutex* safecast_map_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + static SafeCastMap* safecast_map_ GUARDED_BY(safecast_map_lock_); + // Devirtualization map. - typedef SafeMap<const uint32_t, CompilerDriver::MethodReference> PcToConreteMethod; - typedef SafeMap<const CompilerDriver::MethodReference, const PcToConreteMethod*> - DevirtualizationMapTable; - MethodVerifier::PcToConreteMethod* GenerateDevirtMap() + typedef SafeMap<const uint32_t, CompilerDriver::MethodReference> PcToConcreteMethodMap; + typedef SafeMap<const CompilerDriver::MethodReference, const PcToConcreteMethodMap*, + CompilerDriver::MethodReferenceComparator> DevirtualizationMapTable; + MethodVerifier::PcToConcreteMethodMap* GenerateDevirtMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static Mutex* devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + static ReaderWriterMutex* devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; static DevirtualizationMapTable* devirt_maps_ GUARDED_BY(devirt_maps_lock_); - static void SetDevirtMap(CompilerDriver::MethodReference ref, const PcToConreteMethod* pc_method_map); + static void SetDevirtMap(CompilerDriver::MethodReference ref, + const PcToConcreteMethodMap* pc_method_map) LOCKS_EXCLUDED(devirt_maps_lock_); typedef std::set<CompilerDriver::ClassReference> RejectedClassesTable; static Mutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; @@ -613,20 +674,20 @@ class MethodVerifier { // Storage for the register status we're saving for later. UniquePtr<RegisterLine> saved_line_; - uint32_t dex_method_idx_; // The method we're working on. + const uint32_t dex_method_idx_; // The method we're working on. // Its object representation if known. - mirror::AbstractMethod* foo_method_ GUARDED_BY(Locks::mutator_lock_); - uint32_t method_access_flags_; // Method's access flags. - const DexFile* dex_file_; // The dex file containing the method. + mirror::AbstractMethod* mirror_method_ GUARDED_BY(Locks::mutator_lock_); + const uint32_t method_access_flags_; // Method's access flags. + const DexFile* const dex_file_; // The dex file containing the method. // The dex_cache for the declaring class of the method. mirror::DexCache* dex_cache_ GUARDED_BY(Locks::mutator_lock_); // The class loader for the declaring class of the method. mirror::ClassLoader* class_loader_ GUARDED_BY(Locks::mutator_lock_); - uint32_t class_def_idx_; // The class def index of the declaring class of the method. - const DexFile::CodeItem* code_item_; // The code item containing the code for the method. + const uint32_t class_def_idx_; // The class def index of the declaring class of the method. + const DexFile::CodeItem* const code_item_; // The code item containing the code for the method. + const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class. // Instruction widths and flags, one entry per code unit. UniquePtr<InstructionFlags[]> insn_flags_; - // The dex PC of a FindLocksAtDexPc request, -1 otherwise. uint32_t interesting_dex_pc_; // The container into which FindLocksAtDexPc should write the registers containing held locks, diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc index 32679f6100..1c61a29cee 100644 --- a/src/verifier/reg_type.cc +++ b/src/verifier/reg_type.cc @@ -25,6 +25,7 @@ #include "mirror/object_array-inl.h" #include "object_utils.h" #include "reg_type_cache-inl.h" +#include "scoped_thread_state_change.h" #include <limits> #include <sstream> @@ -32,7 +33,6 @@ namespace art { namespace verifier { -static const bool kIsDebugBuild = false; UndefinedType* UndefinedType::instance_ = NULL; ConflictType* ConflictType::instance_ = NULL; BooleanType* BooleanType::instance = NULL; @@ -46,6 +46,41 @@ DoubleLoType* DoubleLoType::instance_ = NULL; DoubleHiType* DoubleHiType::instance_ = NULL; IntegerType* IntegerType::instance_ = NULL; +int32_t RegType::ConstantValue() const { + ScopedObjectAccess soa(Thread::Current()); + LOG(FATAL) << "Unexpected call to ConstantValue: " << *this; + return 0; +} + +int32_t RegType::ConstantValueLo() const { + ScopedObjectAccess soa(Thread::Current()); + LOG(FATAL) << "Unexpected call to ConstantValueLo: " << *this; + return 0; +} + +int32_t RegType::ConstantValueHi() const { + ScopedObjectAccess soa(Thread::Current()); + LOG(FATAL) << "Unexpected call to ConstantValueHi: " << *this; + return 0; +} + +PrimitiveType::PrimitiveType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : RegType(klass, descriptor, cache_id) { + CHECK(klass != NULL); + CHECK(!descriptor.empty()); +} + +Cat1Type::Cat1Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : PrimitiveType(klass, descriptor, cache_id) { +} + +Cat2Type::Cat2Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : PrimitiveType(klass, descriptor, cache_id) { +} + std::string PreciseConstType::Dump() const { std::stringstream result; uint32_t val = ConstantValue(); @@ -70,36 +105,44 @@ std::string BooleanType::Dump() const { std::string ConflictType::Dump() const { return "Conflict"; } + std::string ByteType::Dump() const { return "Byte"; } + std::string ShortType::Dump() const { return "short"; } + std::string CharType::Dump() const { return "Char"; } + std::string FloatType::Dump() const { return "float"; } + std::string LongLoType::Dump() const { return "long (Low Half)"; } + std::string LongHiType::Dump() const { return "long (High Half)"; } + std::string DoubleLoType::Dump() const { return "Double (Low Half)"; } + std::string DoubleHiType::Dump() const { return "Double (High Half)"; } + std::string IntegerType::Dump() const { return "Integer"; } - -DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass, std::string& descriptor, +DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new DoubleHiType(klass, descriptor, cache_id); @@ -119,7 +162,7 @@ void DoubleHiType::Destroy() { } } -DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass, std::string& descriptor, +DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new DoubleLoType(klass, descriptor, cache_id); @@ -139,7 +182,7 @@ void DoubleLoType::Destroy() { } } -LongLoType* LongLoType::CreateInstance(mirror::Class* klass, std::string& descriptor, +LongLoType* LongLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new LongLoType(klass, descriptor, cache_id); @@ -147,7 +190,7 @@ LongLoType* LongLoType::CreateInstance(mirror::Class* klass, std::string& descri return instance_; } -LongHiType* LongHiType::CreateInstance(mirror::Class* klass, std::string& descriptor, +LongHiType* LongHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new LongHiType(klass, descriptor, cache_id); @@ -179,9 +222,8 @@ void LongLoType::Destroy() { } } -FloatType* FloatType::CreateInstance(mirror::Class* klass, std::string& descriptor, - uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +FloatType* FloatType::CreateInstance(mirror::Class* klass, const std::string& descriptor, + uint16_t cache_id) { if (instance_ == NULL) { instance_ = new FloatType(klass, descriptor, cache_id); } @@ -199,17 +241,19 @@ void FloatType::Destroy() { } } -CharType* CharType::CreateInstance(mirror::Class* klass, std::string& descriptor, +CharType* CharType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new CharType(klass, descriptor, cache_id); } return instance_; } + CharType* CharType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void CharType::Destroy() { if (instance_ != NULL) { delete instance_; @@ -217,81 +261,94 @@ void CharType::Destroy() { } } -ShortType* ShortType::CreateInstance(mirror::Class* klass, std::string& descriptor, +ShortType* ShortType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new ShortType(klass, descriptor, cache_id); } return instance_; } + ShortType* ShortType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void ShortType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } -ByteType* ByteType::CreateInstance(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + +ByteType* ByteType::CreateInstance(mirror::Class* klass, const std::string& descriptor, + uint16_t cache_id) { if (instance_ == NULL) { instance_ = new ByteType(klass, descriptor, cache_id); } return instance_; } + ByteType* ByteType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void ByteType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } -IntegerType* IntegerType::CreateInstance(mirror::Class* klass, std::string& descriptor, + +IntegerType* IntegerType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new IntegerType(klass, descriptor, cache_id); } return instance_; } + IntegerType* IntegerType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void IntegerType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } -ConflictType* ConflictType::CreateInstance(mirror::Class* klass, std::string& descriptor, + +ConflictType* ConflictType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new ConflictType(klass, descriptor, cache_id); } return instance_; } + ConflictType* ConflictType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void ConflictType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } -BooleanType* BooleanType::CreateInstance(mirror::Class* klass, std::string& descriptor, + +BooleanType* BooleanType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (BooleanType::instance == NULL) { instance = new BooleanType(klass, descriptor, cache_id); } return BooleanType::instance; } + BooleanType* BooleanType::GetInstance() { CHECK(BooleanType::instance != NULL); return BooleanType::instance; @@ -307,23 +364,33 @@ void BooleanType::Destroy() { std::string UndefinedType::Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return "Undefined"; } -UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass, std::string& descriptor, + +UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new UndefinedType(klass, descriptor, cache_id); } return instance_; } + UndefinedType* UndefinedType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void UndefinedType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } + +PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const std::string& descriptor, + uint16_t cache_id) + : RegType(klass, descriptor, cache_id) { + DCHECK(klass->IsInstantiable()); +} + std::string UnresolvedMergedType::Dump() const { std::stringstream result; std::set<uint16_t> types = GetMergedTypes(); @@ -338,6 +405,7 @@ std::string UnresolvedMergedType::Dump() const { result << ")"; return result.str(); } + std::string UnresolvedSuperClass::Dump() const { std::stringstream result; uint16_t super_type_id = GetUnresolvedSuperClassChildId(); @@ -358,7 +426,7 @@ std::string UnresolvedUninitializedRefType::Dump() const { return result.str(); } -std::string UnresolvedUninitialisedThisRefType::Dump() const { +std::string UnresolvedUninitializedThisRefType::Dump() const { std::stringstream result; result << "Unresolved And Uninitialized This Reference" << PrettyDescriptor(GetDescriptor()); return result.str(); @@ -376,13 +444,14 @@ std::string PreciseReferenceType::Dump() const { return result.str(); } -std::string UninitialisedReferenceType::Dump() const { +std::string UninitializedReferenceType::Dump() const { std::stringstream result; result << "Uninitialized Reference" << ": " << PrettyDescriptor(GetClass()); result << " Allocation PC: " << GetAllocationPc(); return result.str(); } -std::string UninitialisedThisReferenceType::Dump() const { + +std::string UninitializedThisReferenceType::Dump() const { std::stringstream result; result << "Uninitialized This Reference" << ": " << PrettyDescriptor(GetClass()); result << "Allocation PC: " << GetAllocationPc(); @@ -459,77 +528,8 @@ std::string ImpreciseConstHiType::Dump() const { return result.str(); } -BooleanType::BooleanType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -ConflictType::ConflictType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -ByteType::ByteType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -ShortType::ShortType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -CharType::CharType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -IntegerType::IntegerType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -ConstantType::ConstantType(uint32_t constat, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_): RegType(NULL, "", cache_id), constant_(constat) { -} - -ReferenceType::ReferenceType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, std::string& descriptor, - uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { - DCHECK(klass->IsInstantiable()); -} - -UnresolvedUninitialisedThisRefType::UnresolvedUninitialisedThisRefType(std::string& descriptor, - uint16_t cache_id) - : UninitializedType(NULL, descriptor, 0, cache_id) { -} - -UnresolvedUninitializedRefType::UnresolvedUninitializedRefType( std::string& descriptor, - uint32_t allocation_pc, uint16_t cache_id) - : UninitializedType(NULL, descriptor, allocation_pc, cache_id) { -} - -UninitialisedReferenceType::UninitialisedReferenceType(mirror::Class* klass, - std::string& descriptor, uint32_t allocation_pc, uint16_t cache_id) - : UninitializedType(klass, descriptor, allocation_pc, cache_id) { -} - -LongHiType::LongHiType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -FloatType::FloatType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -DoubleLoType::DoubleLoType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -DoubleHiType::DoubleHiType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { -} - -LongLoType::LongLoType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { +ConstantType::ConstantType(uint32_t constant, uint16_t cache_id) + : RegType(NULL, "", cache_id), constant_(constant) { } const RegType& UndefinedType::Merge(const RegType& incoming_type, RegTypeCache* reg_types) const @@ -575,6 +575,17 @@ Primitive::Type RegType::GetPrimitiveType() const { } } +bool UninitializedType::IsUninitializedTypes() const { + return true; +} + +bool UninitializedType::IsNonZeroReferenceTypes() const { + return true; +} + +bool UnresolvedType::IsNonZeroReferenceTypes() const { + return true; +} std::set<uint16_t> UnresolvedMergedType::GetMergedTypes() const { std::pair<uint16_t, uint16_t> refs = GetTopMergedTypes(); const RegType& _left(reg_type_cache_->GetFromId(refs.first)); @@ -612,7 +623,7 @@ const RegType& RegType::GetSuperClass(RegTypeCache* cache) const { if (super_klass != NULL) { // A super class of a precise type isn't precise as a precise type indicates the register // holds exactly that type. - return cache->FromClass(super_klass, false); + return cache->FromClass(ClassHelper(super_klass).GetDescriptor(), super_klass, false); } else { return cache->Zero(); } @@ -697,62 +708,72 @@ ImpreciseConstType::ImpreciseConstType(uint32_t constat, uint16_t cache_id) : ConstantType(constat, cache_id) { } -bool RegType::IsAssignableFrom(const RegType& src) const { - if (Equals(src)) { +static bool AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (lhs.Equals(rhs)) { return true; } else { - if (IsBoolean()) { - return src.IsBooleanTypes(); - } else if (IsByte()) { - return src.IsByteTypes(); - } else if (IsShort()) { - return src.IsShortTypes(); - } else if (IsChar()) { - return src.IsCharTypes(); - } else if (IsInteger()) { - return src.IsIntegralTypes(); - } else if (IsFloat()) { - return src.IsFloatTypes(); - } else if (IsLongLo()) { - return src.IsLongTypes(); - } else if (IsDoubleLo()) { - return src.IsDoubleTypes(); + if (lhs.IsBoolean()) { + return rhs.IsBooleanTypes(); + } else if (lhs.IsByte()) { + return rhs.IsByteTypes(); + } else if (lhs.IsShort()) { + return rhs.IsShortTypes(); + } else if (lhs.IsChar()) { + return rhs.IsCharTypes(); + } else if (lhs.IsInteger()) { + return rhs.IsIntegralTypes(); + } else if (lhs.IsFloat()) { + return rhs.IsFloatTypes(); + } else if (lhs.IsLongLo()) { + return rhs.IsLongTypes(); + } else if (lhs.IsDoubleLo()) { + return rhs.IsDoubleTypes(); } else { - if (!IsReferenceTypes()) { - LOG(FATAL) << "Unexpected register type in 4bleFrom: '" << src << "'"; + CHECK(lhs.IsReferenceTypes()) + << "Unexpected register type in IsAssignableFrom: '" + << lhs << "' := '" << rhs << "'"; + if (rhs.IsZero()) { + return true; // All reference types can be assigned null. + } else if (!rhs.IsReferenceTypes()) { + return false; // Expect rhs to be a reference type. + } else if (lhs.IsJavaLangObject()) { + return true; // All reference types can be assigned to Object. + } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) { + // If we're not strict allow assignment to any interface, see comment in ClassJoin. + return true; + } else if (lhs.IsJavaLangObjectArray()) { + return rhs.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[] + } else if (lhs.HasClass() && rhs.HasClass() && + lhs.GetClass()->IsAssignableFrom(rhs.GetClass())) { + // We're assignable from the Class point-of-view. + return true; + } else { + // Unresolved types are only assignable for null and equality. + return false; } - if (src.IsZero()) { - return true; // all reference types can be assigned null - } else if (!src.IsReferenceTypes()) { - return false; // expect src to be a reference type - } else if (IsJavaLangObject()) { - return true; // all reference types can be assigned to Object - } else if (!IsUnresolvedTypes() && GetClass()->IsInterface()) { - return true; // We allow assignment to any interface, see comment in ClassJoin - } else if (IsJavaLangObjectArray()) { - return src.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[] - } else if (!IsUnresolvedTypes() && !src.IsUnresolvedTypes() && - GetClass()->IsAssignableFrom(src.GetClass())) { - // We're assignable from the Class point-of-view - return true; - } else if (IsUnresolvedTypes()) { - // Unresolved types are only assignable for null, Object and equality. - return (src.IsZero() || src.IsJavaLangObject()); - } else { - return false; - } } } } +bool RegType::IsAssignableFrom(const RegType& src) const { + return AssignableFrom(*this, src, false); +} + +bool RegType::IsStrictlyAssignableFrom(const RegType& src) const { + return AssignableFrom(*this, src, true); +} + int32_t ConstantType::ConstantValue() const { DCHECK(IsConstantTypes()); return constant_; } + int32_t ConstantType::ConstantValueLo() const { DCHECK(IsConstantLo()); return constant_; } + int32_t ConstantType::ConstantValueHi() const { if (IsConstantHi() || IsPreciseConstantHi() || IsImpreciseConstantHi()) { return constant_; @@ -761,6 +782,7 @@ int32_t ConstantType::ConstantValueHi() const { return 0; } } + static const RegType& SelectNonConstant(const RegType& a, const RegType& b) { return a.IsConstant() ? b : a; } @@ -884,7 +906,7 @@ const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_ty } else if (c2 == join_class && !incoming_type.IsPreciseReference()) { return incoming_type; } else { - return reg_types->FromClass(join_class, false); + return reg_types->FromClass(ClassHelper(join_class).GetDescriptor(), join_class, false); } } } else { @@ -949,33 +971,22 @@ void RegType::CheckInvariants() const { CHECK(descriptor_.empty()) << *this; CHECK(klass_ == NULL) << *this; } + if (klass_ != NULL) { + CHECK(!descriptor_.empty()) << *this; + } } -UninitializedType::UninitializedType(mirror::Class* klass, std::string& descriptor, - uint32_t allocation_pc, uint16_t cache_id) - : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) { -} - -void UninitializedType::CheckInvariants() const { - CHECK_EQ(allocation_pc_, 0U) << *this; -} - -void UninitialisedThisReferenceType::CheckInvariants() const { - UninitializedType::CheckInvariants(); -} - -UninitialisedThisReferenceType::UninitialisedThisReferenceType(mirror::Class* klass, - std::string& descriptor, uint16_t cache_id) : UninitializedType(klass, descriptor, 0, cache_id) { +void UninitializedThisReferenceType::CheckInvariants() const { + CHECK_EQ(GetAllocationPc(), 0U) << *this; } -void UnresolvedUninitialisedThisRefType::CheckInvariants() const { - UninitializedType::CheckInvariants(); +void UnresolvedUninitializedThisRefType::CheckInvariants() const { + CHECK_EQ(GetAllocationPc(), 0U) << *this; CHECK(!descriptor_.empty()) << *this; CHECK(klass_ == NULL) << *this; } void UnresolvedUninitializedRefType::CheckInvariants() const { - UninitializedType::CheckInvariants(); CHECK(!descriptor_.empty()) << *this; CHECK(klass_ == NULL) << *this; } diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h index 7c4253604c..9ac0ecac8a 100644 --- a/src/verifier/reg_type.h +++ b/src/verifier/reg_type.h @@ -18,6 +18,7 @@ #define ART_SRC_VERIFIER_REG_TYPE_H_ #include "base/macros.h" +#include "globals.h" #include "primitive.h" #include "jni.h" @@ -39,105 +40,43 @@ class RegTypeCache; */ class RegType { public: - // The high half that corresponds to this low half - const RegType& HighHalf(RegTypeCache* cache) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - inline virtual bool IsUndefined() const { - return false; - } - inline virtual bool IsConflict() const { - return false; - } - inline virtual bool IsBoolean() const { - return false; - } - inline virtual bool IsByte() const { - return false; - } - inline virtual bool IsChar() const { - return false; - } - inline virtual bool IsShort() const { - return false; - } - inline virtual bool IsInteger() const { - return false; - } - inline virtual bool IsLongLo() const { - return false; - } - inline virtual bool IsLongHi() const { - return false; - } - inline virtual bool IsFloat() const { - return false; - } - inline virtual bool IsDouble() const { - return false; - } - inline virtual bool IsDoubleLo() const { - return false; - } - inline virtual bool IsDoubleHi() const { - return false; - } - inline virtual bool IsUnresolvedReference() const { - return false; - } - inline virtual bool IsUninitializedReference() const { - return false; - } - inline virtual bool IsUninitializedThisReference() const { - return false; - } - inline virtual bool IsUnresolvedAndUninitializedReference() const { - return false; - } - inline virtual bool IsUnresolvedAndUninitializedThisReference() const { - return false; - } - inline virtual bool IsUnresolvedMergedReference() const { - return false; - } - inline virtual bool IsUnresolvedSuperClass() const { - return false; - } - inline virtual bool IsReference() const { - return false; - } - inline virtual bool IsPreciseReference() const { - return false; - } - inline virtual bool IsPreciseConstant() const { - return false; - } - inline virtual bool IsPreciseConstantLo() const { - return false; - } - inline virtual bool IsPreciseConstantHi() const { - return false; - } - inline virtual bool IsImpreciseConstantLo() const { - return false; - } - inline virtual bool IsImpreciseConstantHi() const { - return false; - } - virtual bool IsImpreciseConstant() const { - return false; - } - - inline virtual bool IsConstantTypes() const { - return false; - } + virtual bool IsUndefined() const { return false; } + virtual bool IsConflict() const { return false; } + virtual bool IsBoolean() const { return false; } + virtual bool IsByte() const { return false; } + virtual bool IsChar() const { return false; } + virtual bool IsShort() const { return false; } + virtual bool IsInteger() const { return false; } + virtual bool IsLongLo() const { return false; } + virtual bool IsLongHi() const { return false; } + virtual bool IsFloat() const { return false; } + virtual bool IsDouble() const { return false; } + virtual bool IsDoubleLo() const { return false; } + virtual bool IsDoubleHi() const { return false; } + virtual bool IsUnresolvedReference() const { return false; } + virtual bool IsUninitializedReference() const { return false; } + virtual bool IsUninitializedThisReference() const { return false; } + virtual bool IsUnresolvedAndUninitializedReference() const { return false; } + virtual bool IsUnresolvedAndUninitializedThisReference() const { return false; } + virtual bool IsUnresolvedMergedReference() const { return false; } + virtual bool IsUnresolvedSuperClass() const { return false; } + virtual bool IsReference() const { return false; } + virtual bool IsPreciseReference() const { return false; } + virtual bool IsPreciseConstant() const { return false; } + virtual bool IsPreciseConstantLo() const { return false; } + virtual bool IsPreciseConstantHi() const { return false; } + virtual bool IsImpreciseConstantLo() const { return false; } + virtual bool IsImpreciseConstantHi() const { return false; } + virtual bool IsImpreciseConstant() const { return false; } + virtual bool IsConstantTypes() const { return false; } bool IsConstant() const { - return (IsPreciseConstant() || IsImpreciseConstant()); + return IsPreciseConstant() || IsImpreciseConstant(); } bool IsConstantLo() const { - return (IsPreciseConstantLo() || IsImpreciseConstantLo()); + return IsPreciseConstantLo() || IsImpreciseConstantLo(); } bool IsPrecise() const { - return (IsPreciseConstantLo() || IsPreciseConstant() || - IsPreciseConstantHi()); + return IsPreciseConstantLo() || IsPreciseConstant() || IsPreciseConstantHi(); } bool IsLongConstant() const { return IsConstantLo(); @@ -148,11 +87,7 @@ class RegType { bool IsLongConstantHigh() const { return IsConstantHi(); } - bool IsUninitializedTypes() const { - return IsUninitializedReference() || IsUninitializedThisReference() || - IsUnresolvedAndUninitializedReference() || - IsUnresolvedAndUninitializedThisReference(); - } + virtual bool IsUninitializedTypes() const { return false; } bool IsUnresolvedTypes() const { return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference() || @@ -170,7 +105,7 @@ class RegType { bool IsLongOrDoubleTypes() const { return IsLowHalf(); } - // Check this is the low half, and that type_h is its matching high-half + // Check this is the low half, and that type_h is its matching high-half. inline bool CheckWidePair(const RegType& type_h) const { if (IsLowHalf()) { return ((IsPreciseConstantLo() && type_h.IsPreciseConstantHi()) || @@ -182,37 +117,36 @@ class RegType { } return false; } + // The high half that corresponds to this low half + const RegType& HighHalf(RegTypeCache* cache) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsConstantBoolean() const { return IsConstant() && (ConstantValue() >= 0) && (ConstantValue() <= 1); } - inline virtual bool IsConstantChar() const { + virtual bool IsConstantChar() const { return false; } - inline virtual bool IsConstantByte() const { + virtual bool IsConstantByte() const { return false; } - inline virtual bool IsConstantShort() const { + virtual bool IsConstantShort() const { return false; } - inline virtual bool IsOne() const { + virtual bool IsOne() const { return false; } - inline virtual bool IsZero() const { + virtual bool IsZero() const { return false; } bool IsReferenceTypes() const { return IsNonZeroReferenceTypes() || IsZero(); } - bool IsNonZeroReferenceTypes() const { - return IsReference() || IsPreciseReference() || - IsUninitializedReference() || IsUninitializedThisReference() || - IsUnresolvedReference() || IsUnresolvedAndUninitializedReference() || - IsUnresolvedAndUninitializedThisReference() || - IsUnresolvedMergedReference() || IsUnresolvedSuperClass(); + virtual bool IsNonZeroReferenceTypes() const { + return false; } bool IsCategory1Types() const { - return (IsChar() || IsInteger() || IsFloat() || IsConstant() || IsByte() || - IsShort() || IsBoolean() ); + return IsChar() || IsInteger() || IsFloat() || IsConstant() || IsByte() || IsShort() || + IsBoolean(); } bool IsCategory2Types() const { return IsLowHalf(); // Don't expect explicit testing of high halves @@ -230,20 +164,12 @@ class RegType { return IsChar() || IsBooleanTypes() || IsConstantChar(); } bool IsIntegralTypes() const { - return (IsInteger() || IsConstant() || IsByte() || IsShort() || IsChar() || IsBoolean() ); - } - inline virtual int32_t ConstantValue() const { - DCHECK(IsConstant()); - return -1; - } - inline virtual int32_t ConstantValueLo() const { - DCHECK(IsConstantLo()); - return -1; - } - inline virtual int32_t ConstantValueHi() const { - DCHECK(IsConstantHi()); - return -1; + return IsInteger() || IsConstant() || IsByte() || IsShort() || IsChar() || IsBoolean(); } + // Give the constant value encoded, but this shouldn't be called in the general case. + virtual int32_t ConstantValue() const; + virtual int32_t ConstantValueLo() const; + virtual int32_t ConstantValueHi() const; bool IsArrayIndexTypes() const { return IsIntegralTypes(); } @@ -265,12 +191,11 @@ class RegType { bool IsDoubleHighTypes() const { return (IsDoubleHi() || IsPreciseConstantHi() || IsImpreciseConstantHi()); } - inline virtual bool IsLong() const { + virtual bool IsLong() const { return false; } - bool HasClass() const { - return IsReference() || IsPreciseReference() || IsUninitializedReference() || - IsUninitializedThisReference(); + virtual bool HasClass() const { + return false; } bool IsJavaLangObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -279,210 +204,286 @@ class RegType { bool IsJavaLangObjectArray() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsInstantiableTypes() const; const std::string& GetDescriptor() const { - DCHECK(IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()); + DCHECK(HasClass() || (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && + !IsUnresolvedSuperClass())); return descriptor_; } + mirror::Class* GetClass() const { + DCHECK(!IsUnresolvedReference()); + DCHECK(klass_ != NULL); + DCHECK(HasClass()); + return klass_; + } uint16_t GetId() const { return cache_id_; } const RegType& GetSuperClass(RegTypeCache* cache) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + // Can this type access other? bool CanAccess(const RegType& other) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Can this type access a member with the given properties? bool CanAccessMember(mirror::Class* klass, uint32_t access_flags) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Can this type be assigned by src? - bool IsAssignableFrom(const RegType& src) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Note: Object and interface types may always be assigned to one another, see comment on + // ClassJoin. + bool IsAssignableFrom(const RegType& src) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Can this type be assigned by src? Variant of IsAssignableFrom that doesn't allow assignment to + // an interface from an Object. + bool IsStrictlyAssignableFrom(const RegType& src) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Are these RegTypes the same? bool Equals(const RegType& other) const { return GetId() == other.GetId(); } + // Compute the merge of this register from one edge (path) with incoming_type from another. virtual const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Class* GetClass() const { - DCHECK(!IsUnresolvedReference()); - DCHECK(klass_ != NULL); - DCHECK(HasClass()); - return klass_; - } - /* - * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is - * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of - * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J - * is the deepest (lowest upper bound) parent of S and T). - * - * This operation applies for regular classes and arrays, however, for interface types there needn't - * be a partial ordering on the types. We could solve the problem of a lack of a partial order by - * introducing sets of types, however, the only operation permissible on an interface is - * invoke-interface. In the tradition of Java verifiers [1] we defer the verification of interface - * types until an invoke-interface call on the interface typed reference at runtime and allow - * the perversion of Object being assignable to an interface type (note, however, that we don't - * allow assignment of Object or Interface to any concrete class and are therefore type safe). - * - * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy - */ + /* + * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is + * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of + * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J + * is the deepest (lowest upper bound) parent of S and T). + * + * This operation applies for regular classes and arrays, however, for interface types there + * needn't be a partial ordering on the types. We could solve the problem of a lack of a partial + * order by introducing sets of types, however, the only operation permissible on an interface is + * invoke-interface. In the tradition of Java verifiers [1] we defer the verification of interface + * types until an invoke-interface call on the interface typed reference at runtime and allow + * the perversion of Object being assignable to an interface type (note, however, that we don't + * allow assignment of Object or Interface to any concrete class and are therefore type safe). + * + * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy + */ static mirror::Class* ClassJoin(mirror::Class* s, mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - RegType(mirror::Class* klass, std::string descriptor, uint16_t cache_id) + + virtual ~RegType() {} + + protected: + RegType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : descriptor_(descriptor), klass_(klass), cache_id_(cache_id) { + if (kIsDebugBuild) { + CheckInvariants(); + } } - inline virtual ~RegType() { - } - virtual void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - friend class RegTypeCache; - protected: + void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + const std::string descriptor_; - mirror::Class* klass_; + mirror::Class* const klass_; const uint16_t cache_id_; + friend class RegTypeCache; + DISALLOW_COPY_AND_ASSIGN(RegType); }; +// Bottom type. class ConflictType : public RegType { public: bool IsConflict() const { return true; } + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static ConflictType* CreateInstance(mirror::Class* klass, std::string& descriptor, - uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Get the singleton Conflict instance. static ConflictType* GetInstance(); + + // Create the singleton instance. + static ConflictType* CreateInstance(mirror::Class* klass, const std::string& descriptor, + uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Destroy the singleton instance. static void Destroy(); private: - ConflictType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ConflictType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : RegType(klass, descriptor, cache_id) { + } + static ConflictType* instance_; }; +// A variant of the bottom type used to specify an undefined value in the incoming registers. +// Merging with UndefinedType yields ConflictType which is the true bottom. class UndefinedType : public RegType { public: bool IsUndefined() const { return true; } + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static UndefinedType* CreateInstance(mirror::Class* klass, std::string& descriptor, + + // Get the singleton Undefined instance. + static UndefinedType* GetInstance(); + + // Create the singleton instance. + static UndefinedType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static UndefinedType* GetInstance(); + + // Destroy the singleton instance. static void Destroy(); private: - UndefinedType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + UndefinedType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { } + virtual const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static UndefinedType* instance_; }; -class IntegerType : public RegType { +class PrimitiveType : public RegType { + public: + PrimitiveType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +}; + +class Cat1Type : public PrimitiveType { + public: + Cat1Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +}; + +class IntegerType : public Cat1Type { public: bool IsInteger() const { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static IntegerType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static IntegerType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static IntegerType* GetInstance(); static void Destroy(); private: - IntegerType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + IntegerType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat1Type(klass, descriptor, cache_id) { + } static IntegerType* instance_; }; -class BooleanType : public RegType { +class BooleanType : public Cat1Type { public: bool IsBoolean() const { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static BooleanType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static BooleanType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static BooleanType* GetInstance(); static void Destroy(); private: - BooleanType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + BooleanType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat1Type(klass, descriptor, cache_id) { + } + static BooleanType* instance; }; -class ByteType : public RegType { +class ByteType : public Cat1Type { public: bool IsByte() const { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static ByteType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static ByteType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static ByteType* GetInstance(); static void Destroy(); private: - ByteType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ByteType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat1Type(klass, descriptor, cache_id) { + } static ByteType* instance_; }; -class ShortType : public RegType { +class ShortType : public Cat1Type { public: bool IsShort() const { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static ShortType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static ShortType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static ShortType* GetInstance(); static void Destroy(); private: - ShortType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ShortType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat1Type(klass, descriptor, cache_id) { + } static ShortType* instance_; }; -class CharType : public RegType { +class CharType : public Cat1Type { public: bool IsChar() const { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static CharType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static CharType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static CharType* GetInstance(); static void Destroy(); private: - CharType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + CharType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat1Type(klass, descriptor, cache_id) { + } static CharType* instance_; }; -class FloatType : public RegType { +class FloatType : public Cat1Type { public: bool IsFloat() const { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static FloatType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static FloatType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static FloatType* GetInstance(); static void Destroy(); private: - FloatType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + FloatType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat1Type(klass, descriptor, cache_id) { + } static FloatType* instance_; }; -class LongLoType : public RegType { +class Cat2Type : public PrimitiveType { + public: + Cat2Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +}; + +class LongLoType : public Cat2Type { public: std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsLongLo() const { @@ -491,35 +492,39 @@ class LongLoType : public RegType { bool IsLong() const { return true; } - static LongLoType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static LongLoType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static LongLoType* GetInstance(); static void Destroy(); private: - LongLoType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + LongLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat2Type(klass, descriptor, cache_id) { + } static LongLoType* instance_; }; -class LongHiType : public RegType { +class LongHiType : public Cat2Type { public: std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsLongHi() const { return true; } - static LongHiType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static LongHiType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static LongHiType* GetInstance(); static void Destroy(); private: - LongHiType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + LongHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat2Type(klass, descriptor, cache_id) { + } static LongHiType* instance_; }; -class DoubleLoType : public RegType { +class DoubleLoType : public Cat2Type { public: std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsDoubleLo() const { @@ -528,31 +533,35 @@ class DoubleLoType : public RegType { bool IsDouble() const { return true; } - static DoubleLoType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static DoubleLoType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static DoubleLoType* GetInstance(); static void Destroy(); private: - DoubleLoType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + DoubleLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat2Type(klass, descriptor, cache_id) { + } static DoubleLoType* instance_; }; -class DoubleHiType : public RegType { +class DoubleHiType : public Cat2Type { public: std::string Dump() const; virtual bool IsDoubleHi() const { return true; } - static DoubleHiType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static DoubleHiType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static DoubleHiType* GetInstance(); static void Destroy(); private: - DoubleHiType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + DoubleHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : Cat2Type(klass, descriptor, cache_id) { + } static DoubleHiType* instance_; }; @@ -560,9 +569,6 @@ class ConstantType : public RegType { public: ConstantType(uint32_t constat, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - inline virtual ~ConstantType() { - } - const uint32_t constant_; // If this is a 32-bit constant, what is the value? This value may be imprecise in which case // the value represents part of the integer range of values that may be held in the register. virtual int32_t ConstantValue() const; @@ -590,7 +596,10 @@ class ConstantType : public RegType { ConstantValue() >= std::numeric_limits<jshort>::min() && ConstantValue() <= std::numeric_limits<jshort>::max(); } - inline virtual bool IsConstantTypes() const { return true; } + virtual bool IsConstantTypes() const { return true; } + + private: + const uint32_t constant_; }; class PreciseConstType : public ConstantType { @@ -662,147 +671,254 @@ class ImpreciseConstHiType : public ConstantType { std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; +// Common parent of all uninitialized types. Uninitialized types are created by "new" dex +// instructions and must be passed to a constructor. class UninitializedType : public RegType { public: - UninitializedType(mirror::Class* klass, std::string& descriptor, uint32_t allocation_pc, - uint16_t cache_id); - inline virtual ~UninitializedType() { + UninitializedType(mirror::Class* klass, const std::string& descriptor, uint32_t allocation_pc, + uint16_t cache_id) + : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) { } + bool IsUninitializedTypes() const; + bool IsNonZeroReferenceTypes() const; + uint32_t GetAllocationPc() const { DCHECK(IsUninitializedTypes()); return allocation_pc_; } - virtual void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: const uint32_t allocation_pc_; }; -class UninitialisedReferenceType : public UninitializedType { +// Similar to ReferenceType but not yet having been passed to a constructor. +class UninitializedReferenceType : public UninitializedType { public: - UninitialisedReferenceType(mirror::Class* klass, std::string& descriptor, uint32_t allocation_pc, - uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + UninitializedReferenceType(mirror::Class* klass, const std::string& descriptor, + uint32_t allocation_pc, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : UninitializedType(klass, descriptor, allocation_pc, cache_id) { + } bool IsUninitializedReference() const { return true; } + + bool HasClass() const { + return true; + } + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; +// Similar to UnresolvedReferenceType but not yet having been passed to a constructor. class UnresolvedUninitializedRefType : public UninitializedType { public: - UnresolvedUninitializedRefType(std::string& descriptor, uint32_t allocation_pc, - uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + UnresolvedUninitializedRefType(const std::string& descriptor, uint32_t allocation_pc, + uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : UninitializedType(NULL, descriptor, allocation_pc, cache_id) { + if (kIsDebugBuild) { + CheckInvariants(); + } + } + bool IsUnresolvedAndUninitializedReference() const { return true; } + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: + void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; -class UninitialisedThisReferenceType : public UninitializedType { +// Similar to UninitializedReferenceType but special case for the this argument of a constructor. +class UninitializedThisReferenceType : public UninitializedType { public: - UninitialisedThisReferenceType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - inline virtual bool IsUninitializedThisReference() const { + UninitializedThisReferenceType(mirror::Class* klass, const std::string& descriptor, + uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : UninitializedType(klass, descriptor, 0, cache_id) { + if (kIsDebugBuild) { + CheckInvariants(); + } + } + + virtual bool IsUninitializedThisReference() const { return true; } + + bool HasClass() const { + return true; + } + + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; -class UnresolvedUninitialisedThisRefType : public UninitializedType { +class UnresolvedUninitializedThisRefType : public UninitializedType { public: - UnresolvedUninitialisedThisRefType(std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + UnresolvedUninitializedThisRefType(const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : UninitializedType(NULL, descriptor, 0, cache_id) { + if (kIsDebugBuild) { + CheckInvariants(); + } + } + bool IsUnresolvedAndUninitializedThisReference() const { return true; } + + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: + void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; +// A type of register holding a reference to an Object of type GetClass or a sub-class. class ReferenceType : public RegType { public: - ReferenceType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : RegType(klass, descriptor, cache_id) { + } + bool IsReference() const { return true; } + + bool IsNonZeroReferenceTypes() const { + return true; + } + + bool HasClass() const { + return true; + } + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; +// A type of register holding a reference to an Object of type GetClass and only an object of that +// type. class PreciseReferenceType : public RegType { public: - PreciseReferenceType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + PreciseReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsPreciseReference() const { return true; } + + bool IsNonZeroReferenceTypes() const { + return true; + } + + bool HasClass() const { + return true; + } + + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; -class UnresolvedReferenceType : public RegType { +// Common parent of unresolved types. +class UnresolvedType : public RegType { public: - UnresolvedReferenceType(std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : RegType(NULL, descriptor, cache_id) { + UnresolvedType(const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : RegType(NULL, descriptor, cache_id) { } - std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool IsNonZeroReferenceTypes() const; +}; + +// Similar to ReferenceType except the Class couldn't be loaded. Assignability and other tests made +// of this type must be conservative. +class UnresolvedReferenceType : public UnresolvedType { + public: + UnresolvedReferenceType(const std::string& descriptor, uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : UnresolvedType(descriptor, cache_id) { + if (kIsDebugBuild) { + CheckInvariants(); + } + } + bool IsUnresolvedReference() const { return true; } + + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: + void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; -class UnresolvedSuperClass : public RegType { +// Type representing the super-class of an unresolved type. +class UnresolvedSuperClass : public UnresolvedType { public: UnresolvedSuperClass(uint16_t child_id, RegTypeCache* reg_type_cache, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : RegType(NULL, "", cache_id), unresolved_child_id_(child_id), + : UnresolvedType("", cache_id), unresolved_child_id_(child_id), reg_type_cache_(reg_type_cache) { + if (kIsDebugBuild) { + CheckInvariants(); + } } + bool IsUnresolvedSuperClass() const { return true; } + uint16_t GetUnresolvedSuperClassChildId() const { DCHECK(IsUnresolvedSuperClass()); return static_cast<uint16_t>(unresolved_child_id_ & 0xFFFF); } - void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: + void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const uint16_t unresolved_child_id_; const RegTypeCache* const reg_type_cache_; }; -class UnresolvedMergedType : public RegType { +// A merge of two unresolved types. If the types were resolved this may be Conflict or another +// known ReferenceType. +class UnresolvedMergedType : public UnresolvedType { public: - UnresolvedMergedType(uint16_t left_id, uint16_t right_id, const RegTypeCache* reg_type_cache, uint16_t cache_id) + UnresolvedMergedType(uint16_t left_id, uint16_t right_id, const RegTypeCache* reg_type_cache, + uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : RegType(NULL, "", cache_id), reg_type_cache_(reg_type_cache) ,merged_types_(left_id, right_id) { + : UnresolvedType("", cache_id), reg_type_cache_(reg_type_cache) ,merged_types_(left_id, right_id) { + if (kIsDebugBuild) { + CheckInvariants(); + } } + // The top of a tree of merged types. std::pair<uint16_t, uint16_t> GetTopMergedTypes() const { DCHECK(IsUnresolvedMergedReference()); return merged_types_; } + // The complete set of merged types. std::set<uint16_t> GetMergedTypes() const; + bool IsUnresolvedMergedReference() const { return true; } - void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: + void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const RegTypeCache* const reg_type_cache_; const std::pair<uint16_t, uint16_t> merged_types_; }; std::ostream& operator<<(std::ostream& os, const RegType& rhs) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + } // namespace verifier } // namespace art diff --git a/src/verifier/reg_type_cache-inl.h b/src/verifier/reg_type_cache-inl.h index f6b0056536..42474d1849 100644 --- a/src/verifier/reg_type_cache-inl.h +++ b/src/verifier/reg_type_cache-inl.h @@ -24,7 +24,7 @@ namespace art { namespace verifier { template <class Type> -Type* RegTypeCache::CreatePrimitiveTypeInstance(std::string descriptor) { +Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) { mirror::Class* klass = NULL; // Try loading the class from linker. if (!descriptor.empty()) { @@ -35,6 +35,12 @@ Type* RegTypeCache::CreatePrimitiveTypeInstance(std::string descriptor) { return entry; } +inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const { + DCHECK_LT(id, entries_.size()); + RegType* result = entries_[id]; + DCHECK(result != NULL); + return *result; +} } // namespace verifier } // namespace art #endif // ART_SRC_VERIFIER_REG_TYPE_CACHE_INL_H_ diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc index e914d1e679..6013250835 100644 --- a/src/verifier/reg_type_cache.cc +++ b/src/verifier/reg_type_cache.cc @@ -24,13 +24,24 @@ namespace art { namespace verifier { + bool RegTypeCache::primitive_initialized_ = false; uint16_t RegTypeCache::primitive_start_ = 0; uint16_t RegTypeCache::primitive_count_ = 0; static bool MatchingPrecisionForClass(RegType* entry, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return (entry->IsPreciseReference() == precise) || (entry->GetClass()->IsFinal() && !precise); + if (entry->IsPreciseReference() == precise) { + // We were or weren't looking for a precise reference and we found what we need. + return true; + } else { + if (!precise && entry->GetClass()->CannotBeAssignedFromOtherTypes()) { + // We weren't looking for a precise reference, as we're looking up based on a descriptor, but + // we found a matching entry based on the descriptor. Return the precise entry in that case. + return true; + } + return false; + } } void RegTypeCache::FillPrimitiveTypes() { @@ -49,9 +60,10 @@ void RegTypeCache::FillPrimitiveTypes() { DCHECK_EQ(entries_.size(), primitive_count_); } -const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise) { - CHECK(RegTypeCache::primitive_initialized_); - if (std::string(descriptor).length() == 1) { +const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, + bool precise) { + DCHECK(RegTypeCache::primitive_initialized_); + if (descriptor[1] == '\0') { switch (descriptor[0]) { case 'Z': return Boolean(); @@ -80,15 +92,7 @@ const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const c } }; -const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const { - DCHECK_LT(id, entries_.size()); - RegType* result = entries_[id]; - DCHECK(result != NULL); - return *result; -} - -const RegType& RegTypeCache::RegTypeFromPrimitiveType( - Primitive::Type prim_type) const { +const RegType& RegTypeCache::RegTypeFromPrimitiveType(Primitive::Type prim_type) const { CHECK(RegTypeCache::primitive_initialized_); switch (prim_type) { case Primitive::kPrimBoolean: @@ -113,41 +117,29 @@ const RegType& RegTypeCache::RegTypeFromPrimitiveType( } } -bool RegTypeCache::MatchDescriptor(size_t idx, std::string& descriptor, bool precise) { - RegType* cur_entry = entries_[idx]; - if (cur_entry->HasClass()) { - // Check the descriptor in the reg_type if available. - if(!cur_entry->descriptor_.empty()) { - if (descriptor == cur_entry->descriptor_ && MatchingPrecisionForClass(cur_entry, precise)) { - return true; - } - } else { - // Descriptor not found in reg_type , maybe available in Class object. - // So we might have cases where we have the class but not the descriptor - // for that class we need the class helper to get the descriptor - // and match it with the one we are given. - ClassHelper kh(cur_entry->GetClass()); - if ((strcmp(descriptor.c_str(), kh.GetDescriptor()) == 0) && - MatchingPrecisionForClass(cur_entry, precise)) { - return true; - } - } - } else if (cur_entry->IsUnresolvedReference() && cur_entry->GetDescriptor() == descriptor) { - return true; +bool RegTypeCache::MatchDescriptor(size_t idx, const char* descriptor, bool precise) { + RegType* entry = entries_[idx]; + if (entry->descriptor_ != descriptor) { + return false; + } + if (entry->HasClass()) { + return MatchingPrecisionForClass(entry, precise); } - return false; + // There is no notion of precise unresolved references, the precise information is just dropped + // on the floor. + DCHECK(entry->IsUnresolvedReference()); + return true; } - -mirror::Class* RegTypeCache::ResolveClass(std::string descriptor, mirror::ClassLoader* loader) { +mirror::Class* RegTypeCache::ResolveClass(const char* descriptor, mirror::ClassLoader* loader) { // Class was not found, must create new type. // Try resolving class ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::Class* klass = NULL; if (can_load_classes_) { - klass = class_linker->FindClass(descriptor.c_str(), loader); + klass = class_linker->FindClass(descriptor, loader); } else { - klass = class_linker->LookupClass(descriptor.c_str(), loader); + klass = class_linker->LookupClass(descriptor, loader); if (klass != NULL && !klass->IsLoaded()) { // We found the class but without it being loaded its not safe for use. klass = NULL; @@ -155,6 +147,7 @@ mirror::Class* RegTypeCache::ResolveClass(std::string descriptor, mirror::ClassL } return klass; } + void RegTypeCache::ClearException() { if (can_load_classes_) { DCHECK(Thread::Current()->IsExceptionPending()); @@ -163,8 +156,9 @@ void RegTypeCache::ClearException() { DCHECK(!Thread::Current()->IsExceptionPending()); } } -const RegType& RegTypeCache::From(mirror::ClassLoader* loader, std::string descriptor, bool precise) { +const RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descriptor, + bool precise) { // Try looking up the class in the cache first. for (size_t i = primitive_count_; i < entries_.size(); i++) { if (MatchDescriptor(i, descriptor, precise)) { @@ -185,7 +179,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, std::string descr // 2- Precise Flag passed as true. RegType* entry; // Create an imprecise type if we can't tell for a fact that it is precise. - if ((klass->IsFinal()) || precise) { + if (klass->CannotBeAssignedFromOtherTypes() || precise) { DCHECK(!(klass->IsAbstract()) || klass->IsArrayClass()); DCHECK(!klass->IsInterface()); entry = new PreciseReferenceType(klass, descriptor, entries_.size()); @@ -198,7 +192,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, std::string descr // We tried loading the class and failed, this might get an exception raised // so we want to clear it before we go on. ClearException(); - if (IsValidDescriptor(descriptor.c_str())) { + if (IsValidDescriptor(descriptor)) { RegType* entry = new UnresolvedReferenceType(descriptor, entries_.size()); entries_.push_back(entry); return *entry; @@ -209,25 +203,26 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, std::string descr } } } -const RegType& RegTypeCache::FromClass(mirror::Class* klass, bool precise) { + +const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) { if (klass->IsPrimitive()) { + // Note: precise isn't used for primitive classes. A char is assignable to an int. All + // primitive classes are final. return RegTypeFromPrimitiveType(klass->GetPrimitiveType()); } else { // Look for the reference in the list of entries to have. for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; - if ((cur_entry->HasClass()) && cur_entry->GetClass() == klass && - MatchingPrecisionForClass(cur_entry, precise)) { + if (cur_entry->klass_ == klass && MatchingPrecisionForClass(cur_entry, precise)) { return *cur_entry; } } // No reference to the class was found, create new reference. RegType* entry; - std::string empty = ""; if (precise) { - entry = new PreciseReferenceType(klass, empty, entries_.size()); + entry = new PreciseReferenceType(klass, descriptor, entries_.size()); } else { - entry = new ReferenceType(klass, empty, entries_.size()); + entry = new ReferenceType(klass, descriptor, entries_.size()); } entries_.push_back(entry); return *entry; @@ -309,13 +304,14 @@ const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegT // Create entry. RegType* entry = new UnresolvedMergedType(left.GetId(), right.GetId(), this, entries_.size()); entries_.push_back(entry); -#ifndef NDEBUG - UnresolvedMergedType* tmp_entry = down_cast<UnresolvedMergedType*>(entry); - std::set<uint16_t> check_types = tmp_entry->GetMergedTypes(); - CHECK(check_types == types); -#endif + if (kIsDebugBuild) { + UnresolvedMergedType* tmp_entry = down_cast<UnresolvedMergedType*>(entry); + std::set<uint16_t> check_types = tmp_entry->GetMergedTypes(); + CHECK(check_types == types); + } return *entry; } + const RegType& RegTypeCache::FromUnresolvedSuperClass(const RegType& child) { // Check if entry already exists. for (size_t i = primitive_count_; i < entries_.size(); i++) { @@ -334,11 +330,12 @@ const RegType& RegTypeCache::FromUnresolvedSuperClass(const RegType& child) { entries_.push_back(entry); return *entry; } + const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) { RegType* entry = NULL; RegType* cur_entry = NULL; + const std::string& descriptor(type.GetDescriptor()); if (type.IsUnresolvedTypes()) { - std::string descriptor(type.GetDescriptor()); for (size_t i = primitive_count_; i < entries_.size(); i++) { cur_entry = entries_[i]; if (cur_entry->IsUnresolvedAndUninitializedReference() && @@ -353,23 +350,23 @@ const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocat for (size_t i = primitive_count_; i < entries_.size(); i++) { cur_entry = entries_[i]; if (cur_entry->IsUninitializedReference() && - down_cast<UninitialisedReferenceType*>(cur_entry) + down_cast<UninitializedReferenceType*>(cur_entry) ->GetAllocationPc() == allocation_pc && cur_entry->GetClass() == klass) { return *cur_entry; } } - std::string descriptor(""); - entry = new UninitialisedReferenceType(klass, descriptor, allocation_pc, entries_.size()); + entry = new UninitializedReferenceType(klass, descriptor, allocation_pc, entries_.size()); } entries_.push_back(entry); return *entry; } + const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { RegType* entry; if (uninit_type.IsUnresolvedTypes()) { - std::string descriptor(uninit_type.GetDescriptor()); + const std::string& descriptor(uninit_type.GetDescriptor()); for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->IsUnresolvedReference() && @@ -377,63 +374,52 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { return *cur_entry; } } - entry = new UnresolvedReferenceType(descriptor, entries_.size()); + entry = new UnresolvedReferenceType(descriptor.c_str(), entries_.size()); } else { mirror::Class* klass = uninit_type.GetClass(); if(uninit_type.IsUninitializedThisReference() && !klass->IsFinal()) { - // For uninitialized this reference look for reference types that are not precise. + // For uninitialized "this reference" look for reference types that are not precise. for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->IsReference() && cur_entry->GetClass() == klass) { return *cur_entry; } } - std::string descriptor(""); - entry = new ReferenceType(klass, descriptor, entries_.size()); - } else { - std::string descriptor; - if (klass->IsFinal()) { - if (klass->IsInstantiable()) { - for (size_t i = primitive_count_; i < entries_.size(); i++) { - RegType* cur_entry = entries_[i]; - if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) { - return *cur_entry; - } - } - // Precise type was not found , create one ! - entry = new PreciseReferenceType(klass, descriptor, entries_.size()); - } else { - return Conflict(); - } - } else { - // Not a final class, create an imprecise reference. Look up if we have it in the cache first. - for (size_t i = primitive_count_; i < entries_.size(); i++) { - RegType* cur_entry = entries_[i]; - if (cur_entry->IsReference() && !(cur_entry->IsPrecise()) && - cur_entry->GetClass() == klass) { - return *cur_entry; - } + entry = new ReferenceType(klass, "", entries_.size()); + } else if (klass->IsInstantiable()) { + // We're uninitialized because of allocation, look or create a precise type as allocations + // may only create objects of that type. + for (size_t i = primitive_count_; i < entries_.size(); i++) { + RegType* cur_entry = entries_[i]; + if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) { + return *cur_entry; } - entry = new ReferenceType(klass, descriptor, entries_.size()); } + entry = new PreciseReferenceType(klass, uninit_type.GetDescriptor(), entries_.size()); + } else { + return Conflict(); } } entries_.push_back(entry); return *entry; } -const RegType& RegTypeCache::ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + +const RegType& RegTypeCache::ByteConstant() { return FromCat1Const(std::numeric_limits<jbyte>::min(), false); } -const RegType& RegTypeCache::ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + +const RegType& RegTypeCache::ShortConstant() { return FromCat1Const(std::numeric_limits<jshort>::min(), false); } -const RegType& RegTypeCache::IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + +const RegType& RegTypeCache::IntConstant() { return FromCat1Const(std::numeric_limits<jint>::max(), false); } + const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { RegType* entry; + const std::string& descriptor(type.GetDescriptor()); if (type.IsUnresolvedTypes()) { - std::string descriptor(type.GetDescriptor()); for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->IsUnresolvedAndUninitializedThisReference() && @@ -441,26 +427,26 @@ const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { return *cur_entry; } } - entry = new UnresolvedUninitialisedThisRefType(descriptor, entries_.size()); + entry = new UnresolvedUninitializedThisRefType(descriptor, entries_.size()); } else { mirror::Class* klass = type.GetClass(); for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; - if (cur_entry->IsUninitializedThisReference() && - cur_entry->GetClass() == klass) { + if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) { return *cur_entry; } } - std::string descriptor(""); - entry = new UninitialisedThisReferenceType(klass, descriptor, entries_.size()); + entry = new UninitializedThisReferenceType(klass, descriptor, entries_.size()); } entries_.push_back(entry); return *entry; } + const RegType& RegTypeCache::FromCat1Const(int32_t value, bool precise) { for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; - if (cur_entry->IsConstant() && cur_entry->IsPreciseConstant() == precise && + if (cur_entry->klass_ == NULL && cur_entry->IsConstant() && + cur_entry->IsPreciseConstant() == precise && (down_cast<ConstantType*>(cur_entry))->ConstantValue() == value) { return *cur_entry; } @@ -514,12 +500,13 @@ const RegType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::ClassLoader* loader) { CHECK(array.IsArrayTypes()); if (array.IsUnresolvedTypes()) { - std::string descriptor(array.GetDescriptor()); - std::string component(descriptor.substr(1, descriptor.size() - 1)); + const std::string& descriptor(array.GetDescriptor()); + const std::string component(descriptor.substr(1, descriptor.size() - 1)); return FromDescriptor(loader, component.c_str(), false); } else { mirror::Class* klass = array.GetClass()->GetComponentType(); - return FromClass(klass, klass->IsFinal()); + return FromClass(ClassHelper(klass).GetDescriptor(), klass, + klass->CannotBeAssignedFromOtherTypes()); } } diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h index 602c95086b..d70123c2de 100644 --- a/src/verifier/reg_type_cache.h +++ b/src/verifier/reg_type_cache.h @@ -39,6 +39,7 @@ const size_t kNumPrimitives = 12; class RegTypeCache { public: explicit RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) { + entries_.reserve(64); FillPrimitiveTypes(); } ~RegTypeCache(); @@ -52,13 +53,13 @@ class RegTypeCache { } static void ShutDown(); const art::verifier::RegType& GetFromId(uint16_t id) const; - const RegType& From(mirror::ClassLoader* loader, std::string descriptor, bool precise) + const RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template <class Type> - static Type* CreatePrimitiveTypeInstance(std::string descriptor) + static Type* CreatePrimitiveTypeInstance(const std::string& descriptor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void FillPrimitiveTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const RegType& FromClass(mirror::Class* klass, bool precise) + const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& FromCat1Const(int32_t value, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -152,10 +153,10 @@ class RegTypeCache { // Whether or not we're allowed to load classes. const bool can_load_classes_; DISALLOW_COPY_AND_ASSIGN(RegTypeCache); - mirror::Class* ResolveClass(std::string descriptor, mirror::ClassLoader* loader) + mirror::Class* ResolveClass(const char* descriptor, mirror::ClassLoader* loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ClearException(); - bool MatchDescriptor(size_t idx, std::string& descriptor, bool precise) + bool MatchDescriptor(size_t idx, const char* descriptor, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; diff --git a/src/verifier/reg_type_test.cc b/src/verifier/reg_type_test.cc index 9b46a7fbea..f37edff6ac 100644 --- a/src/verifier/reg_type_test.cc +++ b/src/verifier/reg_type_test.cc @@ -74,7 +74,6 @@ TEST_F(RegTypeTest, Pairs) { } TEST_F(RegTypeTest, Primitives) { - ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(true); @@ -108,6 +107,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_FALSE(bool_reg_type.IsLongTypes()); EXPECT_FALSE(bool_reg_type.IsDoubleTypes()); EXPECT_TRUE(bool_reg_type.IsArrayIndexTypes()); + EXPECT_FALSE(bool_reg_type.IsNonZeroReferenceTypes()); const RegType& byte_reg_type = cache.Byte(); EXPECT_FALSE(byte_reg_type.IsUndefined()); @@ -139,6 +139,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_FALSE(byte_reg_type.IsLongTypes()); EXPECT_FALSE(byte_reg_type.IsDoubleTypes()); EXPECT_TRUE(byte_reg_type.IsArrayIndexTypes()); + EXPECT_FALSE(byte_reg_type.IsNonZeroReferenceTypes()); const RegType& char_reg_type = cache.Char(); EXPECT_FALSE(char_reg_type.IsUndefined()); @@ -170,6 +171,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_FALSE(char_reg_type.IsLongTypes()); EXPECT_FALSE(char_reg_type.IsDoubleTypes()); EXPECT_TRUE(char_reg_type.IsArrayIndexTypes()); + EXPECT_FALSE(char_reg_type.IsNonZeroReferenceTypes()); const RegType& short_reg_type = cache.Short(); EXPECT_FALSE(short_reg_type.IsUndefined()); @@ -201,6 +203,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_FALSE(short_reg_type.IsLongTypes()); EXPECT_FALSE(short_reg_type.IsDoubleTypes()); EXPECT_TRUE(short_reg_type.IsArrayIndexTypes()); + EXPECT_FALSE(short_reg_type.IsNonZeroReferenceTypes()); const RegType& int_reg_type = cache.Integer(); EXPECT_FALSE(int_reg_type.IsUndefined()); @@ -232,6 +235,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_FALSE(int_reg_type.IsLongTypes()); EXPECT_FALSE(int_reg_type.IsDoubleTypes()); EXPECT_TRUE(int_reg_type.IsArrayIndexTypes()); + EXPECT_FALSE(int_reg_type.IsNonZeroReferenceTypes()); const RegType& long_reg_type = cache.LongLo(); EXPECT_FALSE(long_reg_type.IsUndefined()); @@ -263,6 +267,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_TRUE(long_reg_type.IsLongTypes()); EXPECT_FALSE(long_reg_type.IsDoubleTypes()); EXPECT_FALSE(long_reg_type.IsArrayIndexTypes()); + EXPECT_FALSE(long_reg_type.IsNonZeroReferenceTypes()); const RegType& float_reg_type = cache.Float(); EXPECT_FALSE(float_reg_type.IsUndefined()); @@ -294,6 +299,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_FALSE(float_reg_type.IsLongTypes()); EXPECT_FALSE(float_reg_type.IsDoubleTypes()); EXPECT_FALSE(float_reg_type.IsArrayIndexTypes()); + EXPECT_FALSE(float_reg_type.IsNonZeroReferenceTypes()); const RegType& double_reg_type = cache.DoubleLo(); EXPECT_FALSE(double_reg_type.IsUndefined()); @@ -325,6 +331,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_FALSE(double_reg_type.IsLongTypes()); EXPECT_TRUE(double_reg_type.IsDoubleTypes()); EXPECT_FALSE(double_reg_type.IsArrayIndexTypes()); + EXPECT_FALSE(double_reg_type.IsNonZeroReferenceTypes()); } @@ -352,12 +359,14 @@ TEST_F(RegTypeReferenceTest, UnresolvedType) { RegTypeCache cache(true); const RegType& ref_type_0 = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); + EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes()); const RegType& ref_type_1 = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.Equals(ref_type_1)); const RegType& unresolved_super_class = cache.FromUnresolvedSuperClass(ref_type_0); EXPECT_TRUE(unresolved_super_class.IsUnresolvedSuperClass()); + EXPECT_TRUE(unresolved_super_class.IsNonZeroReferenceTypes()); } TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { @@ -372,6 +381,7 @@ TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { const RegType& unresolved_unintialised = cache.Uninitialized(ref_type, 1101ull); EXPECT_TRUE(unresolved_unintialised.IsUnresolvedAndUninitializedReference()); EXPECT_TRUE(unresolved_unintialised.IsUninitializedTypes()); + EXPECT_TRUE(unresolved_unintialised.IsNonZeroReferenceTypes()); // Create an uninitialized type of this unresolved type with different PC const RegType& ref_type_unresolved_unintialised_1 = cache.Uninitialized(ref_type, 1102ull); EXPECT_TRUE(unresolved_unintialised.IsUnresolvedAndUninitializedReference()); diff --git a/src/verifier/register_line-inl.h b/src/verifier/register_line-inl.h new file mode 100644 index 0000000000..157e136cc1 --- /dev/null +++ b/src/verifier/register_line-inl.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_VERIFIER_REGISTER_LINE_INL_H_ +#define ART_SRC_VERIFIER_REGISTER_LINE_INL_H_ + +#include "register_line.h" +#include "method_verifier.h" + +namespace art { +namespace verifier { + +inline const RegType& RegisterLine::GetRegisterType(uint32_t vsrc) const { + // The register index was validated during the static pass, so we don't need to check it here. + DCHECK_LT(vsrc, num_regs_); + return verifier_->GetRegTypeCache()->GetFromId(line_[vsrc]); +} + +} // namespace verifier +} // namespace art + +#endif // ART_SRC_VERIFIER_REGISTER_LINE_INL_H_ diff --git a/src/verifier/register_line.cc b/src/verifier/register_line.cc index 544a9ee4c0..3a2145b9bb 100644 --- a/src/verifier/register_line.cc +++ b/src/verifier/register_line.cc @@ -16,7 +16,9 @@ #include "register_line.h" +#include "dex_instruction-inl.h" #include "method_verifier.h" +#include "register_line-inl.h" namespace art { namespace verifier { @@ -92,22 +94,18 @@ void RegisterLine::SetResultRegisterTypeWide(const RegType& new_type1, result_[1] = new_type2.GetId(); } -const RegType& RegisterLine::GetRegisterType(uint32_t vsrc) const { - // The register index was validated during the static pass, so we don't need to check it here. - DCHECK_LT(vsrc, num_regs_); - return verifier_->GetRegTypeCache()->GetFromId(line_[vsrc]); -} - -const RegType& RegisterLine::GetInvocationThis(const DecodedInstruction& dec_insn) { - if (dec_insn.vA < 1) { +const RegType& RegisterLine::GetInvocationThis(const Instruction* inst, bool is_range) { + const size_t args_count = is_range ? inst->VRegA_3rc() : inst->VRegA_35c(); + if (args_count < 1) { verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke lacks 'this'"; return verifier_->GetRegTypeCache()->Conflict(); } /* get the element type of the array held in vsrc */ - const RegType& this_type = GetRegisterType(dec_insn.vC); + const uint32_t this_reg = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); + const RegType& this_type = GetRegisterType(this_reg); if (!this_type.IsReferenceTypes()) { verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "tried to get class from non-reference register v" - << dec_insn.vC << " (type=" << this_type << ")"; + << this_reg << " (type=" << this_type << ")"; return verifier_->GetRegTypeCache()->Conflict(); } return this_type; @@ -260,125 +258,135 @@ void RegisterLine::CopyResultRegister2(uint32_t vdst) { } } -void RegisterLine::CheckUnaryOp(const DecodedInstruction& dec_insn, +void RegisterLine::CheckUnaryOp(const Instruction* inst, const RegType& dst_type, const RegType& src_type) { - if (VerifyRegisterType(dec_insn.vB, src_type)) { - SetRegisterType(dec_insn.vA, dst_type); + if (VerifyRegisterType(inst->VRegB_12x(), src_type)) { + SetRegisterType(inst->VRegA_12x(), dst_type); } } -void RegisterLine::CheckUnaryOpWide(const DecodedInstruction& dec_insn, +void RegisterLine::CheckUnaryOpWide(const Instruction* inst, const RegType& dst_type1, const RegType& dst_type2, const RegType& src_type1, const RegType& src_type2) { - if (VerifyRegisterTypeWide(dec_insn.vB, src_type1, src_type2)) { - SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2); + if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_type1, src_type2)) { + SetRegisterTypeWide(inst->VRegA_12x(), dst_type1, dst_type2); } } -void RegisterLine::CheckUnaryOpToWide(const DecodedInstruction& dec_insn, +void RegisterLine::CheckUnaryOpToWide(const Instruction* inst, const RegType& dst_type1, const RegType& dst_type2, const RegType& src_type) { - if (VerifyRegisterType(dec_insn.vB, src_type)) { - SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2); + if (VerifyRegisterType(inst->VRegB_12x(), src_type)) { + SetRegisterTypeWide(inst->VRegA_12x(), dst_type1, dst_type2); } } -void RegisterLine::CheckUnaryOpFromWide(const DecodedInstruction& dec_insn, +void RegisterLine::CheckUnaryOpFromWide(const Instruction* inst, const RegType& dst_type, const RegType& src_type1, const RegType& src_type2) { - if (VerifyRegisterTypeWide(dec_insn.vB, src_type1, src_type2)) { - SetRegisterType(dec_insn.vA, dst_type); + if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_type1, src_type2)) { + SetRegisterType(inst->VRegA_12x(), dst_type); } } -void RegisterLine::CheckBinaryOp(const DecodedInstruction& dec_insn, +void RegisterLine::CheckBinaryOp(const Instruction* inst, const RegType& dst_type, const RegType& src_type1, const RegType& src_type2, bool check_boolean_op) { - if (VerifyRegisterType(dec_insn.vB, src_type1) && - VerifyRegisterType(dec_insn.vC, src_type2)) { + const uint32_t vregB = inst->VRegB_23x(); + const uint32_t vregC = inst->VRegC_23x(); + if (VerifyRegisterType(vregB, src_type1) && + VerifyRegisterType(vregC, src_type2)) { if (check_boolean_op) { DCHECK(dst_type.IsInteger()); - if (GetRegisterType(dec_insn.vB).IsBooleanTypes() && - GetRegisterType(dec_insn.vC).IsBooleanTypes()) { - SetRegisterType(dec_insn.vA, verifier_->GetRegTypeCache()->Boolean()); + if (GetRegisterType(vregB).IsBooleanTypes() && + GetRegisterType(vregC).IsBooleanTypes()) { + SetRegisterType(inst->VRegA_23x(), verifier_->GetRegTypeCache()->Boolean()); return; } } - SetRegisterType(dec_insn.vA, dst_type); + SetRegisterType(inst->VRegA_23x(), dst_type); } } -void RegisterLine::CheckBinaryOpWide(const DecodedInstruction& dec_insn, +void RegisterLine::CheckBinaryOpWide(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) { - if (VerifyRegisterTypeWide(dec_insn.vB, src_type1_1, src_type1_2) && - VerifyRegisterTypeWide(dec_insn.vC, src_type2_1, src_type2_2)) { - SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2); + if (VerifyRegisterTypeWide(inst->VRegB_23x(), src_type1_1, src_type1_2) && + VerifyRegisterTypeWide(inst->VRegC_23x(), src_type2_1, src_type2_2)) { + SetRegisterTypeWide(inst->VRegA_23x(), dst_type1, dst_type2); } } -void RegisterLine::CheckBinaryOpWideShift(const DecodedInstruction& dec_insn, +void RegisterLine::CheckBinaryOpWideShift(const Instruction* inst, const RegType& long_lo_type, const RegType& long_hi_type, const RegType& int_type) { - if (VerifyRegisterTypeWide(dec_insn.vB, long_lo_type, long_hi_type) && - VerifyRegisterType(dec_insn.vC, int_type)) { - SetRegisterTypeWide(dec_insn.vA, long_lo_type, long_hi_type); + if (VerifyRegisterTypeWide(inst->VRegB_23x(), long_lo_type, long_hi_type) && + VerifyRegisterType(inst->VRegC_23x(), int_type)) { + SetRegisterTypeWide(inst->VRegA_23x(), long_lo_type, long_hi_type); } } -void RegisterLine::CheckBinaryOp2addr(const DecodedInstruction& dec_insn, +void RegisterLine::CheckBinaryOp2addr(const Instruction* inst, const RegType& dst_type, const RegType& src_type1, const RegType& src_type2, bool check_boolean_op) { - if (VerifyRegisterType(dec_insn.vA, src_type1) && - VerifyRegisterType(dec_insn.vB, src_type2)) { + const uint32_t vregA = inst->VRegA_12x(); + const uint32_t vregB = inst->VRegB_12x(); + if (VerifyRegisterType(vregA, src_type1) && + VerifyRegisterType(vregB, src_type2)) { if (check_boolean_op) { DCHECK(dst_type.IsInteger()); - if (GetRegisterType(dec_insn.vA).IsBooleanTypes() && - GetRegisterType(dec_insn.vB).IsBooleanTypes()) { - SetRegisterType(dec_insn.vA, verifier_->GetRegTypeCache()->Boolean()); + if (GetRegisterType(vregA).IsBooleanTypes() && + GetRegisterType(vregB).IsBooleanTypes()) { + SetRegisterType(vregA, verifier_->GetRegTypeCache()->Boolean()); return; } } - SetRegisterType(dec_insn.vA, dst_type); + SetRegisterType(vregA, dst_type); } } -void RegisterLine::CheckBinaryOp2addrWide(const DecodedInstruction& dec_insn, +void RegisterLine::CheckBinaryOp2addrWide(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) { - if (VerifyRegisterTypeWide(dec_insn.vA, src_type1_1, src_type1_2) && - VerifyRegisterTypeWide(dec_insn.vB, src_type2_1, src_type2_2)) { - SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2); + const uint32_t vregA = inst->VRegA_12x(); + const uint32_t vregB = inst->VRegB_12x(); + if (VerifyRegisterTypeWide(vregA, src_type1_1, src_type1_2) && + VerifyRegisterTypeWide(vregB, src_type2_1, src_type2_2)) { + SetRegisterTypeWide(vregA, dst_type1, dst_type2); } } -void RegisterLine::CheckBinaryOp2addrWideShift(const DecodedInstruction& dec_insn, +void RegisterLine::CheckBinaryOp2addrWideShift(const Instruction* inst, const RegType& long_lo_type, const RegType& long_hi_type, const RegType& int_type) { - if (VerifyRegisterTypeWide(dec_insn.vA, long_lo_type, long_hi_type) && - VerifyRegisterType(dec_insn.vB, int_type)) { - SetRegisterTypeWide(dec_insn.vA, long_lo_type, long_hi_type); + const uint32_t vregA = inst->VRegA_12x(); + const uint32_t vregB = inst->VRegB_12x(); + if (VerifyRegisterTypeWide(vregA, long_lo_type, long_hi_type) && + VerifyRegisterType(vregB, int_type)) { + SetRegisterTypeWide(vregA, long_lo_type, long_hi_type); } } -void RegisterLine::CheckLiteralOp(const DecodedInstruction& dec_insn, +void RegisterLine::CheckLiteralOp(const Instruction* inst, const RegType& dst_type, const RegType& src_type, - bool check_boolean_op) { - if (VerifyRegisterType(dec_insn.vB, src_type)) { + bool check_boolean_op, bool is_lit16) { + const uint32_t vregA = is_lit16 ? inst->VRegA_22s() : inst->VRegA_22b(); + const uint32_t vregB = is_lit16 ? inst->VRegB_22s() : inst->VRegB_22b(); + if (VerifyRegisterType(vregB, src_type)) { if (check_boolean_op) { DCHECK(dst_type.IsInteger()); /* check vB with the call, then check the constant manually */ - if (GetRegisterType(dec_insn.vB).IsBooleanTypes() && - (dec_insn.vC == 0 || dec_insn.vC == 1)) { - SetRegisterType(dec_insn.vA, verifier_->GetRegTypeCache()->Boolean()); + const uint32_t val = is_lit16 ? inst->VRegC_22s() : inst->VRegC_22b(); + if (GetRegisterType(vregB).IsBooleanTypes() && (val == 0 || val == 1)) { + SetRegisterType(vregA, verifier_->GetRegTypeCache()->Boolean()); return; } } - SetRegisterType(dec_insn.vA, dst_type); + SetRegisterType(vregA, dst_type); } } @@ -427,6 +435,8 @@ bool RegisterLine::VerifyMonitorStackEmpty() { bool RegisterLine::MergeRegisters(const RegisterLine* incoming_line) { bool changed = false; + CHECK(NULL != incoming_line); + CHECK(NULL != line_.get()); for (size_t idx = 0; idx < num_regs_; idx++) { if (line_[idx] != incoming_line->line_[idx]) { const RegType& incoming_reg_type = incoming_line->GetRegisterType(idx); diff --git a/src/verifier/register_line.h b/src/verifier/register_line.h index 5719082518..5f17049e8e 100644 --- a/src/verifier/register_line.h +++ b/src/verifier/register_line.h @@ -169,28 +169,28 @@ class RegisterLine { * The argument count is in vA, and the first argument is in vC, for both "simple" and "range" * versions. We just need to make sure vA is >= 1 and then return vC. */ - const RegType& GetInvocationThis(const DecodedInstruction& dec_insn) + const RegType& GetInvocationThis(const Instruction* inst, bool is_range) SHARED_LOCKS_REQUIRED(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(const DecodedInstruction& dec_insn, - const RegType& dst_type, const RegType& src_type) + void CheckUnaryOp(const Instruction* inst, const RegType& dst_type, + const RegType& src_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckUnaryOpWide(const DecodedInstruction& dec_insn, + void CheckUnaryOpWide(const Instruction* inst, const RegType& dst_type1, const RegType& dst_type2, const RegType& src_type1, const RegType& src_type2) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckUnaryOpToWide(const DecodedInstruction& dec_insn, + void CheckUnaryOpToWide(const Instruction* inst, const RegType& dst_type1, const RegType& dst_type2, const RegType& src_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckUnaryOpFromWide(const DecodedInstruction& dec_insn, + void CheckUnaryOpFromWide(const Instruction* inst, const RegType& dst_type, const RegType& src_type1, const RegType& src_type2) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -200,18 +200,18 @@ class RegisterLine { * "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified * against vB/vC. */ - void CheckBinaryOp(const DecodedInstruction& dec_insn, + void CheckBinaryOp(const Instruction* inst, const RegType& dst_type, const RegType& src_type1, const RegType& src_type2, bool check_boolean_op) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckBinaryOpWide(const DecodedInstruction& dec_insn, + void CheckBinaryOpWide(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_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckBinaryOpWideShift(const DecodedInstruction& dec_insn, + void CheckBinaryOpWideShift(const Instruction* inst, const RegType& long_lo_type, const RegType& long_hi_type, const RegType& int_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -220,19 +220,19 @@ 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(const DecodedInstruction& dec_insn, + void CheckBinaryOp2addr(const Instruction* inst, const RegType& dst_type, const RegType& src_type1, const RegType& src_type2, bool check_boolean_op) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckBinaryOp2addrWide(const DecodedInstruction& dec_insn, + void CheckBinaryOp2addrWide(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_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckBinaryOp2addrWideShift(const DecodedInstruction& dec_insn, + void CheckBinaryOp2addrWideShift(const Instruction* inst, const RegType& long_lo_type, const RegType& long_hi_type, const RegType& int_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -243,8 +243,9 @@ class RegisterLine { * * If "check_boolean_op" is set, we use the constant value in vC. */ - void CheckLiteralOp(const DecodedInstruction& dec_insn, - const RegType& dst_type, const RegType& src_type, bool check_boolean_op) + void CheckLiteralOp(const Instruction* inst, + const RegType& dst_type, const RegType& src_type, + bool check_boolean_op, bool is_lit16) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Verify/push monitor onto the monitor stack, locking the value in reg_idx at location insn_idx. diff --git a/src/well_known_classes.cc b/src/well_known_classes.cc index f836f4f9b4..4d34c7350e 100644 --- a/src/well_known_classes.cc +++ b/src/well_known_classes.cc @@ -77,7 +77,7 @@ jfieldID WellKnownClasses::java_lang_Thread_lock; jfieldID WellKnownClasses::java_lang_Thread_name; jfieldID WellKnownClasses::java_lang_Thread_priority; jfieldID WellKnownClasses::java_lang_Thread_uncaughtHandler; -jfieldID WellKnownClasses::java_lang_Thread_vmData; +jfieldID WellKnownClasses::java_lang_Thread_nativePeer; jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup; jfieldID WellKnownClasses::java_lang_ThreadGroup_name; jfieldID WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup; @@ -172,7 +172,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;"); java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I"); java_lang_Thread_uncaughtHandler = CacheField(env, java_lang_Thread, false, "uncaughtHandler", "Ljava/lang/Thread$UncaughtExceptionHandler;"); - java_lang_Thread_vmData = CacheField(env, java_lang_Thread, false, "vmData", "I"); + java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "I"); java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;"); java_lang_ThreadGroup_name = CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;"); java_lang_ThreadGroup_systemThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;"); diff --git a/src/well_known_classes.h b/src/well_known_classes.h index d9acf2d636..8170520d45 100644 --- a/src/well_known_classes.h +++ b/src/well_known_classes.h @@ -90,7 +90,7 @@ struct WellKnownClasses { static jfieldID java_lang_Thread_name; static jfieldID java_lang_Thread_priority; static jfieldID java_lang_Thread_uncaughtHandler; - static jfieldID java_lang_Thread_vmData; + static jfieldID java_lang_Thread_nativePeer; static jfieldID java_lang_ThreadGroup_mainThreadGroup; static jfieldID java_lang_ThreadGroup_name; static jfieldID java_lang_ThreadGroup_systemThreadGroup; |