OSDN Git Service

Stop interpreter from accessing code items of compiled code.
authorJeff Hao <jeffhao@google.com>
Mon, 27 Feb 2017 22:47:06 +0000 (14:47 -0800)
committerJeff Hao <jeffhao@google.com>
Thu, 9 Mar 2017 00:36:13 +0000 (00:36 +0000)
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

runtime/common_dex_operations.h
runtime/interpreter/interpreter.cc
runtime/interpreter/interpreter_common.cc
runtime/interpreter/interpreter_common.h

index 6693eef..8776061 100644 (file)
@@ -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);
index 1b3d339..b03ffc9 100644 (file)
@@ -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);
 
index 8978bfd..f47db16 100644 (file)
@@ -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<mirror::Class> 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:
   //
index 6b22af9..5d6cac9 100644 (file)
@@ -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