Implement X86 trampoline for llvm compiler.
Change-Id: I0c6ec3a59eaebd5e28ca8e95f2711a31b79b9279
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 1edfe1a..5bd214a 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1401,7 +1401,13 @@
// back.
// TODO: Remove this workaround.
- GetOatMethodFor(method).LinkMethodPointers(method);
+ OatFile::OatMethod oat_method = GetOatMethodFor(method);
+ if (method->GetCode() == NULL) {
+ method->SetCode(oat_method.GetCode());
+ }
+ if (method->GetInvokeStub() == NULL) {
+ method->SetInvokeStub(oat_method.GetInvokeStub());
+ }
}
}
diff --git a/src/compiler_llvm/art_module.ll b/src/compiler_llvm/art_module.ll
index bdc4504..87be92a 100644
--- a/src/compiler_llvm/art_module.ll
+++ b/src/compiler_llvm/art_module.ll
@@ -167,11 +167,6 @@
declare void @art_mark_gc_card_from_code(%JavaObject*, %JavaObject*)
-declare %JavaObject* @art_ensure_resolved_from_code(%JavaObject*,
- %JavaObject*,
- i32,
- i1)
-
declare %JavaObject* @art_fix_stub_from_code(%JavaObject*)
declare void @art_proxy_invoke_handler_from_code(%JavaObject*, ...)
diff --git a/src/compiler_llvm/generated/art_module.cc b/src/compiler_llvm/generated/art_module.cc
index cf92e87..c727d40 100644
--- a/src/compiler_llvm/generated/art_module.cc
+++ b/src/compiler_llvm/generated/art_module.cc
@@ -314,9 +314,6 @@
std::vector<Type*>FuncTy_34_args;
FuncTy_34_args.push_back(PointerTy_1);
-FuncTy_34_args.push_back(PointerTy_1);
-FuncTy_34_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_34_args.push_back(IntegerType::get(mod->getContext(), 1));
FunctionType* FuncTy_34 = FunctionType::get(
/*Result=*/PointerTy_1,
/*Params=*/FuncTy_34_args,
@@ -325,15 +322,8 @@
std::vector<Type*>FuncTy_35_args;
FuncTy_35_args.push_back(PointerTy_1);
FunctionType* FuncTy_35 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_35_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_36_args;
-FuncTy_36_args.push_back(PointerTy_1);
-FunctionType* FuncTy_36 = FunctionType::get(
/*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_36_args,
+ /*Params=*/FuncTy_35_args,
/*isVarArg=*/true);
@@ -955,21 +945,10 @@
AttrListPtr func_art_mark_gc_card_from_code_PAL;
func_art_mark_gc_card_from_code->setAttributes(func_art_mark_gc_card_from_code_PAL);
-Function* func_art_ensure_resolved_from_code = mod->getFunction("art_ensure_resolved_from_code");
-if (!func_art_ensure_resolved_from_code) {
-func_art_ensure_resolved_from_code = Function::Create(
- /*Type=*/FuncTy_34,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_ensure_resolved_from_code", mod); // (external, no body)
-func_art_ensure_resolved_from_code->setCallingConv(CallingConv::C);
-}
-AttrListPtr func_art_ensure_resolved_from_code_PAL;
-func_art_ensure_resolved_from_code->setAttributes(func_art_ensure_resolved_from_code_PAL);
-
Function* func_art_fix_stub_from_code = mod->getFunction("art_fix_stub_from_code");
if (!func_art_fix_stub_from_code) {
func_art_fix_stub_from_code = Function::Create(
- /*Type=*/FuncTy_35,
+ /*Type=*/FuncTy_34,
/*Linkage=*/GlobalValue::ExternalLinkage,
/*Name=*/"art_fix_stub_from_code", mod); // (external, no body)
func_art_fix_stub_from_code->setCallingConv(CallingConv::C);
@@ -980,7 +959,7 @@
Function* func_art_proxy_invoke_handler_from_code = mod->getFunction("art_proxy_invoke_handler_from_code");
if (!func_art_proxy_invoke_handler_from_code) {
func_art_proxy_invoke_handler_from_code = Function::Create(
- /*Type=*/FuncTy_36,
+ /*Type=*/FuncTy_35,
/*Linkage=*/GlobalValue::ExternalLinkage,
/*Name=*/"art_proxy_invoke_handler_from_code", mod); // (external, no body)
func_art_proxy_invoke_handler_from_code->setCallingConv(CallingConv::C);
diff --git a/src/compiler_llvm/method_compiler.cc b/src/compiler_llvm/method_compiler.cc
index b128693..91b57a6 100644
--- a/src/compiler_llvm/method_compiler.cc
+++ b/src/compiler_llvm/method_compiler.cc
@@ -2783,18 +2783,6 @@
<< PrettyMethod(callee_method_idx, *dex_file_);
}
-llvm::Value* MethodCompiler::EmitEnsureResolved(llvm::Value* callee,
- llvm::Value* caller,
- uint32_t dex_method_idx,
- bool is_virtual) {
- // TODO: Remove this after we solve the trampoline related problems.
- return irb_.CreateCall4(irb_.GetRuntime(EnsureResolved),
- callee,
- caller,
- irb_.getInt32(dex_method_idx),
- irb_.getInt1(is_virtual));
-}
-
llvm::Value* MethodCompiler::EmitFixStub(llvm::Value* callee_method_object_addr,
uint32_t method_idx,
bool is_static) {
@@ -2869,21 +2857,6 @@
dex_pc, is_fast_path);
break;
}
-
- // Ensure the callee method object is resolved.
- bool is_virtual = (dec_insn.opcode == Instruction::INVOKE_VIRTUAL) ||
- (dec_insn.opcode == Instruction::INVOKE_VIRTUAL_RANGE) ||
- (dec_insn.opcode == Instruction::INVOKE_SUPER) ||
- (dec_insn.opcode == Instruction::INVOKE_SUPER_RANGE);
-
- llvm::Value* caller_method_object_addr = EmitLoadMethodObjectAddr();
-
- callee_method_object_addr = EmitEnsureResolved(callee_method_object_addr,
- caller_method_object_addr,
- callee_method_idx,
- is_virtual);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
}
#if 0
diff --git a/src/compiler_llvm/runtime_support_func_list.h b/src/compiler_llvm/runtime_support_func_list.h
index 2f3a6fa..7a51b90 100644
--- a/src/compiler_llvm/runtime_support_func_list.h
+++ b/src/compiler_llvm/runtime_support_func_list.h
@@ -62,7 +62,6 @@
V(IsExceptionPending, art_is_exception_pending_from_code) \
V(FindCatchBlock, art_find_catch_block_from_code) \
V(MarkGCCard, art_mark_gc_card_from_code) \
- V(EnsureResolved, art_ensure_resolved_from_code) \
V(FixStub, art_fix_stub_from_code) \
V(ProxyInvokeHandler, art_proxy_invoke_handler_from_code) \
V(DecodeJObjectInThread, art_decode_jobject_in_thread) \
diff --git a/src/compiler_llvm/runtime_support_llvm.cc b/src/compiler_llvm/runtime_support_llvm.cc
index ad8fded..fce0f41 100644
--- a/src/compiler_llvm/runtime_support_llvm.cc
+++ b/src/compiler_llvm/runtime_support_llvm.cc
@@ -585,83 +585,15 @@
}
}
-// TODO: This runtime support function is the temporary solution for the stubs. Because now the
-// stubs and runtime support functions in the LLVM part and the non-LLVM part is different.
-// After deal these problems, we should remove this function, and will save a function call before
-// every method invocation.
-Method* art_ensure_resolved_from_code(Method* called,
- Method* caller,
- uint32_t dex_method_idx,
- bool is_virtual) {
- if (LIKELY(!called->IsResolutionMethod())) {
- return called;
- }
-
- // If the called method is ResolutionMethod. -> ResolutionTrampoline(kUnknownMethod)
- // Resolve method.
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- called = linker->ResolveMethod(dex_method_idx, caller, !is_virtual);
- if (UNLIKELY(art_get_current_thread_from_code()->IsExceptionPending())) {
- return NULL;
- }
- 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, true);
- return called;
- } else {
- Thread* thread = art_get_current_thread_from_code();
- // Direct method has been made virtual
- thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
- "Expected direct method but found virtual: %s",
- PrettyMethod(called, true).c_str());
- return NULL;
- }
-}
-
-
-// TODO: This runtime support function is the temporary solution for the stubs. Because now the
-// stubs and runtime support functions in the LLVM part and the non-LLVM part is different.
-// After deal these problems, we should remove this function, and will save a function call before
-// every method invocation.
+// TODO: This runtime support function is the temporary solution for the link issue.
// It calls to this function before invoking any function, and this function will check:
-// 1. The code address is ResolutionStub. -> ResolutionTrampoline(kStaticMethod)
-// Initialize class.
-// 2. The code address is AbstractMethodErrorStub. -> AbstractMethodErrorStub
-// 3. The code address is 0. -> Link the code by ELFLoader
-// The item 3 will solved by in-place linking at image loading.
+// 1. The code address is 0. -> Link the code by ELFLoader
+// That will solved by in-place linking at image loading.
const void* art_fix_stub_from_code(Method* called) {
- DCHECK(!called->IsResolutionMethod()) << PrettyMethod(called);
- Runtime* runtime = Runtime::Current();
const void* code = called->GetCode();
-
- // 1. The code address is ResolutionStub. -> ResolutionTrampoline(kStaticMethod)
- if (UNLIKELY(code == runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData())) {
- ClassLinker* linker = runtime->GetClassLinker();
- Class* called_class = called->GetDeclaringClass();
- linker->EnsureInitialized(called_class, true, true);
- if (LIKELY(called_class->IsInitialized())) {
- return called->GetCode();
- } else if (called_class->IsInitializing()) {
- return linker->GetOatCodeFor(called);
- } else {
- DCHECK(art_get_current_thread_from_code()->IsExceptionPending());
- DCHECK(called_class->IsErroneous());
- return NULL;
- }
- }
-
- // 2. The code address is AbstractMethodErrorStub. -> AbstractMethodErrorStub
- if (UNLIKELY(code == runtime->GetAbstractMethodErrorStubArray()->GetData())) {
- art_get_current_thread_from_code()->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;",
- "abstract method \"%s\"",
- PrettyMethod(called).c_str());
- return NULL;
- }
-
- // 3. The code address is 0. -> Link the code by ELFLoader
- if (UNLIKELY(called->GetInvokeStub() == NULL || code == NULL)) {
- runtime->GetClassLinker()->LinkOatCodeFor(called);
+ // 1. The code address is 0. -> Link the code by ELFLoader
+ if (UNLIKELY(code == NULL)) {
+ Runtime::Current()->GetClassLinker()->LinkOatCodeFor(called);
return called->GetCode();
}
diff --git a/src/nth_caller_visitor.h b/src/nth_caller_visitor.h
index 07b9e49..84d2b3b 100644
--- a/src/nth_caller_visitor.h
+++ b/src/nth_caller_visitor.h
@@ -24,17 +24,19 @@
// Walks up the stack 'n' callers, when used with Thread::WalkStack.
struct NthCallerVisitor : public Thread::StackVisitor {
- NthCallerVisitor(size_t n) : n(n), count(0), caller(NULL) {}
- bool VisitFrame(const Frame& f, uintptr_t) {
+ NthCallerVisitor(size_t n) : n(n), count(0), pc(0), caller(NULL) {}
+ bool VisitFrame(const Frame& f, uintptr_t fpc) {
DCHECK(caller == NULL);
if (count++ == n) {
caller = f.GetMethod();
+ pc = fpc;
return false;
}
return true;
}
size_t n;
size_t count;
+ uintptr_t pc;
Method* caller;
};
diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc
index fb0b5a4..ac5d6f9 100644
--- a/src/oat/runtime/support_stubs.cc
+++ b/src/oat/runtime/support_stubs.cc
@@ -14,16 +14,22 @@
* limitations under the License.
*/
+#if !defined(ART_USE_LLVM_COMPILER)
#include "callee_save_frame.h"
+#endif
#include "dex_instruction.h"
#include "object.h"
#include "object_utils.h"
+#if defined(ART_USE_LLVM_COMPILER)
+#include "nth_caller_visitor.h"
+#endif
// Architecture specific assembler helper to deliver exception.
extern "C" void art_deliver_exception_from_code(void*);
namespace art {
+#if !defined(ART_USE_LLVM_COMPILER)
// Lazily resolve a method. Called by stub code.
const void* UnresolvedDirectMethodTrampolineFromCode(Method* called, Method** sp, Thread* thread,
Runtime::TrampolineType type) {
@@ -206,7 +212,96 @@
}
return code;
}
+#else // ART_USE_LLVM_COMPILER
+const void* UnresolvedDirectMethodTrampolineFromCode(Method* called, Method** called_addr,
+ Thread* thread, Runtime::TrampolineType type) {
+ NthCallerVisitor visitor(0);
+ thread->WalkStack(&visitor);
+ Method* caller = visitor.caller;
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ bool is_static;
+ bool is_virtual;
+ uint32_t dex_method_idx;
+ if (type == Runtime::kUnknownMethod) {
+ DCHECK(called->IsRuntimeMethod());
+ // less two as return address may span into next dex instruction
+ uint32_t dex_pc = static_cast<uint32_t>(visitor.pc);
+ 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;
+ } else {
+ DCHECK(!called->IsRuntimeMethod());
+ is_static = type == Runtime::kStaticMethod;
+ is_virtual = false;
+ dex_method_idx = called->GetDexMethodIndex();
+ }
+ 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, true);
+ if (LIKELY(called_class->IsInitialized())) {
+ code = called->GetCode();
+ // TODO: remove this after we solve the link issue.
+ { // for lazy link.
+ if (code == NULL) {
+ code = linker->GetOatCodeFor(called);
+ }
+ }
+ } 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();
+ // TODO: remove this after we solve the link issue.
+ { // for lazy link.
+ if (code == NULL) {
+ code = linker->GetOatCodeFor(called);
+ }
+ }
+ }
+ } 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 (LIKELY(code != NULL)) {
+ // 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
+ *called_addr = called;
+ }
+ return code;
+}
+#endif // ART_USE_LLVM_COMPILER
+
+#if !defined(ART_USE_LLVM_COMPILER)
// Called by the AbstractMethodError. Called by stub code.
extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method** sp) {
FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
@@ -214,5 +309,11 @@
"abstract method \"%s\"", PrettyMethod(method).c_str());
thread->DeliverException();
}
+#else // ART_USE_LLVM_COMPILER
+extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method**) {
+ thread->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;",
+ "abstract method \"%s\"", PrettyMethod(method).c_str());
+}
+#endif // ART_USE_LLVM_COMPILER
} // namespace art
diff --git a/src/oat/runtime/x86/stub_x86.cc b/src/oat/runtime/x86/stub_x86.cc
index 1dea0a1..0371a1e 100644
--- a/src/oat/runtime/x86/stub_x86.cc
+++ b/src/oat/runtime/x86/stub_x86.cc
@@ -28,6 +28,7 @@
ByteArray* X86CreateResolutionTrampoline(Runtime::TrampolineType type) {
UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+#if !defined(ART_USE_LLVM_COMPILER)
// Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
// return address
__ pushl(EDI);
@@ -59,6 +60,32 @@
__ xchgl(EDI, Address(ESP,0));
// Tail call to intended method.
__ ret();
+#else // ART_USE_LLVM_COMPILER
+ __ pushl(EBP);
+ __ movl(EBP, ESP); // save ESP
+ __ movl(EAX, Address(EBP,8)); // Method* called
+ __ leal(EDX, Address(EBP,8)); // Method** called_addr
+ __ pushl(Immediate(type)); // pass is_static
+ __ fs()->pushl(Address::Absolute(Thread::SelfOffset())); // pass thread
+ __ pushl(EDX); // pass called_addr
+ __ pushl(EAX); // pass called
+
+ // Call to resolve method.
+ __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pUnresolvedDirectMethodTrampolineFromCode)),
+ X86ManagedRegister::FromCpuRegister(ECX));
+
+ __ leave();
+
+ Label resolve_fail; // forward declaration
+ __ cmpl(EAX, Immediate(0));
+ __ j(kEqual, &resolve_fail);
+
+ __ jmp(EAX);
+ // Tail call to intended method.
+
+ __ Bind(&resolve_fail);
+ __ ret();
+#endif // ART_USE_LLVM_COMPILER
assembler->EmitSlowPaths();
size_t cs = assembler->CodeSize();
@@ -75,6 +102,7 @@
ByteArray* CreateAbstractMethodErrorStub() {
UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+#if !defined(ART_USE_LLVM_COMPILER)
// Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kSaveAll)
// return address
@@ -95,18 +123,21 @@
__ Call(ThreadOffset(ENTRYPOINT_OFFSET(pThrowAbstractMethodErrorFromCode)),
X86ManagedRegister::FromCpuRegister(ECX));
-#if defined(ART_USE_LLVM_COMPILER)
- // Return to caller who will handle pending exception.
- // TODO: The callee save set up is unnecessary for LLVM as it uses shadow stacks.
- __ addl(ESP, Immediate(32));
- __ popl(EBP);
- __ popl(ESI);
- __ popl(EDI);
- __ ret();
-#else
// Call never returns.
__ int3();
-#endif
+#else // ART_USE_LLVM_COMPILER
+ __ pushl(EBP);
+ __ movl(EBP, ESP); // save ESP
+ __ pushl(ESP); // pass sp (not use)
+ __ fs()->pushl(Address::Absolute(Thread::SelfOffset())); // pass thread*
+ __ pushl(Address(EBP,8)); // pass method
+ // Call to throw AbstractMethodError.
+ __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pThrowAbstractMethodErrorFromCode)),
+ X86ManagedRegister::FromCpuRegister(ECX));
+ __ leave();
+ // Return to caller who will handle pending exception.
+ __ ret();
+#endif // ART_USE_LLVM_COMPILER
assembler->EmitSlowPaths();
diff --git a/src/object.cc b/src/object.cc
index 5413194..d91e70c 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -549,16 +549,20 @@
self->PushNativeToManagedRecord(&record);
#endif
-#if defined(ART_USE_LLVM_COMPILER)
- art_fix_stub_from_code(const_cast<Method*>(this));
-#endif
-
// Call the invoke stub associated with the method.
// Pass everything as arguments.
const Method::InvokeStub* stub = GetInvokeStub();
bool have_executable_code = (GetCode() != NULL);
+#if defined(ART_USE_LLVM_COMPILER)
+ if (stub == NULL || !have_executable_code) {
+ Runtime::Current()->GetClassLinker()->LinkOatCodeFor(const_cast<Method*>(this));
+ stub = GetInvokeStub();
+ have_executable_code = (GetCode() != NULL);
+ }
+#endif
+
if (Runtime::Current()->IsStarted() && have_executable_code && stub != NULL) {
bool log = false;
if (log) {