diff options
239 files changed, 10219 insertions, 5553 deletions
diff --git a/.gitignore b/.gitignore index 5973a95e1f..1cdfed9231 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ USE_LLVM_COMPILER +USE_PORTABLE_COMPILER +SMALL_ART +SEA_IR_ART diff --git a/Android.mk b/Android.mk index 4eac005f37..daff6cb529 100644 --- a/Android.mk +++ b/Android.mk @@ -369,22 +369,19 @@ cpplint-art: .PHONY: use-art use-art: adb root && sleep 3 - adb shell "echo dalvik.vm.lib=libart.so > /data/local.prop" - adb shell chmod 644 /data/local.prop + adb shell setprop persist.sys.dalvik.vm.lib libart.so adb reboot .PHONY: use-artd use-artd: adb root && sleep 3 - adb shell "echo dalvik.vm.lib=libartd.so > /data/local.prop" - adb shell chmod 644 /data/local.prop + adb shell setprop persist.sys.dalvik.vm.lib libartd.so adb reboot .PHONY: use-dalvik use-dalvik: adb root && sleep 3 - adb shell "echo dalvik.vm.lib=libdvm.so > /data/local.prop" - adb shell chmod 644 /data/local.prop + adb shell setprop persist.sys.dalvik.vm.lib libdvm.so adb reboot ######################################################################## diff --git a/build/Android.common.mk b/build/Android.common.mk index e84667dd38..33c5ac68bf 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -19,6 +19,18 @@ ifneq ($(wildcard art/SMALL_ART),) $(info Enabling ART_SMALL_MODE because of existence of art/SMALL_ART) ART_SMALL_MODE := true endif +ifeq ($(WITH_ART_SMALL_MODE), true) +ART_SMALL_MODE := true +endif + +ART_SEA_IR_MODE := false +ifneq ($(wildcard art/SEA_IR_ART),) +$(info Enabling ART_SEA_IR_MODE because of existence of art/SEA_IR_ART) +ART_SEA_IR_MODE := true +endif +ifeq ($(WITH_ART_SEA_IR_MODE), true) +ART_SEA_IR_MODE := true +endif ART_USE_PORTABLE_COMPILER := false ifneq ($(wildcard art/USE_PORTABLE_COMPILER),) @@ -49,11 +61,13 @@ ART_CPP_EXTENSION := .cc ART_C_INCLUDES := \ external/gtest/include \ + external/valgrind/main/include \ external/zlib \ frameworks/compile/mclinker/include \ art/src art_cflags := \ + -fno-rtti \ -O2 \ -ggdb3 \ -Wall \ @@ -62,17 +76,14 @@ art_cflags := \ -Wstrict-aliasing=3 \ -fstrict-aliasing -# Enable thread-safety for GCC 4.6 but not for GCC 4.7 where this feature was removed. -# Enable GCC 4.6 builds with 'export TARGET_GCC_VERSION_EXP=4.6' -ifneq ($(filter 4.6 4.6.%, $(TARGET_GCC_VERSION)),) - $(info Enabling thread-safety for GCC $(TARGET_GCC_VERSION)) - art_cflags += -Wthread-safety -endif - ifeq ($(ART_SMALL_MODE),true) art_cflags += -DART_SMALL_MODE=1 endif +ifeq ($(ART_SEA_IR_MODE),true) + art_cflags += -DART_SEA_IR_MODE=1 +endif + # TODO: enable -std=gnu++0x for auto support when on Ubuntu 12.04 LTS (Precise Pangolin) # On 10.04 LTS (Lucid Lynx), it can cause dependencies on GLIBCXX_3.4.14 version symbols. @@ -108,6 +119,22 @@ else ART_TARGET_CFLAGS += -DANDROID_SMP=0 endif +# Enable thread-safety for GCC 4.6 on the target but not for GCC 4.7 where this feature was removed. +ifneq ($(filter 4.6 4.6.%, $(TARGET_GCC_VERSION)),) + ART_TARGET_CFLAGS += -Wthread-safety +else + # Warn if not using GCC 4.6 for target builds when not doing a top-level or 'mma' build. + ifneq ($(ONE_SHOT_MAKEFILE),) + # Enable target GCC 4.6 with: export TARGET_GCC_VERSION_EXP=4.6 + $(info Using target GCC $(TARGET_GCC_VERSION) disables thread-safety checks.) + endif +endif +# We build with GCC 4.6 on the host. +ART_HOST_CFLAGS += -Wthread-safety + +# Make host builds easier to debug and profile by not omitting the frame pointer. +ART_HOST_CFLAGS += -fno-omit-frame-pointer + # To use oprofile_android --callgraph, uncomment this and recompile with "mmm art -B -j16" # ART_TARGET_CFLAGS += -fno-omit-frame-pointer -marm -mapcs @@ -156,6 +183,8 @@ LIBART_COMMON_SRC_FILES := \ src/compiled_method.cc \ src/compiler/driver/compiler_driver.cc \ src/compiler/llvm/runtime_support_llvm.cc \ + src/compiler/stubs/portable/stubs.cc \ + src/compiler/stubs/quick/stubs.cc \ src/debugger.cc \ src/dex_file.cc \ src/dex_file_verifier.cc \ @@ -164,20 +193,22 @@ LIBART_COMMON_SRC_FILES := \ src/disassembler_arm.cc \ src/disassembler_mips.cc \ src/disassembler_x86.cc \ - src/dlmalloc.cc \ src/elf_file.cc \ src/file_output_stream.cc \ - src/gc/card_table.cc \ - src/gc/garbage_collector.cc \ - src/gc/heap_bitmap.cc \ - src/gc/large_object_space.cc \ - src/gc/mark_sweep.cc \ - src/gc/mod_union_table.cc \ - src/gc/partial_mark_sweep.cc \ - src/gc/space.cc \ - src/gc/space_bitmap.cc \ - src/gc/sticky_mark_sweep.cc \ - src/heap.cc \ + src/gc/allocator/dlmalloc.cc \ + src/gc/accounting/card_table.cc \ + src/gc/accounting/heap_bitmap.cc \ + src/gc/accounting/mod_union_table.cc \ + src/gc/accounting/space_bitmap.cc \ + src/gc/collector/garbage_collector.cc \ + src/gc/collector/mark_sweep.cc \ + src/gc/collector/partial_mark_sweep.cc \ + src/gc/collector/sticky_mark_sweep.cc \ + src/gc/heap.cc \ + src/gc/space/dlmalloc_space.cc \ + src/gc/space/image_space.cc \ + src/gc/space/large_object_space.cc \ + src/gc/space/space.cc \ src/hprof/hprof.cc \ src/image.cc \ src/image_writer.cc \ @@ -284,6 +315,11 @@ LIBART_COMMON_SRC_FILES += \ src/oat/runtime/support_throw.cc \ src/oat/runtime/support_interpreter.cc +ifeq ($(ART_SEA_IR_MODE),true) +LIBART_COMMON_SRC_FILES += \ + src/compiler/sea_ir/sea.cc +endif + LIBART_TARGET_SRC_FILES := \ $(LIBART_COMMON_SRC_FILES) \ src/base/logging_android.cc \ @@ -357,9 +393,9 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \ src/compiler/dex/compiler_enums.h \ src/dex_file.h \ src/dex_instruction.h \ - src/gc/gc_type.h \ - src/gc/space.h \ - src/heap.h \ + src/gc/collector/gc_type.h \ + src/gc/space/space.h \ + src/gc/heap.h \ src/indirect_reference_table.h \ src/instruction_set.h \ src/invoke_type.h \ @@ -392,10 +428,10 @@ TEST_COMMON_SRC_FILES := \ src/dex_method_iterator_test.cc \ src/elf_writer_test.cc \ src/exception_test.cc \ - src/gc/space_bitmap_test.cc \ - src/gc/space_test.cc \ + src/gc/accounting/space_bitmap_test.cc \ + src/gc/heap_test.cc \ + src/gc/space/space_test.cc \ src/gtest_test.cc \ - src/heap_test.cc \ src/image_test.cc \ src/indenter_test.cc \ src/indirect_reference_table_test.cc \ diff --git a/build/Android.libart-compiler.mk b/build/Android.libart-compiler.mk index 4452f05f52..25e6997ffc 100644 --- a/build/Android.libart-compiler.mk +++ b/build/Android.libart-compiler.mk @@ -44,8 +44,8 @@ LIBART_COMPILER_SRC_FILES := \ src/compiler/dex/quick/x86/target_x86.cc \ src/compiler/dex/quick/x86/utility_x86.cc \ src/compiler/dex/portable/mir_to_gbc.cc \ + src/compiler/dex/dex_to_dex_compiler.cc \ src/compiler/dex/mir_dataflow.cc \ - src/compiler/dex/dataflow_iterator.cc \ src/compiler/dex/mir_optimization.cc \ src/compiler/dex/frontend.cc \ src/compiler/dex/mir_graph.cc \ @@ -76,6 +76,11 @@ LIBART_COMPILER_SRC_FILES := \ src/elf_writer.cc \ src/elf_writer_quick.cc +ifeq ($(ART_SEA_IR_MODE),true) +LIBART_COMPILER_SRC_FILES += \ + src/compiler/sea_ir/frontend.cc +endif + LIBART_COMPILER_CFLAGS := ifeq ($(ART_USE_PORTABLE_COMPILER),true) LIBART_COMPILER_SRC_FILES += src/elf_writer_mclinker.cc 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; diff --git a/test/031-class-attributes/expected.txt b/test/031-class-attributes/expected.txt index afa3416e21..4ae1eeda96 100644 --- a/test/031-class-attributes/expected.txt +++ b/test/031-class-attributes/expected.txt @@ -1,3 +1,25 @@ +public abstract final int +public abstract final [I +public java.lang.Object +public abstract final [Ljava.lang.Object; +public ClassAttrs$PublicInnerClass +public abstract final [LClassAttrs$PublicInnerClass; +protected ClassAttrs$ProtectedInnerClass +protected abstract final [LClassAttrs$ProtectedInnerClass; +private ClassAttrs$PrivateInnerClass +private abstract final [LClassAttrs$PrivateInnerClass; + ClassAttrs$PackagePrivateInnerClass +abstract final [LClassAttrs$PackagePrivateInnerClass; +public abstract interface java.io.Serializable +public abstract final [Ljava.io.Serializable; +public abstract static interface ClassAttrs$PublicInnerInterface +public abstract final [LClassAttrs$PublicInnerInterface; +protected abstract static interface ClassAttrs$ProtectedInnerInterface +protected abstract final [LClassAttrs$ProtectedInnerInterface; +private abstract static interface ClassAttrs$PrivateInnerInterface +private abstract final [LClassAttrs$PrivateInnerInterface; +abstract static interface ClassAttrs$PackagePrivateInnerInterface +abstract final [LClassAttrs$PackagePrivateInnerInterface; ***** class ClassAttrs: name: ClassAttrs canonical: ClassAttrs @@ -11,8 +33,8 @@ enclosingMeth: null modifiers: 1 package: null - declaredClasses: [2] class ClassAttrs$PublicMemberClass, class ClassAttrs$MemberClass - member classes: [1] class ClassAttrs$PublicMemberClass + declaredClasses: [10] class ClassAttrs$PublicMemberClass, class ClassAttrs$MemberClass, interface ClassAttrs$PackagePrivateInnerInterface, interface ClassAttrs$PrivateInnerInterface, interface ClassAttrs$ProtectedInnerInterface, interface ClassAttrs$PublicInnerInterface, class ClassAttrs$PackagePrivateInnerClass, class ClassAttrs$PrivateInnerClass, class ClassAttrs$ProtectedInnerClass, class ClassAttrs$PublicInnerClass + member classes: [3] class ClassAttrs$PublicMemberClass, interface ClassAttrs$PublicInnerInterface, class ClassAttrs$PublicInnerClass isAnnotation: false isAnonymous: false isArray: false diff --git a/test/031-class-attributes/src/ClassAttrs.java b/test/031-class-attributes/src/ClassAttrs.java index 8719e3bd06..ae8b2f56da 100644 --- a/test/031-class-attributes/src/ClassAttrs.java +++ b/test/031-class-attributes/src/ClassAttrs.java @@ -1,10 +1,12 @@ import otherpackage.OtherPackageClass; +import java.io.Serializable; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -21,7 +23,72 @@ public class ClassAttrs { cinner.showMe(); } + public class PublicInnerClass { + } + + protected class ProtectedInnerClass { + } + + private class PrivateInnerClass { + } + + class PackagePrivateInnerClass { + } + + public interface PublicInnerInterface { + } + + protected interface ProtectedInnerInterface { + } + + private interface PrivateInnerInterface { + } + + interface PackagePrivateInnerInterface { + } + + private static void showModifiers(Class<?> c) { + System.out.println(Modifier.toString(c.getModifiers()) + " " + c.getName()); + } + + // https://code.google.com/p/android/issues/detail?id=56267 + private static void test56267() { + // Primitive classes. + showModifiers(int.class); + showModifiers(int[].class); + + // Regular classes. + showModifiers(Object.class); + showModifiers(Object[].class); + + // Inner classes. + showModifiers(PublicInnerClass.class); + showModifiers(PublicInnerClass[].class); + showModifiers(ProtectedInnerClass.class); + showModifiers(ProtectedInnerClass[].class); + showModifiers(PrivateInnerClass.class); + showModifiers(PrivateInnerClass[].class); + showModifiers(PackagePrivateInnerClass.class); + showModifiers(PackagePrivateInnerClass[].class); + + // Regular interfaces. + showModifiers(Serializable.class); + showModifiers(Serializable[].class); + + // Inner interfaces. + showModifiers(PublicInnerInterface.class); + showModifiers(PublicInnerInterface[].class); + showModifiers(ProtectedInnerInterface.class); + showModifiers(ProtectedInnerInterface[].class); + showModifiers(PrivateInnerInterface.class); + showModifiers(PrivateInnerInterface[].class); + showModifiers(PackagePrivateInnerInterface.class); + showModifiers(PackagePrivateInnerInterface[].class); + } + public static void main() { + test56267(); + printClassAttrs(ClassAttrs.class); printClassAttrs(OtherClass.class); printClassAttrs(OtherPackageClass.class); diff --git a/test/045-reflect-array/expected.txt b/test/045-reflect-array/expected.txt index 5990b34a40..b9a98c9849 100644 --- a/test/045-reflect-array/expected.txt +++ b/test/045-reflect-array/expected.txt @@ -6,4 +6,7 @@ ReflectArrayTest.testSingle passed ReflectArrayTest.testMultiInt passed zero one two ++ ReflectArrayTest.testMulti passed +class [Ljava.lang.Number; modifiers: 1041 +class [Ljava.lang.Cloneable; modifiers: 1041 +ReflectArrayTest.testAbstract passed ReflectArrayTest passed diff --git a/test/045-reflect-array/src/Main.java b/test/045-reflect-array/src/Main.java index 05cf84302a..7418eed824 100644 --- a/test/045-reflect-array/src/Main.java +++ b/test/045-reflect-array/src/Main.java @@ -16,6 +16,7 @@ public class Main { testSingle(); testMultiInt(); testMulti(); + testAbstract(); System.out.println("ReflectArrayTest passed"); } @@ -255,4 +256,14 @@ public class Main { } System.out.println("ReflectArrayTest.testMulti passed"); } + + static void testAbstract() { + Object arrayOfAbstractClasses = Array.newInstance(Number.class, 1); + System.out.println(arrayOfAbstractClasses.getClass().toString() + " modifiers: " + + arrayOfAbstractClasses.getClass().getModifiers()); + arrayOfAbstractClasses = Array.newInstance(Cloneable.class, 1); + System.out.println(arrayOfAbstractClasses.getClass().toString() + " modifiers: " + + arrayOfAbstractClasses.getClass().getModifiers()); + System.out.println("ReflectArrayTest.testAbstract passed"); + } } diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index 0c567d49c5..f56fd98947 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -34,7 +34,7 @@ z (class java.lang.Character) 30 (class java.lang.Integer) 62 (class java.lang.Long) 14 (class java.lang.Short) -[public java.lang.String(), java.lang.String(int,int,char[]), public java.lang.String(java.lang.String), private java.lang.String(java.lang.String,char), private java.lang.String(java.lang.String,int), private java.lang.String(java.lang.String,java.lang.String), private java.lang.String(java.lang.String,java.lang.String,java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int)] +[public java.lang.String(), java.lang.String(int,int,char[]), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int)] [private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final char java.lang.String.REPLACEMENT_CHAR, private static final long java.lang.String.serialVersionUID] [void java.lang.String._getChars(int,int,char[],int), public char java.lang.String.charAt(int), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public volatile int java.lang.String.compareTo(java.lang.Object), public native int java.lang.String.compareTo(java.lang.String), public int java.lang.String.compareToIgnoreCase(java.lang.String), public java.lang.String java.lang.String.concat(java.lang.String), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public void java.lang.String.getBytes(int,int,byte[],int), public [B java.lang.String.getBytes(), public [B java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public [B java.lang.String.getBytes(java.nio.charset.Charset), public void java.lang.String.getChars(int,int,char[],int), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public native java.lang.String java.lang.String.intern(), public boolean java.lang.String.isEmpty(), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public boolean java.lang.String.matches(java.lang.String), public int java.lang.String.offsetByCodePoints(int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public [C java.lang.String.toCharArray(), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), static void java.lang.String.<clinit>(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.failedBoundsCheck(int,int,int), private native int java.lang.String.fastIndexOf(int,int), private char java.lang.String.foldCase(char), public static transient java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static transient java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), private java.lang.StringIndexOutOfBoundsException java.lang.String.indexAndLength(int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int,int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.startEndAndLength(int,int), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(long), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int)] [] diff --git a/test/108-check-cast/expected.txt b/test/108-check-cast/expected.txt new file mode 100644 index 0000000000..d86bac9de5 --- /dev/null +++ b/test/108-check-cast/expected.txt @@ -0,0 +1 @@ +OK diff --git a/test/108-check-cast/info.txt b/test/108-check-cast/info.txt new file mode 100644 index 0000000000..e7ffa4fdd0 --- /dev/null +++ b/test/108-check-cast/info.txt @@ -0,0 +1,10 @@ +This test relies on the correct behavior of instance-of to test check-cast behavior, +as shown below: + + +CCE throw| InstanceOf | Correct? +---------+------------+---------- + 1 | 0 | OK + 1 | 1 | BAD + 0 | 0 | BAD + 0 | 1 | OK
\ No newline at end of file diff --git a/test/108-check-cast/src/Main.java b/test/108-check-cast/src/Main.java new file mode 100644 index 0000000000..7ef13fd3ed --- /dev/null +++ b/test/108-check-cast/src/Main.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +/** + * Testing check-cast, see comment in info.txt + */ + +class B {} +class D extends B {} + +public class Main { + public static void main(String args[]) { + B b = null; + try { + if (1 == args.length) { + b = new B(); + } else { + b = new D(); + } + D d = (D) b; + if (!(b instanceof D)) { + System.out.println("Error: No ClassCastException throuwn when it should have been."); + } else { + System.out.println("OK"); + } + } + catch (ClassCastException cce) { + if (b instanceof D) { + System.out.println("Error: ClassCastException thrown when it shouldn't have been."); + } else { + System.out.println("OK"); + } + } + } +} diff --git a/test/201-built-in-exception-detail-messages/src/Main.java b/test/201-built-in-exception-detail-messages/src/Main.java index f8da6446cf..24ee6e0eb3 100644 --- a/test/201-built-in-exception-detail-messages/src/Main.java +++ b/test/201-built-in-exception-detail-messages/src/Main.java @@ -286,10 +286,19 @@ public class Main { } } + // Defeat the fact that null's are untyped for precise detail message creation with quickening. + private static Object returnNullObject() { + return null; + } + + private static A returnNullA() { + return null; + } + private static void nullPointers() throws Exception { // Invoke method. try { - Object o = null; + Object o = returnNullObject(); o.hashCode(); fail(); } catch (NullPointerException ex) { @@ -298,7 +307,7 @@ public class Main { // Read field. try { - A a = null; + A a = returnNullA(); int i = a.i; fail(); } catch (NullPointerException ex) { @@ -307,7 +316,7 @@ public class Main { // Write field. try { - A a = null; + A a = returnNullA(); a.i = 1; fail(); } catch (NullPointerException ex) { @@ -332,7 +341,7 @@ public class Main { assertEquals("Attempt to write to null array", ex.getMessage()); } - // Invoke method. + // Array length. try { int[] is = null; int i = is.length; |
