From: Jeff Hao Date: Mon, 27 Feb 2017 22:47:06 +0000 (-0800) Subject: Stop interpreter from accessing code items of compiled code. X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=df79ddb545f0d6e71d6eebb9cb94aa6916351ee9;p=android-x86%2Fart.git Stop interpreter from accessing code items of compiled code. The ArtInterpreterToCompiledCodeBridge accesses the code item in a number of places to handle argument marshalling. However, the code item of a compiled method should have no need to be accessed by the runtime at all, since the code has been compiled. By removing these accesses, there is a drop in the memory footprint of the dex file, since these code items remain untouched by the runtime. Bug: 35800981 Test: mm test-art-host Change-Id: Ib7d29c17e80b1690aa819d083f5b12739492ebd6 --- diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 6693eefa5..877606143 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -36,8 +36,8 @@ namespace interpreter { void ArtInterpreterToCompiledCodeBridge(Thread* self, ArtMethod* caller, - const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, + uint16_t arg_offset, JValue* result); } // namespace interpreter @@ -56,7 +56,7 @@ inline void PerformCall(Thread* self, interpreter::ArtInterpreterToInterpreterBridge(self, code_item, callee_frame, result); } else { interpreter::ArtInterpreterToCompiledCodeBridge( - self, caller_method, code_item, callee_frame, result); + self, caller_method, callee_frame, first_dest_reg, result); } } else { interpreter::UnstartedRuntime::Invoke(self, code_item, callee_frame, result, first_dest_reg); diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 1b3d339f3..b03ffc95e 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -270,7 +270,12 @@ static inline JValue Execute( // Pop the shadow frame before calling into compiled code. self->PopShadowFrame(); - ArtInterpreterToCompiledCodeBridge(self, nullptr, code_item, &shadow_frame, &result); + // Calculate the offset of the first input reg. The input registers are in the high regs. + // If there is no code item, all the registers are inputs. + uint16_t arg_offset = (code_item == nullptr) + ? 0 + : code_item->registers_size_ - code_item->ins_size_; + ArtInterpreterToCompiledCodeBridge(self, nullptr, &shadow_frame, arg_offset, &result); // Push the shadow frame back as the caller will expect it. self->PushShadowFrame(&shadow_frame); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 8978bfd5a..f47db16bf 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -459,8 +459,8 @@ ALWAYS_INLINE void CopyRegisters(ShadowFrame& caller_frame, void ArtInterpreterToCompiledCodeBridge(Thread* self, ArtMethod* caller, - const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, + uint16_t arg_offset, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* method = shadow_frame->GetMethod(); @@ -471,21 +471,25 @@ void ArtInterpreterToCompiledCodeBridge(Thread* self, self->PushShadowFrame(shadow_frame); StackHandleScope<1> hs(self); Handle h_class(hs.NewHandle(declaringClass)); - if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, - true))) { + if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized( + self, h_class, true, true))) { self->PopShadowFrame(); DCHECK(self->IsExceptionPending()); return; } self->PopShadowFrame(); CHECK(h_class->IsInitializing()); - // Reload from shadow frame in case the method moved, this is faster than adding a handle. - method = shadow_frame->GetMethod(); } } - uint16_t arg_offset = (code_item == nullptr) - ? 0 - : code_item->registers_size_ - code_item->ins_size_; + // Basic checks for the arg_offset. If there's no code item, the arg_offset must be 0. Otherwise, + // check that the arg_offset isn't greater than the number of registers. A stronger check is hard + // to do because the method being called may have been replaced if it was a string init method, + // and the arguments are handled differently in that case. + if (method->GetCodeItem() == nullptr) { + DCHECK_EQ(0u, arg_offset); + } else { + DCHECK_LE(arg_offset, shadow_frame->NumberOfVRegs()); + } jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr && caller != nullptr) { jit->NotifyInterpreterToCompiledCodeTransition(self, caller); @@ -922,13 +926,24 @@ static inline bool DoCallCommon(ArtMethod* called_method, // Number of registers for the callee's call frame. uint16_t num_regs; - if (LIKELY(code_item != nullptr)) { + // When transitioning to compiled code, the frame only contains input registers. + // We can avoid accessing compiled code items here so they remain untouched during runtime, + // saving memory since they never get paged in. + bool use_compiler_entrypoint = Runtime::Current()->IsStarted() && + !ClassLinker::ShouldUseInterpreterEntrypoint( + called_method, called_method->GetEntryPointFromQuickCompiledCode()); + if (LIKELY(code_item != nullptr && !use_compiler_entrypoint)) { num_regs = code_item->registers_size_; - DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, code_item->ins_size_); } else { - DCHECK(called_method->IsNative() || called_method->IsProxyMethod()); + DCHECK(called_method->IsNative() || called_method->IsProxyMethod() || use_compiler_entrypoint); num_regs = number_of_inputs; } + // Check that the number of inputs passed in agrees with the code item. If a string initializer + // is called, it will have been replaced by an equivalent StringFactory call above, and they + // have one fewer input (no this pointer). + if (code_item != nullptr) { + DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, code_item->ins_size_); + } // Hack for String init: // diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 6b22af982..5d6cac992 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -481,10 +481,11 @@ static inline void AssignRegister(ShadowFrame* new_shadow_frame, const ShadowFra } } +// The arg_offset is the offset to the first input register in the frame. void ArtInterpreterToCompiledCodeBridge(Thread* self, ArtMethod* caller, - const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, + uint16_t arg_offset, JValue* result); // Set string value created from StringFactory.newStringFromXXX() into all aliases of