Improve interpreter to interpreter invokes.
The interpreter constructs a shadow frame instead of arg array to make
interpreter to interpreter transitions faster. This adds a pointer to
an entry for the interpreter to each method.
Change-Id: If48911d3aa3470847b8548a9e92090b829f4f254
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index b82c632..9f48e78 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -42,6 +42,7 @@
using namespace art::mirror;
namespace art {
+
namespace interpreter {
static const int32_t kMaxInt = std::numeric_limits<int32_t>::max();
@@ -50,13 +51,13 @@
static const int64_t kMinLong = std::numeric_limits<int64_t>::min();
static void UnstartedRuntimeInvoke(Thread* self, AbstractMethod* target_method,
- Object* receiver, uint32_t* args, JValue* result)
+ 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));
if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") {
- std::string descriptor(DotToDescriptor(reinterpret_cast<Object*>(args[0])->AsString()->ToModifiedUtf8().c_str()));
+ std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str()));
ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
class_loader);
@@ -64,7 +65,7 @@
<< PrettyDescriptor(descriptor);
result->SetL(found);
} else if (name == "java.lang.Object java.lang.Class.newInstance()") {
- Class* klass = receiver->AsClass();
+ Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
AbstractMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V");
CHECK(c != NULL);
Object* obj = klass->AllocObject(self);
@@ -74,8 +75,8 @@
} else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") {
// Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
// going the reflective Dex way.
- Class* klass = receiver->AsClass();
- String* name = reinterpret_cast<Object*>(args[0])->AsString();
+ Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
+ String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
Field* found = NULL;
FieldHelper fh;
ObjectArray<Field>* fields = klass->GetIFields();
@@ -104,25 +105,25 @@
result->SetL(found);
} else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") {
// Special case array copying without initializing System.
- Class* ctype = reinterpret_cast<Object*>(args[0])->GetClass()->GetComponentType();
- jint srcPos = args[1];
- jint dstPos = args[3];
- jint length = args[4];
+ Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType();
+ jint srcPos = shadow_frame->GetVReg(arg_offset + 1);
+ jint dstPos = shadow_frame->GetVReg(arg_offset + 3);
+ jint length = shadow_frame->GetVReg(arg_offset + 4);
if (!ctype->IsPrimitive()) {
- ObjectArray<Object>* src = reinterpret_cast<Object*>(args[0])->AsObjectArray<Object>();
- ObjectArray<Object>* dst = reinterpret_cast<Object*>(args[2])->AsObjectArray<Object>();
+ ObjectArray<Object>* src = shadow_frame->GetVRegReference(arg_offset)->AsObjectArray<Object>();
+ ObjectArray<Object>* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray<Object>();
for (jint i = 0; i < length; ++i) {
dst->Set(dstPos + i, src->Get(srcPos + i));
}
} else if (ctype->IsPrimitiveChar()) {
- CharArray* src = reinterpret_cast<Object*>(args[0])->AsCharArray();
- CharArray* dst = reinterpret_cast<Object*>(args[2])->AsCharArray();
+ CharArray* src = shadow_frame->GetVRegReference(arg_offset)->AsCharArray();
+ CharArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray();
for (jint i = 0; i < length; ++i) {
dst->Set(dstPos + i, src->Get(srcPos + i));
}
} else if (ctype->IsPrimitiveInt()) {
- IntArray* src = reinterpret_cast<Object*>(args[0])->AsIntArray();
- IntArray* dst = reinterpret_cast<Object*>(args[2])->AsIntArray();
+ IntArray* src = shadow_frame->GetVRegReference(arg_offset)->AsIntArray();
+ IntArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsIntArray();
for (jint i = 0; i < length; ++i) {
dst->Set(dstPos + i, src->Get(srcPos + i));
}
@@ -131,7 +132,7 @@
}
} else {
// Not special, continue with regular interpreter execution.
- EnterInterpreterFromInvoke(self, target_method, receiver, args, result);
+ result->SetJ(EnterInterpreterFromInterpreter(self, shadow_frame).GetJ());
}
}
@@ -402,21 +403,65 @@
return;
}
mh.ChangeMethod(target_method);
- ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
- if (is_range) {
- arg_array.BuildArgArray(shadow_frame, receiver, dec_insn.vC + (type != kStatic ? 1 : 0));
+
+ 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()) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;",
+ "abstract method \"%s\"", PrettyMethod(target_method).c_str());
+ return;
} else {
- arg_array.BuildArgArray(shadow_frame, receiver, dec_insn.arg + (type != kStatic ? 1 : 0));
- }
- if (LIKELY(Runtime::Current()->IsStarted())) {
- target_method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result,
- mh.GetShorty()[0]);
- } else {
- uint32_t* args = arg_array.GetArray();
- if (type != kStatic) {
- args++;
+ DCHECK(target_method->IsNative() || target_method->IsProxyMethod());
+ num_regs = num_ins = AbstractMethod::NumArgRegisters(mh.GetShorty());
+ if (!target_method->IsStatic()) {
+ num_regs++;
+ num_ins++;
}
- UnstartedRuntimeInvoke(self, target_method, receiver, args, result);
+ }
+
+ Runtime* runtime = Runtime::Current();
+ UniquePtr<ShadowFrame> new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame,
+ target_method, 0));
+ 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();
+ 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 ? dec_insn.vC + arg_offset : dec_insn.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->IsStarted())) {
+ result->SetJ((target_method->GetEntryPointFromInterpreter())(self, new_shadow_frame.get()).GetJ());
+ } else {
+ UnstartedRuntimeInvoke(self, target_method, new_shadow_frame.get(), result, num_regs - num_ins);
}
mh.ChangeMethod(shadow_frame.GetMethod());
}
@@ -1925,5 +1970,42 @@
return Execute(self, mh, code_item, shadow_frame, JValue());
}
+JValue EnterInterpreterFromInterpreter(Thread* self, ShadowFrame* shadow_frame)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (__builtin_frame_address(0) < self->GetStackEnd()) {
+ ThrowStackOverflowError(self);
+ return JValue();
+ }
+
+ AbstractMethod* method = shadow_frame->GetMethod();
+ if (method->IsStatic() && !method->GetDeclaringClass()->IsInitializing()) {
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(),
+ true, true)) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return JValue();
+ }
+ CHECK(method->GetDeclaringClass()->IsInitializing());
+ }
+
+ self->PushShadowFrame(shadow_frame);
+
+ MethodHelper mh(method);
+ const DexFile::CodeItem* code_item = mh.GetCodeItem();
+ JValue result;
+ if (LIKELY(!method->IsNative())) {
+ result = Execute(self, mh, code_item, *shadow_frame, JValue());
+ } else {
+ // We don't expect to be asked to interpret native code (which is entered via a JNI compiler
+ // generated stub) except during testing and image writing.
+ CHECK(!Runtime::Current()->IsStarted());
+ Object* receiver = method->IsStatic() ? NULL : shadow_frame->GetVRegReference(0);
+ uint32_t* args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
+ UnstartedRuntimeJni(self, method, receiver, args, &result);
+ }
+
+ self->PopShadowFrame();
+ return result;
+}
+
} // namespace interpreter
} // namespace art
diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h
index cf47b68..94fdff1 100644
--- a/src/interpreter/interpreter.h
+++ b/src/interpreter/interpreter.h
@@ -47,6 +47,9 @@
ShadowFrame& shadow_frame)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+extern JValue EnterInterpreterFromInterpreter(Thread* self, ShadowFrame* shadow_frame)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
} // namespace interpreter
} // namespace art