Refactor runtime support.
Change-Id: Id7470a4105838150d5ceb73ab2c8c83e739660df
diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc
new file mode 100644
index 0000000..cb224e8
--- /dev/null
+++ b/src/oat/runtime/support_stubs.cc
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "callee_save_frame.h"
+#include "dex_instruction.h"
+#include "object.h"
+#include "object_utils.h"
+
+// Architecture specific assembler helper to deliver exception.
+extern "C" void art_deliver_exception_from_code(void*);
+
+namespace art {
+
+// Lazily resolve a method. Called by stub code.
+const void* UnresolvedDirectMethodTrampolineFromCode(Method* called, Method** sp, Thread* thread,
+ Runtime::TrampolineType type) {
+ // TODO: this code is specific to ARM
+ // On entry the stack pointed by sp is:
+ // | argN | |
+ // | ... | |
+ // | arg4 | |
+ // | arg3 spill | | Caller's frame
+ // | arg2 spill | |
+ // | arg1 spill | |
+ // | Method* | ---
+ // | LR |
+ // | ... | callee saves
+ // | R3 | arg3
+ // | R2 | arg2
+ // | R1 | arg1
+ // | R0 |
+ // | Method* | <- sp
+ uintptr_t* regs = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp) + kPointerSize);
+ DCHECK_EQ(48U, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
+ Method** caller_sp = reinterpret_cast<Method**>(reinterpret_cast<byte*>(sp) + 48);
+ uintptr_t caller_pc = regs[10];
+ FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsAndArgs);
+ // Start new JNI local reference state
+ JNIEnvExt* env = thread->GetJniEnv();
+ ScopedJniEnvLocalRefState env_state(env);
+
+ // Compute details about the called method (avoid GCs)
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ Method* caller = *caller_sp;
+ bool is_static;
+ bool is_virtual;
+ uint32_t dex_method_idx;
+ const char* shorty;
+ uint32_t shorty_len;
+ if (type == Runtime::kUnknownMethod) {
+ DCHECK(called->IsRuntimeMethod());
+ // less two as return address may span into next dex instruction
+ uint32_t dex_pc = caller->ToDexPC(caller_pc - 2);
+ const DexFile::CodeItem* code = MethodHelper(caller).GetCodeItem();
+ CHECK_LT(dex_pc, code->insns_size_in_code_units_);
+ const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
+ Instruction::Code instr_code = instr->Opcode();
+ is_static = (instr_code == Instruction::INVOKE_STATIC) ||
+ (instr_code == Instruction::INVOKE_STATIC_RANGE);
+ is_virtual = (instr_code == Instruction::INVOKE_VIRTUAL) ||
+ (instr_code == Instruction::INVOKE_VIRTUAL_RANGE) ||
+ (instr_code == Instruction::INVOKE_SUPER) ||
+ (instr_code == Instruction::INVOKE_SUPER_RANGE);
+ DCHECK(is_static || is_virtual || (instr_code == Instruction::INVOKE_DIRECT) ||
+ (instr_code == Instruction::INVOKE_DIRECT_RANGE));
+ DecodedInstruction dec_insn(instr);
+ dex_method_idx = dec_insn.vB;
+ shorty = linker->MethodShorty(dex_method_idx, caller, &shorty_len);
+ } else {
+ DCHECK(!called->IsRuntimeMethod());
+ is_static = type == Runtime::kStaticMethod;
+ is_virtual = false;
+ dex_method_idx = called->GetDexMethodIndex();
+ MethodHelper mh(called);
+ shorty = mh.GetShorty();
+ shorty_len = mh.GetShortyLength();
+ }
+ // Discover shorty (avoid GCs)
+ size_t args_in_regs = 0;
+ for (size_t i = 1; i < shorty_len; i++) {
+ char c = shorty[i];
+ args_in_regs = args_in_regs + (c == 'J' || c == 'D' ? 2 : 1);
+ if (args_in_regs > 3) {
+ args_in_regs = 3;
+ break;
+ }
+ }
+ // Place into local references incoming arguments from the caller's register arguments
+ size_t cur_arg = 1; // skip method_idx in R0, first arg is in R1
+ if (!is_static) {
+ Object* obj = reinterpret_cast<Object*>(regs[cur_arg]);
+ cur_arg++;
+ if (args_in_regs < 3) {
+ // If we thought we had fewer than 3 arguments in registers, account for the receiver
+ args_in_regs++;
+ }
+ AddLocalReference<jobject>(env, obj);
+ }
+ size_t shorty_index = 1; // skip return value
+ // Iterate while arguments and arguments in registers (less 1 from cur_arg which is offset to skip
+ // R0)
+ while ((cur_arg - 1) < args_in_regs && shorty_index < shorty_len) {
+ char c = shorty[shorty_index];
+ shorty_index++;
+ if (c == 'L') {
+ Object* obj = reinterpret_cast<Object*>(regs[cur_arg]);
+ AddLocalReference<jobject>(env, obj);
+ }
+ cur_arg = cur_arg + (c == 'J' || c == 'D' ? 2 : 1);
+ }
+ // Place into local references incoming arguments from the caller's stack arguments
+ cur_arg += 11; // skip LR, Method* and spills for R1 to R3 and callee saves
+ while (shorty_index < shorty_len) {
+ char c = shorty[shorty_index];
+ shorty_index++;
+ if (c == 'L') {
+ Object* obj = reinterpret_cast<Object*>(regs[cur_arg]);
+ AddLocalReference<jobject>(env, obj);
+ }
+ cur_arg = cur_arg + (c == 'J' || c == 'D' ? 2 : 1);
+ }
+ // Resolve method filling in dex cache
+ if (type == Runtime::kUnknownMethod) {
+ called = linker->ResolveMethod(dex_method_idx, caller, !is_virtual);
+ }
+ const void* code = NULL;
+ if (LIKELY(!thread->IsExceptionPending())) {
+ if (LIKELY(called->IsDirect() == !is_virtual)) {
+ // Ensure that the called method's class is initialized.
+ Class* called_class = called->GetDeclaringClass();
+ linker->EnsureInitialized(called_class, true);
+ if (LIKELY(called_class->IsInitialized())) {
+ code = called->GetCode();
+ } else if (called_class->IsInitializing()) {
+ if (is_static) {
+ // Class is still initializing, go to oat and grab code (trampoline must be left in place
+ // until class is initialized to stop races between threads).
+ code = linker->GetOatCodeFor(called);
+ } else {
+ // No trampoline for non-static methods.
+ code = called->GetCode();
+ }
+ } else {
+ DCHECK(called_class->IsErroneous());
+ }
+ } else {
+ // Direct method has been made virtual
+ thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
+ "Expected direct method but found virtual: %s",
+ PrettyMethod(called, true).c_str());
+ }
+ }
+ if (UNLIKELY(code == NULL)) {
+ // Something went wrong in ResolveMethod or EnsureInitialized,
+ // go into deliver exception with the pending exception in r0
+ code = reinterpret_cast<void*>(art_deliver_exception_from_code);
+ regs[0] = reinterpret_cast<uintptr_t>(thread->GetException());
+ thread->ClearException();
+ } else {
+ // Expect class to at least be initializing.
+ DCHECK(called->GetDeclaringClass()->IsInitializing());
+ // Don't want infinite recursion.
+ DCHECK(code != Runtime::Current()->GetResolutionStubArray(Runtime::kUnknownMethod)->GetData());
+ // Set up entry into main method
+ regs[0] = reinterpret_cast<uintptr_t>(called);
+ }
+ return code;
+}
+
+// Called by the AbstractMethodError. Called by stub code.
+extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method** sp) {
+ FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
+ thread->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;",
+ "abstract method \"%s\"", PrettyMethod(method).c_str());
+ thread->DeliverException();
+}
+
+} // namespace art