Fix 044-proxy. Implement proxy for now, working on x86 and ARM.

Already added a TODO to do the assembly code for x86 and ARM for proxy.
Use LLVM .ll for multi-architecture now.

Change-Id: Ibdeeee113dcf284592e9d7769d3044438cb1e453
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 08792eb..b94003a 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -43,6 +43,9 @@
 #include "os.h"
 #include "runtime.h"
 #include "runtime_support.h"
+#if defined(ART_USE_LLVM_COMPILER)
+#include "compiler_llvm/runtime_support_llvm.h"
+#endif
 #include "ScopedLocalRef.h"
 #include "space.h"
 #include "stack_indirect_reference_table.h"
@@ -2299,6 +2302,11 @@
   ObjectArray<Method>* proxy_direct_methods = proxy_class->GetDirectMethods();
   CHECK_EQ(proxy_direct_methods->GetLength(), 15);
   Method* proxy_constructor = proxy_direct_methods->Get(2);
+#if defined(ART_USE_LLVM_COMPILER)
+  // Ensure link.
+  // TODO: Remove this after fixing the link problem by in-place linking.
+  art_fix_stub_from_code(proxy_constructor);
+#endif
   // Clone the existing constructor of Proxy (our constructor would just invoke it so steal its
   // code_ too)
   Method* constructor = down_cast<Method*>(proxy_constructor->Clone());
@@ -2336,7 +2344,12 @@
   method->SetCoreSpillMask(refs_and_args->GetCoreSpillMask());
   method->SetFpSpillMask(refs_and_args->GetFpSpillMask());
   method->SetFrameSizeInBytes(refs_and_args->GetFrameSizeInBytes());
+#if !defined(ART_USE_LLVM_COMPILER)
   method->SetCode(reinterpret_cast<void*>(art_proxy_invoke_handler));
+#else
+  method->SetCode(reinterpret_cast<const void*>(
+      static_cast<uintptr_t>(compiler_llvm::special_stub::kProxyStub)));
+#endif
 
   return method;
 }
diff --git a/src/compiler_llvm/art_module.ll b/src/compiler_llvm/art_module.ll
index 9354bb6..a5de7a3 100644
--- a/src/compiler_llvm/art_module.ll
+++ b/src/compiler_llvm/art_module.ll
@@ -158,3 +158,5 @@
                                                     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 6889ed9..cdde019 100644
--- a/src/compiler_llvm/generated/art_module.cc
+++ b/src/compiler_llvm/generated/art_module.cc
@@ -290,6 +290,13 @@
  /*Params=*/FuncTy_30_args,
  /*isVarArg=*/false);
 
+std::vector<Type*>FuncTy_31_args;
+FuncTy_31_args.push_back(PointerTy_1);
+FunctionType* FuncTy_31 = FunctionType::get(
+ /*Result=*/Type::getVoidTy(mod->getContext()),
+ /*Params=*/FuncTy_31_args,
+ /*isVarArg=*/true);
+
 
 // Function Declarations
 
@@ -876,6 +883,17 @@
 AttrListPtr func_art_fix_stub_from_code_PAL;
 func_art_fix_stub_from_code->setAttributes(func_art_fix_stub_from_code_PAL);
 
+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_31,
+ /*Linkage=*/GlobalValue::ExternalLinkage,
+ /*Name=*/"art_proxy_invoke_handler_from_code", mod); // (external, no body)
+func_art_proxy_invoke_handler_from_code->setCallingConv(CallingConv::C);
+}
+AttrListPtr func_art_proxy_invoke_handler_from_code_PAL;
+func_art_proxy_invoke_handler_from_code->setAttributes(func_art_proxy_invoke_handler_from_code_PAL);
+
 // Global Variable Declarations
 
 
diff --git a/src/compiler_llvm/ir_builder.h b/src/compiler_llvm/ir_builder.h
index 0762a0d..d199b76 100644
--- a/src/compiler_llvm/ir_builder.h
+++ b/src/compiler_llvm/ir_builder.h
@@ -89,6 +89,26 @@
     return CreatePtrDisp(base, total_offset, ret_ty);
   }
 
+  llvm::Value* LoadFromObjectOffset(llvm::Value* object_addr, int32_t offset, llvm::Type* type) {
+    // Convert offset to llvm::value
+    llvm::Value* llvm_offset = getPtrEquivInt(offset);
+    // Calculate the value's address
+    llvm::Value* value_addr = CreatePtrDisp(object_addr, llvm_offset, type->getPointerTo());
+    // Load
+    return CreateLoad(value_addr);
+  }
+
+  void StoreToObjectOffset(llvm::Value* object_addr, int32_t offset, llvm::Value* new_value) {
+    // Convert offset to llvm::value
+    llvm::Value* llvm_offset = getPtrEquivInt(offset);
+    // Calculate the value's address
+    llvm::Value* value_addr = CreatePtrDisp(object_addr,
+                                            llvm_offset,
+                                            new_value->getType()->getPointerTo());
+    // Store
+    CreateStore(new_value, value_addr);
+  }
+
 
   //--------------------------------------------------------------------------
   // Runtime Helper Function
diff --git a/src/compiler_llvm/jni_compiler.cc b/src/compiler_llvm/jni_compiler.cc
index 252daef..68ce2d8 100644
--- a/src/compiler_llvm/jni_compiler.cc
+++ b/src/compiler_llvm/jni_compiler.cc
@@ -88,9 +88,9 @@
   } else {
     // Load class object
     this_object_or_class_object =
-        LoadFromObjectOffset(method_object_addr,
-                             Method::DeclaringClassOffset().Int32Value(),
-                             irb_.getJObjectTy());
+        irb_.LoadFromObjectOffset(method_object_addr,
+                                  Method::DeclaringClassOffset().Int32Value(),
+                                  irb_.getJObjectTy());
   }
   // Actual argument (ignore method and this object)
   arg_begin = arg_iter;
@@ -126,34 +126,35 @@
   irb_.CreateStore(method_object_addr, method_field_addr);
 
   // Store the line number
-  StoreToObjectOffset(shadow_frame_,
-                      ShadowFrame::LineNumOffset(),
-                      irb_.getInt32(dex_file_->GetLineNumFromPC(method_, 0)));
+  irb_.StoreToObjectOffset(shadow_frame_,
+                           ShadowFrame::LineNumOffset(),
+                           irb_.getInt32(dex_file_->GetLineNumFromPC(method_, 0)));
 
   // Store the number of the pointer slots
-  StoreToObjectOffset(shadow_frame_,
-                      ShadowFrame::NumberOfReferencesOffset(),
-                      irb_.getInt32(sirt_size));
+  irb_.StoreToObjectOffset(shadow_frame_,
+                           ShadowFrame::NumberOfReferencesOffset(),
+                           irb_.getInt32(sirt_size));
 
   // Push the shadow frame
   llvm::Value* shadow_frame_upcast = irb_.CreateConstGEP2_32(shadow_frame_, 0, 0);
   irb_.CreateCall(irb_.GetRuntime(PushShadowFrame), shadow_frame_upcast);
 
   // Get JNIEnv
-  llvm::Value* jni_env_object_addr = LoadFromObjectOffset(thread_object_addr,
-                                                          Thread::JniEnvOffset().Int32Value(),
-                                                          irb_.getJObjectTy());
+  llvm::Value* jni_env_object_addr =
+      irb_.LoadFromObjectOffset(thread_object_addr,
+                                Thread::JniEnvOffset().Int32Value(),
+                                irb_.getJObjectTy());
 
   // Set thread state to kNative
-  StoreToObjectOffset(thread_object_addr,
-                      Thread::StateOffset().Int32Value(),
-                      irb_.getInt32(kNative));
+  irb_.StoreToObjectOffset(thread_object_addr,
+                           Thread::StateOffset().Int32Value(),
+                           irb_.getInt32(kNative));
 
   // Get callee code_addr
   llvm::Value* code_addr =
-      LoadFromObjectOffset(method_object_addr,
-                           Method::NativeMethodOffset().Int32Value(),
-                           GetFunctionType(method_idx_, is_static, true)->getPointerTo());
+      irb_.LoadFromObjectOffset(method_object_addr,
+                                Method::NativeMethodOffset().Int32Value(),
+                                GetFunctionType(method_idx_, is_static, true)->getPointerTo());
 
   // Load actual parameters
   std::vector<llvm::Value*> args;
@@ -230,18 +231,18 @@
 
   // saved_local_ref_cookie = env->local_ref_cookie
   llvm::Value* saved_local_ref_cookie =
-      LoadFromObjectOffset(jni_env_object_addr,
-                           JNIEnvExt::LocalRefCookieOffset().Int32Value(),
-                           irb_.getInt32Ty());
+      irb_.LoadFromObjectOffset(jni_env_object_addr,
+                                JNIEnvExt::LocalRefCookieOffset().Int32Value(),
+                                irb_.getInt32Ty());
 
   // env->local_ref_cookie = env->locals.segment_state
   llvm::Value* segment_state =
-      LoadFromObjectOffset(jni_env_object_addr,
-                           JNIEnvExt::SegmentStateOffset().Int32Value(),
-                           irb_.getInt32Ty());
-  StoreToObjectOffset(jni_env_object_addr,
-                      JNIEnvExt::LocalRefCookieOffset().Int32Value(),
-                      segment_state);
+      irb_.LoadFromObjectOffset(jni_env_object_addr,
+                                JNIEnvExt::SegmentStateOffset().Int32Value(),
+                                irb_.getInt32Ty());
+  irb_.StoreToObjectOffset(jni_env_object_addr,
+                           JNIEnvExt::LocalRefCookieOffset().Int32Value(),
+                           segment_state);
 
 
   // Call!!!
@@ -254,9 +255,9 @@
   }
 
   // Set thread state to kRunnable
-  StoreToObjectOffset(thread_object_addr,
-                      Thread::StateOffset().Int32Value(),
-                      irb_.getInt32(kRunnable));
+  irb_.StoreToObjectOffset(thread_object_addr,
+                           Thread::StateOffset().Int32Value(),
+                           irb_.getInt32(kRunnable));
 
   if (return_shorty == 'L') {
     // If the return value is reference, it may point to SIRT, we should decode it.
@@ -267,17 +268,17 @@
 
   // env->locals.segment_state = env->local_ref_cookie
   llvm::Value* local_ref_cookie =
-      LoadFromObjectOffset(jni_env_object_addr,
-                           JNIEnvExt::LocalRefCookieOffset().Int32Value(),
-                           irb_.getInt32Ty());
-  StoreToObjectOffset(jni_env_object_addr,
-                      JNIEnvExt::SegmentStateOffset().Int32Value(),
-                      local_ref_cookie);
+      irb_.LoadFromObjectOffset(jni_env_object_addr,
+                                JNIEnvExt::LocalRefCookieOffset().Int32Value(),
+                                irb_.getInt32Ty());
+  irb_.StoreToObjectOffset(jni_env_object_addr,
+                           JNIEnvExt::SegmentStateOffset().Int32Value(),
+                           local_ref_cookie);
 
   // env->local_ref_cookie = saved_local_ref_cookie
-  StoreToObjectOffset(jni_env_object_addr,
-                      JNIEnvExt::LocalRefCookieOffset().Int32Value(),
-                      saved_local_ref_cookie);
+  irb_.StoreToObjectOffset(jni_env_object_addr,
+                           JNIEnvExt::LocalRefCookieOffset().Int32Value(),
+                           saved_local_ref_cookie);
 
   // Pop the shadow frame
   irb_.CreateCall(irb_.GetRuntime(PopShadowFrame));
@@ -348,29 +349,5 @@
   return llvm::FunctionType::get(ret_type, args_type, false);
 }
 
-llvm::Value* JniCompiler::LoadFromObjectOffset(llvm::Value* object_addr,
-                                               int32_t offset,
-                                               llvm::Type* type) {
-  // Convert offset to llvm::value
-  llvm::Value* llvm_offset = irb_.getPtrEquivInt(offset);
-  // Calculate the value's address
-  llvm::Value* value_addr = irb_.CreatePtrDisp(object_addr, llvm_offset, type->getPointerTo());
-  // Load
-  return irb_.CreateLoad(value_addr);
-}
-
-void JniCompiler::StoreToObjectOffset(llvm::Value* object_addr,
-                                      int32_t offset,
-                                      llvm::Value* new_value) {
-  // Convert offset to llvm::value
-  llvm::Value* llvm_offset = irb_.getPtrEquivInt(offset);
-  // Calculate the value's address
-  llvm::Value* value_addr = irb_.CreatePtrDisp(object_addr,
-                                               llvm_offset,
-                                               new_value->getType()->getPointerTo());
-  // Store
-  irb_.CreateStore(new_value, value_addr);
-}
-
 } // namespace compiler_llvm
 } // namespace art
diff --git a/src/compiler_llvm/jni_compiler.h b/src/compiler_llvm/jni_compiler.h
index d91293d..15a789c 100644
--- a/src/compiler_llvm/jni_compiler.h
+++ b/src/compiler_llvm/jni_compiler.h
@@ -62,10 +62,6 @@
                                       bool is_static, bool is_target_function);
 
  private:
-  llvm::Value* LoadFromObjectOffset(llvm::Value* object_addr, int32_t offset, llvm::Type* type);
-
-  void StoreToObjectOffset(llvm::Value* object_addr, int32_t offset, llvm::Value* new_value);
-
   CompilationUnit* cunit_;
   Compiler const* compiler_;
 
diff --git a/src/compiler_llvm/method_compiler.cc b/src/compiler_llvm/method_compiler.cc
index 50ed471..fb6cc19 100644
--- a/src/compiler_llvm/method_compiler.cc
+++ b/src/compiler_llvm/method_compiler.cc
@@ -27,6 +27,7 @@
 #include "object.h"
 #include "object_utils.h"
 #include "runtime_support_func.h"
+#include "runtime_support_llvm.h"
 #include "shadow_frame.h"
 #include "stl_util.h"
 #include "stringprintf.h"
@@ -2884,6 +2885,7 @@
   EmitLoadActualParameters(args, callee_method_idx, dec_insn,
                            arg_fmt, is_static);
 
+#if 0
   // Invoke callee
   EmitUpdateLineNumFromDexPC(dex_pc);
   llvm::Value* retval = irb_.CreateCall(code_addr, args);
@@ -2897,6 +2899,72 @@
   if (ret_shorty != 'V') {
     EmitStoreDalvikRetValReg(ret_shorty, kAccurate, retval);
   }
+#else
+  uint32_t callee_access_flags = is_static ? kAccStatic : 0;
+  UniquePtr<OatCompilationUnit> callee_oat_compilation_unit(
+    oat_compilation_unit_->GetCallee(callee_method_idx, callee_access_flags));
+
+  char ret_shorty = callee_oat_compilation_unit->GetShorty()[0];
+
+
+  EmitUpdateLineNumFromDexPC(dex_pc);
+
+
+  llvm::BasicBlock* block_normal = CreateBasicBlockWithDexPC(dex_pc, "normal");
+  llvm::BasicBlock* block_proxy_stub = CreateBasicBlockWithDexPC(dex_pc, "proxy");
+  llvm::BasicBlock* block_continue = CreateBasicBlockWithDexPC(dex_pc, "cont");
+
+  llvm::Type* accurate_ret_type = irb_.getJType(ret_shorty, kAccurate);
+  llvm::Value* retval_addr = NULL;
+  if (ret_shorty != 'V') {
+    retval_addr = irb_.CreateAlloca(accurate_ret_type);
+  }
+
+
+  // TODO: Remove this after we solve the proxy trampoline calling convention problem.
+  llvm::Value* code_addr_int = irb_.CreatePtrToInt(code_addr, irb_.getPtrEquivIntTy());
+  llvm::Value* proxy_stub_int = irb_.getPtrEquivInt(special_stub::kProxyStub);
+  llvm::Value* is_proxy_stub = irb_.CreateICmpEQ(code_addr_int, proxy_stub_int);
+  irb_.CreateCondBr(is_proxy_stub, block_proxy_stub, block_normal);
+
+
+  irb_.SetInsertPoint(block_normal);
+  {
+    // Invoke callee
+    llvm::Value* result = irb_.CreateCall(code_addr, args);
+    if (ret_shorty != 'V') {
+      irb_.CreateStore(result, retval_addr);
+    }
+  }
+  irb_.CreateBr(block_continue);
+
+
+  irb_.SetInsertPoint(block_proxy_stub);
+  {
+    llvm::Value* temp_space_addr;
+    if (ret_shorty != 'V') {
+      temp_space_addr = irb_.CreateAlloca(irb_.getJValueTy());
+      args.push_back(temp_space_addr);
+    }
+    irb_.CreateCall(irb_.GetRuntime(ProxyInvokeHandler), args);
+    if (ret_shorty != 'V') {
+      llvm::Value* result_addr =
+          irb_.CreateBitCast(temp_space_addr, accurate_ret_type->getPointerTo());
+      llvm::Value* retval = irb_.CreateLoad(result_addr);
+      irb_.CreateStore(retval, retval_addr);
+    }
+  }
+  irb_.CreateBr(block_continue);
+
+
+  irb_.SetInsertPoint(block_continue);
+
+  if (ret_shorty != 'V') {
+    llvm::Value* retval = irb_.CreateLoad(retval_addr);
+    EmitStoreDalvikRetValReg(ret_shorty, kAccurate, retval);
+  }
+  EmitGuard_ExceptionLandingPad(dex_pc);
+#endif
 
   irb_.CreateBr(GetNextBasicBlock(dex_pc));
 }
diff --git a/src/compiler_llvm/runtime_support_func_list.h b/src/compiler_llvm/runtime_support_func_list.h
index a271c71..0e58e9c 100644
--- a/src/compiler_llvm/runtime_support_func_list.h
+++ b/src/compiler_llvm/runtime_support_func_list.h
@@ -63,4 +63,5 @@
   V(FindCatchBlock, art_find_catch_block_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 c566aea..d354bee 100644
--- a/src/compiler_llvm/runtime_support_llvm.cc
+++ b/src/compiler_llvm/runtime_support_llvm.cc
@@ -19,13 +19,16 @@
 #include "nth_caller_visitor.h"
 #include "object.h"
 #include "object_utils.h"
+#include "reflection.h"
 #include "runtime_support.h"
 #include "runtime_support_llvm.h"
+#include "ScopedLocalRef.h"
 #include "shadow_frame.h"
 #include "thread.h"
 #include "thread_list.h"
 
 #include <algorithm>
+#include <cstdarg>
 #include <stdint.h>
 
 namespace art {
@@ -629,6 +632,157 @@
   return code;
 }
 
+// Handler for invocation on proxy methods. We create a boxed argument array. And we invoke
+// the invocation handler which is a field within the proxy object receiver.
+void art_proxy_invoke_handler_from_code(Method* proxy_method, ...) {
+  va_list ap;
+  va_start(ap, proxy_method);
+
+  Object* receiver = va_arg(ap, Object*);
+  Thread* thread = Thread::Current();
+  MethodHelper proxy_mh(proxy_method);
+  const size_t num_params = proxy_mh.NumArgs();
+
+  // Start new JNI local reference state
+  JNIEnvExt* env = thread->GetJniEnv();
+  ScopedJniEnvLocalRefState env_state(env);
+
+  // Create local ref. copies of proxy method and the receiver
+  jobject rcvr_jobj = AddLocalReference<jobject>(env, receiver);
+
+  // Convert proxy method into expected interface method
+  Method* interface_method = proxy_method->FindOverriddenMethod();
+  DCHECK(interface_method != NULL);
+  DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method);
+
+  // Set up arguments array and place in local IRT during boxing (which may allocate/GC)
+  jvalue args_jobj[3];
+  args_jobj[0].l = rcvr_jobj;
+  args_jobj[1].l = AddLocalReference<jobject>(env, interface_method);
+  // Args array, if no arguments then NULL (don't include receiver in argument count)
+  args_jobj[2].l = NULL;
+  ObjectArray<Object>* args = NULL;
+  if ((num_params - 1) > 0) {
+    args = Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(num_params - 1);
+    if (args == NULL) {
+      CHECK(thread->IsExceptionPending());
+      return;
+    }
+    args_jobj[2].l = AddLocalReference<jobjectArray>(env, args);
+  }
+
+  // Get parameter types.
+  const char* shorty = proxy_mh.GetShorty();
+  ObjectArray<Class>* param_types = proxy_mh.GetParameterTypes();
+  if (param_types == NULL) {
+    CHECK(thread->IsExceptionPending());
+    return;
+  }
+
+  // Box arguments.
+  for (size_t i = 0; i < (num_params - 1);++i) {
+    JValue val;
+    switch (shorty[i+1]) {
+      case 'Z':
+      case 'B':
+      case 'C':
+      case 'S':
+      case 'I':
+      case 'F':
+        val.i = va_arg(ap, jint);
+        break;
+      case 'L':
+        val.l = va_arg(ap, Object*);
+        break;
+      case 'D':
+      case 'J':
+        val.j = va_arg(ap, jlong);
+        break;
+    }
+    Class* param_type = param_types->Get(i);
+    if (param_type->IsPrimitive()) {
+      BoxPrimitive(param_type->GetPrimitiveType(), val);
+      if (thread->IsExceptionPending()) {
+        return;
+      }
+    }
+    args->Set(i, val.l);
+  }
+
+  // Get the InvocationHandler method and the field that holds it within the Proxy object
+  static jmethodID inv_hand_invoke_mid = NULL;
+  static jfieldID proxy_inv_hand_fid = NULL;
+  if (proxy_inv_hand_fid == NULL) {
+    ScopedLocalRef<jclass> proxy(env, env->FindClass("java/lang/reflect/Proxy"));
+    proxy_inv_hand_fid = env->GetFieldID(proxy.get(), "h", "Ljava/lang/reflect/InvocationHandler;");
+    ScopedLocalRef<jclass> inv_hand_class(env, env->FindClass("java/lang/reflect/InvocationHandler"));
+    inv_hand_invoke_mid = env->GetMethodID(inv_hand_class.get(), "invoke",
+        "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+  }
+
+  DCHECK(env->IsInstanceOf(rcvr_jobj, env->FindClass("java/lang/reflect/Proxy")));
+
+  jobject inv_hand = env->GetObjectField(rcvr_jobj, proxy_inv_hand_fid);
+  // Call InvocationHandler.invoke
+  jobject result = env->CallObjectMethodA(inv_hand, inv_hand_invoke_mid, args_jobj);
+
+  // Place result in stack args
+  if (!thread->IsExceptionPending()) {
+    if (shorty[0] == 'V') {
+      return;
+    }
+    Object* result_ref = thread->DecodeJObject(result);
+    JValue* result_unboxed = va_arg(ap, JValue*);
+    if (result_ref == NULL) {
+      result_unboxed->l = NULL;
+    } else {
+      bool unboxed_okay = UnboxPrimitive(result_ref, proxy_mh.GetReturnType(), *result_unboxed, "result");
+      if (!unboxed_okay) {
+        thread->ClearException();
+        thread->ThrowNewExceptionF("Ljava/lang/ClassCastException;",
+                                 "Couldn't convert result of type %s to %s",
+                                 PrettyTypeOf(result_ref).c_str(),
+                                 PrettyDescriptor(proxy_mh.GetReturnType()).c_str());
+        return;
+      }
+    }
+  } else {
+    // In the case of checked exceptions that aren't declared, the exception must be wrapped by
+    // a UndeclaredThrowableException.
+    Throwable* exception = thread->GetException();
+    thread->ClearException();
+    if (!exception->IsCheckedException()) {
+      thread->SetException(exception);
+    } else {
+      SynthesizedProxyClass* proxy_class =
+          down_cast<SynthesizedProxyClass*>(proxy_method->GetDeclaringClass());
+      int throws_index = -1;
+      size_t num_virt_methods = proxy_class->NumVirtualMethods();
+      for (size_t i = 0; i < num_virt_methods; i++) {
+        if (proxy_class->GetVirtualMethod(i) == proxy_method) {
+          throws_index = i;
+          break;
+        }
+      }
+      CHECK_NE(throws_index, -1);
+      ObjectArray<Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index);
+      Class* exception_class = exception->GetClass();
+      bool declares_exception = false;
+      for (int i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) {
+        Class* declared_exception = declared_exceptions->Get(i);
+        declares_exception = declared_exception->IsAssignableFrom(exception_class);
+      }
+      if (declares_exception) {
+        thread->SetException(exception);
+      } else {
+        ThrowNewUndeclaredThrowableException(thread, env, exception);
+      }
+    }
+  }
+
+  va_end(ap);
+}
+
 void* art_find_runtime_support_func(void* context, char const* name) {
   struct func_entry_t {
     char const* name;
diff --git a/src/compiler_llvm/runtime_support_llvm.h b/src/compiler_llvm/runtime_support_llvm.h
index ab5c804..1120251 100644
--- a/src/compiler_llvm/runtime_support_llvm.h
+++ b/src/compiler_llvm/runtime_support_llvm.h
@@ -19,6 +19,16 @@
 
 namespace art {
 
+namespace compiler_llvm {
+namespace special_stub {
+  enum SpecialStub {
+    kProxyStub = 16,
+    kMaxSpecialStub
+  };
+}
+}  // namespace compiler_llvm
+
+
 class Method;
 class Object;
 
diff --git a/src/oat/runtime/support_proxy.cc b/src/oat/runtime/support_proxy.cc
index dd92fb1..25cafc2 100644
--- a/src/oat/runtime/support_proxy.cc
+++ b/src/oat/runtime/support_proxy.cc
@@ -17,32 +17,13 @@
 #include "object.h"
 #include "object_utils.h"
 #include "reflection.h"
+#include "runtime_support.h"
 #include "thread.h"
 
 #include "ScopedLocalRef.h"
 
 namespace art {
 
-static void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception) {
-  ScopedLocalRef<jclass> jlr_UTE_class(env,
-      env->FindClass("java/lang/reflect/UndeclaredThrowableException"));
-  if (jlr_UTE_class.get() == NULL) {
-    LOG(ERROR) << "Couldn't throw new \"java/lang/reflect/UndeclaredThrowableException\"";
-  } else {
-    jmethodID jlre_UTE_constructor = env->GetMethodID(jlr_UTE_class.get(), "<init>",
-                                                      "(Ljava/lang/Throwable;)V");
-    jthrowable jexception = AddLocalReference<jthrowable>(env, exception);
-    ScopedLocalRef<jthrowable> jlr_UTE(env,
-        reinterpret_cast<jthrowable>(env->NewObject(jlr_UTE_class.get(), jlre_UTE_constructor,
-                                                    jexception)));
-    int rc = env->Throw(jlr_UTE.get());
-    if (rc != JNI_OK) {
-      LOG(ERROR) << "Couldn't throw new \"java/lang/reflect/UndeclaredThrowableException\"";
-    }
-  }
-  CHECK(self->IsExceptionPending());
-}
-
 // Handler for invocation on proxy methods. On entry a frame will exist for the proxy object method
 // which is responsible for recording callee save registers. We explicitly handlerize incoming
 // reference arguments (so they survive GC) and create a boxed argument array. Finally we invoke
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index d6efe1d..38a680e 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -16,6 +16,8 @@
 
 #include "runtime_support.h"
 
+#include "ScopedLocalRef.h"
+
 namespace art {
 
 void ThrowNewIllegalAccessErrorClass(Thread* self,
@@ -529,4 +531,24 @@
   return klass;
 }
 
+void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception) {
+  ScopedLocalRef<jclass> jlr_UTE_class(env,
+      env->FindClass("java/lang/reflect/UndeclaredThrowableException"));
+  if (jlr_UTE_class.get() == NULL) {
+    LOG(ERROR) << "Couldn't throw new \"java/lang/reflect/UndeclaredThrowableException\"";
+  } else {
+    jmethodID jlre_UTE_constructor = env->GetMethodID(jlr_UTE_class.get(), "<init>",
+                                                      "(Ljava/lang/Throwable;)V");
+    jthrowable jexception = AddLocalReference<jthrowable>(env, exception);
+    ScopedLocalRef<jthrowable> jlr_UTE(env,
+        reinterpret_cast<jthrowable>(env->NewObject(jlr_UTE_class.get(), jlre_UTE_constructor,
+                                                    jexception)));
+    int rc = env->Throw(jlr_UTE.get());
+    if (rc != JNI_OK) {
+      LOG(ERROR) << "Couldn't throw new \"java/lang/reflect/UndeclaredThrowableException\"";
+    }
+  }
+  CHECK(self->IsExceptionPending());
+}
+
 }  // namespace art
diff --git a/src/runtime_support.h b/src/runtime_support.h
index bbb9512..673a535 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -207,6 +207,8 @@
   return class_linker->ResolveString(string_idx, referrer);
 }
 
+extern void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception);
+
 }  // namespace art
 
 #endif  // ART_SRC_RUNTIME_SUPPORT_H_