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.
Includes fixes for JIT and deopt.
For Maps:
Systrace vdex memory usage: 19.4/33.4MB -> 18.8/33.4MB
Dumpsys meminfo vdex PSS: 9371kB -> 9275kB
Bug: 35800981
Test: mm test-art-host
Change-Id: I3bf17f8866287d9a8f127c16da23bebb801456dc
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index 6693eef..133ddb0 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -36,8 +36,8 @@
void ArtInterpreterToCompiledCodeBridge(Thread* self,
ArtMethod* caller,
- const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame,
+ uint16_t arg_offset,
JValue* result);
} // namespace interpreter
@@ -46,17 +46,15 @@
ArtMethod* caller_method,
const size_t first_dest_reg,
ShadowFrame* callee_frame,
- JValue* result)
+ JValue* result,
+ bool use_interpreter_entrypoint)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (LIKELY(Runtime::Current()->IsStarted())) {
- ArtMethod* target = callee_frame->GetMethod();
- if (ClassLinker::ShouldUseInterpreterEntrypoint(
- target,
- target->GetEntryPointFromQuickCompiledCode())) {
+ if (use_interpreter_entrypoint) {
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 bf49e84..d2f5232 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -264,7 +264,11 @@
// 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.
+ // It's ok to access the code item here since JIT code will have been touched by the
+ // interpreter and compiler already.
+ uint16_t arg_offset = 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 ef0ddb3..084cb42 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -458,8 +458,8 @@
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();
@@ -482,9 +482,17 @@
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
+ // difficult since the frame may contain space for all the registers in the method, or only enough
+ // space for the arguments.
+ if (kIsDebugBuild) {
+ if (method->GetCodeItem() == nullptr) {
+ DCHECK_EQ(0u, arg_offset) << method->PrettyMethod();
+ } else {
+ DCHECK_LE(arg_offset, shadow_frame->NumberOfVRegs());
+ }
+ }
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr && caller != nullptr) {
jit->NotifyInterpreterToCompiledCodeTransition(self, caller);
@@ -918,12 +926,23 @@
// Compute method information.
const DexFile::CodeItem* code_item = called_method->GetCodeItem();
-
// Number of registers for the callee's call frame.
uint16_t num_regs;
+ // Test whether to use the interpreter or compiler entrypoint, and save that result to pass to
+ // PerformCall. A deoptimization could occur at any time, and we shouldn't change which
+ // entrypoint to use once we start building the shadow frame.
+ bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
+ called_method, called_method->GetEntryPointFromQuickCompiledCode());
if (LIKELY(code_item != nullptr)) {
- num_regs = code_item->registers_size_;
- DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, code_item->ins_size_);
+ // When transitioning to compiled code, space only needs to be reserved for the input registers.
+ // The rest of the frame gets discarded. This also prevents accessing the called method's code
+ // item, saving memory by keeping code items of compiled code untouched.
+ if (Runtime::Current()->IsStarted() && !use_interpreter_entrypoint) {
+ num_regs = number_of_inputs;
+ } else {
+ 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());
num_regs = number_of_inputs;
@@ -1077,7 +1096,13 @@
self->EndAssertNoThreadSuspension(old_cause);
}
- PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
+ PerformCall(self,
+ code_item,
+ shadow_frame.GetMethod(),
+ first_dest_reg,
+ new_shadow_frame,
+ result,
+ use_interpreter_entrypoint);
if (string_init && !self->IsExceptionPending()) {
SetStringInitValueToAllAliases(&shadow_frame, string_init_vreg_this, *result);
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index fdc0505..38edc7a 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -527,10 +527,11 @@
}
}
+// 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
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 54d45b1..090bac1 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -514,7 +514,15 @@
}
}
- PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
+ bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
+ called_method, called_method->GetEntryPointFromQuickCompiledCode());
+ PerformCall(self,
+ code_item,
+ shadow_frame.GetMethod(),
+ first_dest_reg,
+ new_shadow_frame,
+ result,
+ use_interpreter_entrypoint);
if (self->IsExceptionPending()) {
return false;
}
@@ -602,12 +610,15 @@
new_shadow_frame->SetVRegReference(0, receiver.Get());
new_shadow_frame->SetVRegReference(1, sf.Get());
+ bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
+ called_method, called_method->GetEntryPointFromQuickCompiledCode());
PerformCall(self,
code_item,
shadow_frame.GetMethod(),
0 /* first destination register */,
new_shadow_frame,
- result);
+ result,
+ use_interpreter_entrypoint);
if (self->IsExceptionPending()) {
return false;
}
@@ -1091,7 +1102,15 @@
num_input_regs);
self->EndAssertNoThreadSuspension(old_cause);
- PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
+ bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
+ called_method, called_method->GetEntryPointFromQuickCompiledCode());
+ PerformCall(self,
+ code_item,
+ shadow_frame.GetMethod(),
+ first_dest_reg,
+ new_shadow_frame,
+ result,
+ use_interpreter_entrypoint);
if (self->IsExceptionPending()) {
return false;
}