diff options
| -rw-r--r-- | compiler/optimizing/instruction_builder.cc | 10 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler_stats.h | 1 | ||||
| -rw-r--r-- | runtime/common_dex_operations.h | 1 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_common.cc | 2 | ||||
| -rw-r--r-- | runtime/jit/jit.cc | 79 | ||||
| -rw-r--r-- | runtime/jit/jit.h | 229 | ||||
| -rw-r--r-- | runtime/jvalue-inl.h | 2 | ||||
| -rw-r--r-- | runtime/jvalue.h | 3 | ||||
| -rw-r--r-- | runtime/method_handles-inl.h | 2 | ||||
| -rw-r--r-- | runtime/method_handles.cc | 1 | ||||
| -rw-r--r-- | runtime/native/java_lang_reflect_Field.cc | 1 | ||||
| -rw-r--r-- | runtime/parsed_options.cc | 3 | ||||
| -rw-r--r-- | runtime/reflection.cc | 1 | ||||
| -rw-r--r-- | runtime/runtime_options.def | 1 | ||||
| -rw-r--r-- | runtime/var_handles.cc | 68 | ||||
| -rw-r--r-- | test/563-checker-fakestring/smali/TestCase.smali | 43 | ||||
| -rw-r--r-- | test/563-checker-fakestring/src/Main.java | 11 | ||||
| -rw-r--r-- | tools/dexanalyze/dexanalyze.cc | 10 | ||||
| -rw-r--r-- | tools/dexanalyze/dexanalyze_experiments.cc | 82 | ||||
| -rw-r--r-- | tools/dexanalyze/dexanalyze_experiments.h | 21 | ||||
| -rw-r--r-- | tools/ti-fast/tifast.cc | 2 |
21 files changed, 366 insertions, 207 deletions
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 0e20a65a2d..42031f9e25 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1360,9 +1360,15 @@ bool HInstructionBuilder::HandleStringInit(HInvoke* invoke, if (arg_this->IsNewInstance()) { ssa_builder_->AddUninitializedString(arg_this->AsNewInstance()); } else { + // The only reason a HPhi can flow in a String.<init> is when there is an + // irreducible loop, which will create HPhi for all dex registers at loop entry. DCHECK(arg_this->IsPhi()); - // NewInstance is not the direct input of the StringFactory call. It might - // be redundant but optimizing this case is not worth the effort. + DCHECK(graph_->HasIrreducibleLoops()); + // Don't bother compiling a method in that situation. While we could look at all + // phis related to the HNewInstance, it's not worth the trouble. + MaybeRecordStat(compilation_stats_, + MethodCompilationStat::kNotCompiledIrreducibleAndStringInit); + return false; } // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`. diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 9a26f2f6c4..f246228074 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -50,6 +50,7 @@ enum class MethodCompilationStat { kNotCompiledThrowCatchLoop, kNotCompiledAmbiguousArrayOp, kNotCompiledHugeMethod, + kNotCompiledIrreducibleAndStringInit, kNotCompiledLargeMethodNoBranches, kNotCompiledMalformedOpcode, kNotCompiledNoCodegen, diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 37e074d552..9c2a40b50d 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -29,6 +29,7 @@ #include "instrumentation.h" #include "interpreter/shadow_frame.h" #include "interpreter/unstarted_runtime.h" +#include "jvalue-inl.h" #include "mirror/class.h" #include "mirror/object.h" #include "obj_ptr-inl.h" diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index d30bc10450..5a50ec5586 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -24,7 +24,7 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "intrinsics_enum.h" #include "jit/jit.h" -#include "jvalue.h" +#include "jvalue-inl.h" #include "method_handles-inl.h" #include "method_handles.h" #include "mirror/array-inl.h" diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 0684b461ae..86e69f49a4 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -46,8 +46,6 @@ namespace art { namespace jit { static constexpr bool kEnableOnStackReplacement = true; -// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. -static constexpr int kJitPoolThreadPthreadPriority = 9; // Different compilation threshold constants. These can be overridden on the command line. static constexpr size_t kJitDefaultCompileThreshold = 10000; // Non-debug default. @@ -80,6 +78,8 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown); jit_options->profile_saver_options_ = options.GetOrDefault(RuntimeArgumentMap::ProfileSaverOpts); + jit_options->thread_pool_pthread_priority_ = + options.GetOrDefault(RuntimeArgumentMap::JITPoolThreadPthreadPriority); if (options.Exists(RuntimeArgumentMap::JITCompileThreshold)) { jit_options->compile_threshold_ = *options.Get(RuntimeArgumentMap::JITCompileThreshold); @@ -167,21 +167,14 @@ void Jit::AddTimingLogger(const TimingLogger& logger) { cumulative_timings_.AddLogger(logger); } -Jit::Jit() : dump_info_on_shutdown_(false), - cumulative_timings_("JIT timings"), - memory_use_("Memory used for compilation", 16), - lock_("JIT memory use lock"), - use_jit_compilation_(true), - hot_method_threshold_(0), - warm_method_threshold_(0), - osr_method_threshold_(0), - priority_thread_weight_(0), - invoke_transition_weight_(0) {} +Jit::Jit(JitOptions* options) : options_(options), + cumulative_timings_("JIT timings"), + memory_use_("Memory used for compilation", 16), + lock_("JIT memory use lock") {} Jit* Jit::Create(JitOptions* options, std::string* error_msg) { DCHECK(options->UseJitCompilation() || options->GetProfileSaverOptions().IsEnabled()); - std::unique_ptr<Jit> jit(new Jit); - jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown(); + std::unique_ptr<Jit> jit(new Jit(options)); if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) { return nullptr; } @@ -195,8 +188,6 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { if (jit->GetCodeCache() == nullptr) { return nullptr; } - jit->use_jit_compilation_ = options->UseJitCompilation(); - jit->profile_saver_options_ = options->GetProfileSaverOptions(); VLOG(jit) << "JIT created with initial_capacity=" << PrettySize(options->GetCodeCacheInitialCapacity()) << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity()) @@ -204,12 +195,6 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { << ", profile_saver_options=" << options->GetProfileSaverOptions(); - jit->hot_method_threshold_ = options->GetCompileThreshold(); - jit->warm_method_threshold_ = options->GetWarmupThreshold(); - jit->osr_method_threshold_ = options->GetOsrThreshold(); - jit->priority_thread_weight_ = options->GetPriorityThreadWeight(); - jit->invoke_transition_weight_ = options->GetInvokeTransitionWeight(); - jit->CreateThreadPool(); // Notify native debugger about the classes already loaded before the creation of the jit. @@ -330,7 +315,7 @@ void Jit::CreateThreadPool() { constexpr bool kJitPoolNeedsPeers = true; thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers)); - thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority); + thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority()); Start(); } @@ -360,8 +345,8 @@ void Jit::DeleteThreadPool() { void Jit::StartProfileSaver(const std::string& filename, const std::vector<std::string>& code_paths) { - if (profile_saver_options_.IsEnabled()) { - ProfileSaver::Start(profile_saver_options_, + if (options_->GetSaveProfilingInfo()) { + ProfileSaver::Start(options_->GetProfileSaverOptions(), filename, code_cache_.get(), code_paths); @@ -369,8 +354,8 @@ void Jit::StartProfileSaver(const std::string& filename, } void Jit::StopProfileSaver() { - if (profile_saver_options_.IsEnabled() && ProfileSaver::IsStarted()) { - ProfileSaver::Stop(dump_info_on_shutdown_); + if (options_->GetSaveProfilingInfo() && ProfileSaver::IsStarted()) { + ProfileSaver::Stop(options_->DumpJitInfoOnShutdown()); } } @@ -383,8 +368,8 @@ bool Jit::CanInvokeCompiledCode(ArtMethod* method) { } Jit::~Jit() { - DCHECK(!profile_saver_options_.IsEnabled() || !ProfileSaver::IsStarted()); - if (dump_info_on_shutdown_) { + DCHECK(!options_->GetSaveProfilingInfo() || !ProfileSaver::IsStarted()); + if (options_->DumpJitInfoOnShutdown()) { DumpInfo(LOG_STREAM(INFO)); Runtime::Current()->DumpDeoptimizations(LOG_STREAM(INFO)); } @@ -671,25 +656,25 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ if (IgnoreSamplesForMethod(method)) { return; } - if (hot_method_threshold_ == 0) { + if (HotMethodThreshold() == 0) { // Tests might request JIT on first use (compiled synchronously in the interpreter). return; } DCHECK(thread_pool_ != nullptr); - DCHECK_GT(warm_method_threshold_, 0); - DCHECK_GT(hot_method_threshold_, warm_method_threshold_); - DCHECK_GT(osr_method_threshold_, hot_method_threshold_); - DCHECK_GE(priority_thread_weight_, 1); - DCHECK_LE(priority_thread_weight_, hot_method_threshold_); + DCHECK_GT(WarmMethodThreshold(), 0); + DCHECK_GT(HotMethodThreshold(), WarmMethodThreshold()); + DCHECK_GT(OSRMethodThreshold(), HotMethodThreshold()); + DCHECK_GE(PriorityThreadWeight(), 1); + DCHECK_LE(PriorityThreadWeight(), HotMethodThreshold()); - int32_t starting_count = method->GetCounter(); + uint16_t starting_count = method->GetCounter(); if (Jit::ShouldUsePriorityThreadWeight(self)) { - count *= priority_thread_weight_; + count *= PriorityThreadWeight(); } - int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; + uint32_t new_count = starting_count + count; // Note: Native method have no "warm" state or profiling info. - if (LIKELY(!method->IsNative()) && starting_count < warm_method_threshold_) { - if ((new_count >= warm_method_threshold_) && + if (LIKELY(!method->IsNative()) && starting_count < WarmMethodThreshold()) { + if ((new_count >= WarmMethodThreshold()) && (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); if (success) { @@ -710,23 +695,23 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ } } // Avoid jumping more than one state at a time. - new_count = std::min(new_count, hot_method_threshold_ - 1); - } else if (use_jit_compilation_) { - if (starting_count < hot_method_threshold_) { - if ((new_count >= hot_method_threshold_) && + new_count = std::min(new_count, static_cast<uint32_t>(HotMethodThreshold() - 1)); + } else if (UseJitCompilation()) { + if (starting_count < HotMethodThreshold()) { + if ((new_count >= HotMethodThreshold()) && !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile)); } // Avoid jumping more than one state at a time. - new_count = std::min(new_count, osr_method_threshold_ - 1); - } else if (starting_count < osr_method_threshold_) { + new_count = std::min(new_count, static_cast<uint32_t>(OSRMethodThreshold() - 1)); + } else if (starting_count < OSRMethodThreshold()) { if (!with_backedges) { // If the samples don't contain any back edge, we don't increment the hotness. return; } DCHECK(!method->IsNative()); // No back edges reported for native methods. - if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) { + if ((new_count >= OSRMethodThreshold()) && !code_cache_->IsOsrCompiled(method)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 4b8b8919d1..edaf348cc4 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -44,6 +44,110 @@ class JitOptions; static constexpr int16_t kJitCheckForOSR = -1; static constexpr int16_t kJitHotnessDisabled = -2; +// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. +// See android/os/Process.java. +static constexpr int kJitPoolThreadPthreadDefaultPriority = 9; + +class JitOptions { + public: + static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options); + + uint16_t GetCompileThreshold() const { + return compile_threshold_; + } + + uint16_t GetWarmupThreshold() const { + return warmup_threshold_; + } + + uint16_t GetOsrThreshold() const { + return osr_threshold_; + } + + uint16_t GetPriorityThreadWeight() const { + return priority_thread_weight_; + } + + uint16_t GetInvokeTransitionWeight() const { + return invoke_transition_weight_; + } + + size_t GetCodeCacheInitialCapacity() const { + return code_cache_initial_capacity_; + } + + size_t GetCodeCacheMaxCapacity() const { + return code_cache_max_capacity_; + } + + bool DumpJitInfoOnShutdown() const { + return dump_info_on_shutdown_; + } + + const ProfileSaverOptions& GetProfileSaverOptions() const { + return profile_saver_options_; + } + + bool GetSaveProfilingInfo() const { + return profile_saver_options_.IsEnabled(); + } + + int GetThreadPoolPthreadPriority() const { + return thread_pool_pthread_priority_; + } + + bool UseJitCompilation() const { + return use_jit_compilation_; + } + + void SetUseJitCompilation(bool b) { + use_jit_compilation_ = b; + } + + void SetSaveProfilingInfo(bool save_profiling_info) { + profile_saver_options_.SetEnabled(save_profiling_info); + } + + void SetWaitForJitNotificationsToSaveProfile(bool value) { + profile_saver_options_.SetWaitForJitNotificationsToSave(value); + } + + void SetProfileAOTCode(bool value) { + profile_saver_options_.SetProfileAOTCode(value); + } + + void SetJitAtFirstUse() { + use_jit_compilation_ = true; + compile_threshold_ = 0; + } + + private: + bool use_jit_compilation_; + size_t code_cache_initial_capacity_; + size_t code_cache_max_capacity_; + uint16_t compile_threshold_; + uint16_t warmup_threshold_; + uint16_t osr_threshold_; + uint16_t priority_thread_weight_; + uint16_t invoke_transition_weight_; + bool dump_info_on_shutdown_; + int thread_pool_pthread_priority_; + ProfileSaverOptions profile_saver_options_; + + JitOptions() + : use_jit_compilation_(false), + code_cache_initial_capacity_(0), + code_cache_max_capacity_(0), + compile_threshold_(0), + warmup_threshold_(0), + osr_threshold_(0), + priority_thread_weight_(0), + invoke_transition_weight_(0), + dump_info_on_shutdown_(false), + thread_pool_pthread_priority_(kJitPoolThreadPthreadDefaultPriority) {} + + DISALLOW_COPY_AND_ASSIGN(JitOptions); +}; class Jit { public: @@ -77,29 +181,29 @@ class Jit { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - size_t OSRMethodThreshold() const { - return osr_method_threshold_; + uint16_t OSRMethodThreshold() const { + return options_->GetOsrThreshold(); } - size_t HotMethodThreshold() const { - return hot_method_threshold_; + uint16_t HotMethodThreshold() const { + return options_->GetCompileThreshold(); } - size_t WarmMethodThreshold() const { - return warm_method_threshold_; + uint16_t WarmMethodThreshold() const { + return options_->GetWarmupThreshold(); } uint16_t PriorityThreadWeight() const { - return priority_thread_weight_; + return options_->GetPriorityThreadWeight(); } // Returns false if we only need to save profile information and not compile methods. bool UseJitCompilation() const { - return use_jit_compilation_; + return options_->UseJitCompilation(); } bool GetSaveProfilingInfo() const { - return profile_saver_options_.IsEnabled(); + return options_->GetSaveProfilingInfo(); } // Wait until there is no more pending compilation tasks. @@ -120,12 +224,12 @@ class Jit { void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller) REQUIRES_SHARED(Locks::mutator_lock_) { - AddSamples(self, caller, invoke_transition_weight_, false); + AddSamples(self, caller, options_->GetInvokeTransitionWeight(), false); } void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee) REQUIRES_SHARED(Locks::mutator_lock_) { - AddSamples(self, callee, invoke_transition_weight_, false); + AddSamples(self, callee, options_->GetInvokeTransitionWeight(), false); } // Starts the profile saver if the config options allow profile recording. @@ -177,7 +281,7 @@ class Jit { void Start(); private: - Jit(); + explicit Jit(JitOptions* options); static bool LoadCompiler(std::string* error_msg); @@ -189,107 +293,22 @@ class Jit { static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool); static void (*jit_types_loaded_)(void*, mirror::Class**, size_t count); + // We make this static to simplify the interaction with libart-compiler.so. + static bool generate_debug_info_; + + const JitOptions* const options_; + + std::unique_ptr<jit::JitCodeCache> code_cache_; + std::unique_ptr<ThreadPool> thread_pool_; + // Performance monitoring. - bool dump_info_on_shutdown_; CumulativeLogger cumulative_timings_; Histogram<uint64_t> memory_use_ GUARDED_BY(lock_); Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - std::unique_ptr<jit::JitCodeCache> code_cache_; - - bool use_jit_compilation_; - ProfileSaverOptions profile_saver_options_; - static bool generate_debug_info_; - uint16_t hot_method_threshold_; - uint16_t warm_method_threshold_; - uint16_t osr_method_threshold_; - uint16_t priority_thread_weight_; - uint16_t invoke_transition_weight_; - std::unique_ptr<ThreadPool> thread_pool_; - DISALLOW_COPY_AND_ASSIGN(Jit); }; -class JitOptions { - public: - static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options); - size_t GetCompileThreshold() const { - return compile_threshold_; - } - size_t GetWarmupThreshold() const { - return warmup_threshold_; - } - size_t GetOsrThreshold() const { - return osr_threshold_; - } - uint16_t GetPriorityThreadWeight() const { - return priority_thread_weight_; - } - size_t GetInvokeTransitionWeight() const { - return invoke_transition_weight_; - } - size_t GetCodeCacheInitialCapacity() const { - return code_cache_initial_capacity_; - } - size_t GetCodeCacheMaxCapacity() const { - return code_cache_max_capacity_; - } - bool DumpJitInfoOnShutdown() const { - return dump_info_on_shutdown_; - } - const ProfileSaverOptions& GetProfileSaverOptions() const { - return profile_saver_options_; - } - bool GetSaveProfilingInfo() const { - return profile_saver_options_.IsEnabled(); - } - bool UseJitCompilation() const { - return use_jit_compilation_; - } - void SetUseJitCompilation(bool b) { - use_jit_compilation_ = b; - } - void SetSaveProfilingInfo(bool save_profiling_info) { - profile_saver_options_.SetEnabled(save_profiling_info); - } - void SetWaitForJitNotificationsToSaveProfile(bool value) { - profile_saver_options_.SetWaitForJitNotificationsToSave(value); - } - void SetProfileAOTCode(bool value) { - profile_saver_options_.SetProfileAOTCode(value); - } - - void SetJitAtFirstUse() { - use_jit_compilation_ = true; - compile_threshold_ = 0; - } - - private: - bool use_jit_compilation_; - size_t code_cache_initial_capacity_; - size_t code_cache_max_capacity_; - size_t compile_threshold_; - size_t warmup_threshold_; - size_t osr_threshold_; - uint16_t priority_thread_weight_; - size_t invoke_transition_weight_; - bool dump_info_on_shutdown_; - ProfileSaverOptions profile_saver_options_; - - JitOptions() - : use_jit_compilation_(false), - code_cache_initial_capacity_(0), - code_cache_max_capacity_(0), - compile_threshold_(0), - warmup_threshold_(0), - osr_threshold_(0), - priority_thread_weight_(0), - invoke_transition_weight_(0), - dump_info_on_shutdown_(false) {} - - DISALLOW_COPY_AND_ASSIGN(JitOptions); -}; - // Helper class to stop the JIT for a given scope. This will wait for the JIT to quiesce. class ScopedJitSuspend { public: diff --git a/runtime/jvalue-inl.h b/runtime/jvalue-inl.h index 25e34b2a74..5bd4f176f6 100644 --- a/runtime/jvalue-inl.h +++ b/runtime/jvalue-inl.h @@ -19,7 +19,7 @@ #include "jvalue.h" -#include "obj_ptr.h" +#include "obj_ptr-inl.h" namespace art { diff --git a/runtime/jvalue.h b/runtime/jvalue.h index 266abcf399..b42d995d5c 100644 --- a/runtime/jvalue.h +++ b/runtime/jvalue.h @@ -33,7 +33,7 @@ union PACKED(alignof(mirror::Object*)) JValue { // We default initialize JValue instances to all-zeros. JValue() : j(0) {} - template<typename T> static JValue FromPrimitive(T v); + template<typename T> ALWAYS_INLINE static JValue FromPrimitive(T v); int8_t GetB() const { return b; } void SetB(int8_t new_b) { @@ -62,6 +62,7 @@ union PACKED(alignof(mirror::Object*)) JValue { mirror::Object* GetL() const REQUIRES_SHARED(Locks::mutator_lock_) { return l; } + ALWAYS_INLINE void SetL(ObjPtr<mirror::Object> new_l) REQUIRES_SHARED(Locks::mutator_lock_); int16_t GetS() const { return s; } diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h index 41c8384e0d..00a8c00880 100644 --- a/runtime/method_handles-inl.h +++ b/runtime/method_handles-inl.h @@ -22,7 +22,7 @@ #include "common_throws.h" #include "dex/dex_instruction.h" #include "interpreter/interpreter_common.h" -#include "jvalue.h" +#include "jvalue-inl.h" #include "mirror/class.h" #include "mirror/method_type.h" #include "mirror/object.h" diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 64ab78997f..1d45aaeb2e 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -20,7 +20,6 @@ #include "common_dex_operations.h" #include "jvalue-inl.h" -#include "jvalue.h" #include "mirror/emulated_stack_frame.h" #include "mirror/method_handle_impl-inl.h" #include "mirror/method_type.h" diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index e0afbee845..25599843e9 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -27,6 +27,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" #include "jni/jni_internal.h" +#include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" #include "native_util.h" diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 3aa481af8c..7383d477bb 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -194,6 +194,9 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xjittransitionweight:_") .WithType<unsigned int>() .IntoKey(M::JITInvokeTransitionWeight) + .Define("-Xjitpthreadpriority:_") + .WithType<int>() + .IntoKey(M::JITPoolThreadPthreadPriority) .Define("-Xjitsaveprofilinginfo") .WithType<ProfileSaverOptions>() .AppendValues() diff --git a/runtime/reflection.cc b/runtime/reflection.cc index dfa4b3daab..66eba1e1d4 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -25,6 +25,7 @@ #include "indirect_reference_table-inl.h" #include "jni/java_vm_ext.h" #include "jni/jni_internal.h" +#include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/executable.h" #include "mirror/object_array-inl.h" diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 427385d914..e647423b9c 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -77,6 +77,7 @@ RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITPriorityThreadWeight) RUNTIME_OPTIONS_KEY (unsigned int, JITInvokeTransitionWeight) +RUNTIME_OPTIONS_KEY (int, JITPoolThreadPthreadPriority, jit::kJitPoolThreadPthreadDefaultPriority) RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheInitialCapacity, jit::JitCodeCache::kInitialCapacity) RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheMaxCapacity, jit::JitCodeCache::kMaxCapacity) RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc index d71745e531..e6730c6114 100644 --- a/runtime/var_handles.cc +++ b/runtime/var_handles.cc @@ -25,6 +25,39 @@ namespace art { +namespace { + +bool VarHandleInvokeAccessorWithConversions(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::VarHandle> var_handle, + Handle<mirror::MethodType> callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle<mirror::MethodType> accessor_type(hs.NewHandle( + var_handle->GetMethodTypeForAccessMode(self, access_mode))); + const size_t num_vregs = accessor_type->NumberOfVRegs(); + const int num_params = accessor_type->GetPTypes()->GetLength(); + ShadowFrameAllocaUniquePtr accessor_frame = + CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); + ShadowFrameGetter getter(shadow_frame, operands); + static const uint32_t kFirstDestinationReg = 0; + ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); + if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { + return false; + } + RangeInstructionOperands accessor_operands(kFirstDestinationReg, + kFirstDestinationReg + num_vregs); + if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { + return false; + } + return ConvertReturnValue(callsite_type, accessor_type, result); +} + +} // namespace + bool VarHandleInvokeAccessor(Thread* self, ShadowFrame& shadow_frame, Handle<mirror::VarHandle> var_handle, @@ -44,35 +77,22 @@ bool VarHandleInvokeAccessor(Thread* self, mirror::VarHandle::MatchKind match_kind = var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type.Get()); - if (match_kind == mirror::VarHandle::MatchKind::kExact) { + if (LIKELY(match_kind == mirror::VarHandle::MatchKind::kExact)) { return var_handle->Access(access_mode, &shadow_frame, operands, result); - } - if (match_kind == mirror::VarHandle::MatchKind::kNone) { + } else if (match_kind == mirror::VarHandle::MatchKind::kWithConversions) { + return VarHandleInvokeAccessorWithConversions(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + operands, + result); + } else { + DCHECK_EQ(match_kind, mirror::VarHandle::MatchKind::kNone); ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), callsite_type.Get()); return false; } - DCHECK_EQ(mirror::VarHandle::MatchKind::kWithConversions, match_kind); - - StackHandleScope<1> hs(self); - Handle<mirror::MethodType> accessor_type(hs.NewHandle( - var_handle->GetMethodTypeForAccessMode(self, access_mode))); - const size_t num_vregs = accessor_type->NumberOfVRegs(); - const int num_params = accessor_type->GetPTypes()->GetLength(); - ShadowFrameAllocaUniquePtr accessor_frame = - CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); - ShadowFrameGetter getter(shadow_frame, operands); - static const uint32_t kFirstDestinationReg = 0; - ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); - if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { - return false; - } - RangeInstructionOperands accessor_operands(kFirstDestinationReg, - kFirstDestinationReg + num_vregs); - if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { - return false; - } - return ConvertReturnValue(callsite_type, accessor_type, result); } } // namespace art diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali index adafb78bd7..8898c48ea1 100644 --- a/test/563-checker-fakestring/smali/TestCase.smali +++ b/test/563-checker-fakestring/smali/TestCase.smali @@ -133,17 +133,8 @@ .end method -# Test that the compiler does not assume that the first argument of String.<init> -# is a NewInstance by inserting an irreducible loop between them (b/26676472). - -# We verify the type of the input instruction (Phi) in debuggable mode, because -# it is eliminated by later stages of SsaBuilder otherwise. - -## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance1(byte[], boolean) register (after) -## CHECK-DAG: InvokeStaticOrDirect env:[[<<Phi:l\d+>>,{{.*]]}} -## CHECK-DAG: <<Phi>> Phi - -.method public static thisNotNewInstance1([BZ)Ljava/lang/String; +# Test #1 for irreducible loops and String.<init>. +.method public static irreducibleLoopAndStringInit1([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; @@ -164,11 +155,8 @@ .end method -## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance2(byte[], boolean) register (after) -## CHECK-DAG: InvokeStaticOrDirect env:[[<<Phi:l\d+>>,{{.*]]}} -## CHECK-DAG: <<Phi>> Phi - -.method public static thisNotNewInstance2([BZ)Ljava/lang/String; +# Test #2 for irreducible loops and String.<init>. +.method public static irreducibleLoopAndStringInit2([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; @@ -188,3 +176,26 @@ return-object v0 .end method + +# Test #3 for irreducible loops and String.<init> alias. +.method public static irreducibleLoopAndStringInit3([BZ)Ljava/lang/String; + .registers 5 + + new-instance v0, Ljava/lang/String; + move-object v2, v0 + + # Irreducible loop + if-eqz p1, :loop_entry + :loop_header + const v1, 0x1 + xor-int p1, p1, v1 + :loop_entry + if-eqz p1, :string_init + goto :loop_header + + :string_init + const-string v1, "UTF8" + invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V + return-object v2 + +.end method diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java index 78cb37ae09..d38b7f4f23 100644 --- a/test/563-checker-fakestring/src/Main.java +++ b/test/563-checker-fakestring/src/Main.java @@ -65,14 +65,21 @@ public class Main { } { - Method m = c.getMethod("thisNotNewInstance1", byte[].class, boolean.class); + Method m = c.getMethod("irreducibleLoopAndStringInit1", byte[].class, boolean.class); String result = (String) m.invoke(null, new Object[] { testData, true }); assertEqual(testString, result); result = (String) m.invoke(null, new Object[] { testData, false }); assertEqual(testString, result); } { - Method m = c.getMethod("thisNotNewInstance2", byte[].class, boolean.class); + Method m = c.getMethod("irreducibleLoopAndStringInit2", byte[].class, boolean.class); + String result = (String) m.invoke(null, new Object[] { testData, true }); + assertEqual(testString, result); + result = (String) m.invoke(null, new Object[] { testData, false }); + assertEqual(testString, result); + } + { + Method m = c.getMethod("irreducibleLoopAndStringInit3", byte[].class, boolean.class); String result = (String) m.invoke(null, new Object[] { testData, true }); assertEqual(testString, result); result = (String) m.invoke(null, new Object[] { testData, false }); diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index c4aebc6f56..0b08beed70 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -63,6 +63,8 @@ class DexAnalyze { run_all_experiments_ = true; } else if (arg == "-count-indices") { exp_count_indices_ = true; + } else if (arg == "-analyze-strings") { + exp_analyze_strings_ = true; } else if (arg == "-d") { dump_per_input_dex_ = true; } else if (!arg.empty() && arg[0] == '-') { @@ -82,6 +84,7 @@ class DexAnalyze { bool run_dex_file_verifier_ = true; bool dump_per_input_dex_ = false; bool exp_count_indices_ = false; + bool exp_analyze_strings_ = false; bool run_all_experiments_ = false; std::vector<std::string> filenames_; }; @@ -92,25 +95,30 @@ class DexAnalyze { if (options->run_all_experiments_ || options->exp_count_indices_) { experiments_.emplace_back(new CountDexIndices); } + if (options->run_all_experiments_ || options->exp_analyze_strings_) { + experiments_.emplace_back(new AnalyzeStrings); + } } bool ProcessDexFile(const DexFile& dex_file) { for (std::unique_ptr<Experiment>& experiment : experiments_) { experiment->ProcessDexFile(dex_file); } + total_size_ += dex_file.Size(); ++dex_count_; return true; } void Dump(std::ostream& os) { for (std::unique_ptr<Experiment>& experiment : experiments_) { - experiment->Dump(os); + experiment->Dump(os, total_size_); } } const Options* const options_; std::vector<std::unique_ptr<Experiment>> experiments_; size_t dex_count_ = 0; + uint64_t total_size_ = 0u; }; public: diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index e1f119df59..bfeb4b9d72 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -15,12 +15,91 @@ */ #include "dexanalyze_experiments.h" + +#include <stdint.h> +#include <inttypes.h> +#include <iostream> +#include <map> +#include <vector> + +#include "android-base/stringprintf.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_instruction-inl.h" #include "dex/standard_dex_file.h" namespace art { +std::string Percent(uint64_t value, uint64_t max) { + if (max == 0) { + ++max; + } + return android::base::StringPrintf("%" PRId64 "(%.2f%%)", + value, + static_cast<double>(value * 100) / static_cast<double>(max)); +} + +static size_t PrefixLen(const std::string& a, const std::string& b) { + size_t len = 0; + for (; len < a.length() && len < b.length() && a[len] == b[len]; ++len) {} + return len; +} + +void AnalyzeStrings::ProcessDexFile(const DexFile& dex_file) { + std::vector<std::string> strings; + for (size_t i = 0; i < dex_file.NumStringIds(); ++i) { + uint32_t length = 0; + const char* data = + dex_file.GetStringDataAndUtf16Length(dex_file.GetStringId(dex::StringIndex(i)), &length); + strings.push_back(data); + } + // Note that the strings are probably already sorted. + std::sort(strings.begin(), strings.end()); + + // Tunable parameters. + static const size_t kMinPrefixLen = 3; + static const size_t kPrefixConstantCost = 5; + static const size_t kPrefixIndexCost = 2; + + // Calculate total shared prefix. + std::vector<size_t> shared_len; + std::set<std::string> prefixes; + for (size_t i = 0; i < strings.size(); ++i) { + size_t best_len = 0; + if (i > 0) { + best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1])); + } + if (i < strings.size() - 1) { + best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1])); + } + std::string prefix; + if (best_len >= kMinPrefixLen) { + prefix = strings[i].substr(0, best_len); + prefixes.insert(prefix); + total_prefix_savings_ += prefix.length(); + } + total_prefix_index_cost_ += kPrefixIndexCost; + } + total_num_prefixes_ += prefixes.size(); + for (const std::string& s : prefixes) { + // 4 bytes for an offset, one for length. + total_prefix_dict_ += s.length(); + total_prefix_table_ += kPrefixConstantCost; + } +} + +void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const { + os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n"; + os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n"; + os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n"; + os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n"; + int64_t net_savings = total_prefix_savings_; + net_savings -= total_prefix_dict_; + net_savings -= total_prefix_table_; + net_savings -= total_prefix_index_cost_; + os << "Prefix net savings " << Percent(net_savings, total_size) << "\n"; + os << "Prefix dictionary elements " << total_num_prefixes_ << "\n"; +} + void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { num_string_ids_ += dex_file.NumStringIds(); num_method_ids_ += dex_file.NumMethodIds(); @@ -107,7 +186,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { } } -void CountDexIndices::Dump(std::ostream& os) const { +void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const { os << "Num string ids: " << num_string_ids_ << "\n"; os << "Num method ids: " << num_method_ids_ << "\n"; os << "Num field ids: " << num_field_ids_ << "\n"; @@ -127,6 +206,7 @@ void CountDexIndices::Dump(std::ostream& os) const { os << "Same class invoke: " << same_class_total << "\n"; os << "Other class invoke: " << other_class_total << "\n"; os << "Invokes from code: " << (same_class_total + other_class_total) << "\n"; + os << "Total dex size: " << total_size << "\n"; } } // namespace art diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h index 5d0f51b821..6f70f5d166 100644 --- a/tools/dexanalyze/dexanalyze_experiments.h +++ b/tools/dexanalyze/dexanalyze_experiments.h @@ -24,12 +24,28 @@ namespace art { class DexFile; +std::string Percent(uint64_t value, uint64_t max); + // An experiment a stateful visitor that runs on dex files. Results are cumulative. class Experiment { public: virtual ~Experiment() {} virtual void ProcessDexFile(const DexFile& dex_file) = 0; - virtual void Dump(std::ostream& os) const = 0; + virtual void Dump(std::ostream& os, uint64_t total_size) const = 0; +}; + +// Analyze string data and strings accessed from code. +class AnalyzeStrings : public Experiment { + public: + void ProcessDexFile(const DexFile& dex_file); + void Dump(std::ostream& os, uint64_t total_size) const; + + private: + int64_t total_prefix_savings_ = 0u; + int64_t total_prefix_dict_ = 0u; + int64_t total_prefix_table_ = 0u; + int64_t total_prefix_index_cost_ = 0u; + int64_t total_num_prefixes_ = 0u; }; // Count numbers of dex indices. @@ -37,7 +53,7 @@ class CountDexIndices : public Experiment { public: void ProcessDexFile(const DexFile& dex_file); - void Dump(std::ostream& os) const; + void Dump(std::ostream& os, uint64_t total_size) const; private: // Total string ids loaded from dex code. @@ -65,4 +81,3 @@ class CountDexIndices : public Experiment { } // namespace art #endif // ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_ - diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc index 952fba8720..428304e517 100644 --- a/tools/ti-fast/tifast.cc +++ b/tools/ti-fast/tifast.cc @@ -62,7 +62,7 @@ static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) { #define FOR_ALL_SUPPORTED_EVENTS(fun) \ fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation)) \ fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID)) \ - fun(MethodExit, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \ + fun(MethodExit, EVENT(METHOD_EXIT), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \ fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, void*, void**)) \ fun(Exception, EVENT(EXCEPTION), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject, jmethodID, jlocation)) \ fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject)) \ |