Remove use of compiled proxy stub from portable.

ARM implementation is tested, but x86 and MIPS are not.

Change-Id: I497d650c55fe7fd4ea24d283c8d8ee99b1456610
diff --git a/src/class_linker.cc b/src/class_linker.cc
index b5e4020..a9e17b2 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -2585,8 +2585,7 @@
 #if !defined(ART_USE_PORTABLE_COMPILER)
   method->SetCode(reinterpret_cast<void*>(art_quick_proxy_invoke_handler));
 #else
-  OatFile::OatMethod oat_method = GetOatMethodFor(prototype.get());
-  method->SetCode(oat_method.GetProxyStub());
+  method->SetCode(reinterpret_cast<void*>(art_portable_proxy_invoke_handler));
 #endif
 
   return method;
diff --git a/src/oat/runtime/argument_visitor.h b/src/oat/runtime/argument_visitor.h
index df7bc12..07eab50 100644
--- a/src/oat/runtime/argument_visitor.h
+++ b/src/oat/runtime/argument_visitor.h
@@ -22,45 +22,155 @@
 namespace art {
 
 // Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame.
-class ArgumentVisitor {
+class PortableArgumentVisitor {
  public:
 // Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame.
 // Size of Runtime::kRefAndArgs callee save frame.
 // Size of Method* and register parameters in out stack arguments.
 #if defined(__arm__)
-#define CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
-#define CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
-#define STACK_ARG_SKIP 16
+#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
+#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
+#define PORTABLE_STACK_ARG_SKIP 0
 #elif defined(__mips__)
-#define CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
-#define CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
-#define STACK_ARG_SKIP 16
+#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
+#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
+#define PORTABLE_STACK_ARG_SKIP 16
 #elif defined(__i386__)
-#define CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
-#define CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 32
-#define STACK_ARG_SKIP 16
+#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
+#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 32
+#define PORTABLE_STACK_ARG_SKIP 4
 #else
 #error "Unsupported architecture"
-#define CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
-#define CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
-#define STACK_ARG_SKIP 0
+#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
+#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
+#define PORTABLE_STACK_ARG_SKIP 0
 #endif
 
-  ArgumentVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp)
+  PortableArgumentVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
     caller_mh_(caller_mh),
     args_in_regs_(ComputeArgsInRegs(caller_mh)),
     num_params_(caller_mh.NumArgs()),
-    reg_args_(reinterpret_cast<byte*>(sp) + CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
-    stack_args_(reinterpret_cast<byte*>(sp) + CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
-                + STACK_ARG_SKIP),
+    reg_args_(reinterpret_cast<byte*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
+    stack_args_(reinterpret_cast<byte*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
+                + PORTABLE_STACK_ARG_SKIP),
+    cur_args_(reg_args_),
+    cur_arg_index_(0),
+    param_index_(0) {
+  }
+
+  virtual ~PortableArgumentVisitor() {}
+
+  virtual void Visit() = 0;
+
+  bool IsParamAReference() const {
+    return caller_mh_.IsParamAReference(param_index_);
+  }
+
+  bool IsParamALongOrDouble() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return caller_mh_.IsParamALongOrDouble(param_index_);
+  }
+
+  Primitive::Type GetParamPrimitiveType() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return caller_mh_.GetParamPrimitiveType(param_index_);
+  }
+
+  byte* GetParamAddress() const {
+    return cur_args_ + (cur_arg_index_ * kPointerSize);
+  }
+
+  void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    for (cur_arg_index_ = 0;  cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) {
+#if (defined(__arm__) || defined(__mips__))
+      if (IsParamALongOrDouble() && cur_arg_index_ == 2) {
+        break;
+      }
+#endif
+      Visit();
+      cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
+      param_index_++;
+    }
+    cur_args_ = stack_args_;
+    cur_arg_index_ = 0;
+    while (param_index_ < num_params_) {
+#if (defined(__arm__) || defined(__mips__))
+      if (IsParamALongOrDouble() && cur_arg_index_ % 2 != 0) {
+        cur_arg_index_++;
+      }
+#endif
+      Visit();
+      cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
+      param_index_++;
+    }
+  }
+
+ private:
+  static size_t ComputeArgsInRegs(MethodHelper& mh) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if (defined(__i386__))
+    return 0;
+#else
+    size_t args_in_regs = 0;
+    size_t num_params = mh.NumArgs();
+    for (size_t i = 0; i < num_params; i++) {
+      args_in_regs = args_in_regs + (mh.IsParamALongOrDouble(i) ? 2 : 1);
+      if (args_in_regs > 3) {
+        args_in_regs = 3;
+        break;
+      }
+    }
+    return args_in_regs;
+#endif
+  }
+  MethodHelper& caller_mh_;
+  const size_t args_in_regs_;
+  const size_t num_params_;
+  byte* const reg_args_;
+  byte* const stack_args_;
+  byte* cur_args_;
+  size_t cur_arg_index_;
+  size_t param_index_;
+};
+
+// Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame.
+class QuickArgumentVisitor {
+ public:
+// Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame.
+// Size of Runtime::kRefAndArgs callee save frame.
+// Size of Method* and register parameters in out stack arguments.
+#if defined(__arm__)
+#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
+#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
+#define QUICK_STACK_ARG_SKIP 16
+#elif defined(__mips__)
+#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
+#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
+#define QUICK_STACK_ARG_SKIP 16
+#elif defined(__i386__)
+#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
+#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 32
+#define QUICK_STACK_ARG_SKIP 16
+#else
+#error "Unsupported architecture"
+#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
+#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
+#define QUICK_STACK_ARG_SKIP 0
+#endif
+
+  QuickArgumentVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
+    caller_mh_(caller_mh),
+    args_in_regs_(ComputeArgsInRegs(caller_mh)),
+    num_params_(caller_mh.NumArgs()),
+    reg_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
+    stack_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
+                + QUICK_STACK_ARG_SKIP),
     cur_args_(reg_args_),
     cur_arg_index_(0),
     param_index_(0),
     is_split_long_or_double_(false) {
   }
 
-  virtual ~ArgumentVisitor() {}
+  virtual ~QuickArgumentVisitor() {}
 
   virtual void Visit() = 0;
 
@@ -96,7 +206,7 @@
     for (cur_arg_index_ = 0;  cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) {
       is_split_long_or_double_ = (cur_arg_index_ == 2) && IsParamALongOrDouble();
       Visit();
-      cur_arg_index_ += (caller_mh_.IsParamALongOrDouble(param_index_) ? 2 : 1);
+      cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
       param_index_++;
     }
     cur_args_ = stack_args_;
@@ -104,7 +214,7 @@
     is_split_long_or_double_ = false;
     while (param_index_ < num_params_) {
       Visit();
-      cur_arg_index_ += (caller_mh_.IsParamALongOrDouble(param_index_) ? 2 : 1);
+      cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
       param_index_++;
     }
   }
diff --git a/src/oat/runtime/arm/runtime_support_arm.S b/src/oat/runtime/arm/runtime_support_arm.S
index a2ffac7..fe7d69f 100644
--- a/src/oat/runtime/arm/runtime_support_arm.S
+++ b/src/oat/runtime/arm/runtime_support_arm.S
@@ -922,18 +922,32 @@
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
 END art_quick_test_suspend
 
+    .extern artPortableProxyInvokeHandler
+ENTRY art_portable_proxy_invoke_handler
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    str     r0, [sp, #0]           @ place proxy method at bottom of frame
+    mov     r2, r9                 @ pass Thread::Current
+    mov     r3, sp                 @ pass SP
+    blx     artPortableProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
+    ldr     r12, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    ldr     lr,  [sp, #44]         @ restore lr
+    add     sp,  #48               @ pop frame
+    .cfi_adjust_cfa_offset -48
+    bx      lr                     @ return
+END art_portable_proxy_invoke_handler
+
     /*
      * Called by managed code that is attempting to call a method on a proxy class. On entry
      * r0 holds the proxy method and r1 holds the receiver; r2 and r3 may contain arguments. The
      * frame size of the invoked proxy method agrees with a ref and args callee save frame.
      */
-    .extern artProxyInvokeHandler
+     .extern artQuickProxyInvokeHandler
 ENTRY art_quick_proxy_invoke_handler
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
     str     r0, [sp, #0]           @ place proxy method at bottom of frame
     mov     r2, r9                 @ pass Thread::Current
     mov     r3, sp                 @ pass SP
-    blx     artProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
+    blx     artQuickProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
     ldr     r12, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     ldr     lr,  [sp, #44]         @ restore lr
     add     sp,  #48               @ pop frame
diff --git a/src/oat/runtime/mips/runtime_support_mips.S b/src/oat/runtime/mips/runtime_support_mips.S
index a4d3e9c..0fc2437 100644
--- a/src/oat/runtime/mips/runtime_support_mips.S
+++ b/src/oat/runtime/mips/runtime_support_mips.S
@@ -926,17 +926,31 @@
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
 END art_quick_test_suspend
 
+    .extern artPortableProxyInvokeHandler
+ENTRY art_portable_proxy_invoke_handler
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    sw      $a0, 0($sp)            # place proxy method at bottom of frame
+    move    $a2, rSELF             # pass Thread::Current
+    jal     artPortableProxyInvokeHandler  # (Method* proxy method, receiver, Thread*, SP)
+    move    $a3, $sp               # pass $sp
+    lw      $ra, 44($sp)           # restore $ra
+    jr      $ra
+    addiu   $sp, $sp, 48           # pop frame
+    .cfi_adjust_cfa_offset -48
+END art_portable_proxy_invoke_handler
+
     /*
      * Called by managed code that is attempting to call a method on a proxy class. On entry
      * r0 holds the proxy method; r1, r2 and r3 may contain arguments.
      */
-    .extern artProxyInvokeHandler
+    .extern artQuickProxyInvokeHandler
 ENTRY art_quick_proxy_invoke_handler
     GENERATE_GLOBAL_POINTER
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
     sw      $a0, 0($sp)            # place proxy method at bottom of frame
     move    $a2, rSELF             # pass Thread::Current
-    jal     artProxyInvokeHandler  # (Method* proxy method, receiver, Thread*, SP)
+    jal     artQuickProxyInvokeHandler  # (Method* proxy method, receiver, Thread*, SP)
     move    $a3, $sp               # pass $sp
     lw      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
     lw      $ra, 44($sp)           # restore $ra
diff --git a/src/oat/runtime/support_interpreter.cc b/src/oat/runtime/support_interpreter.cc
index 2b23bd7..3ab234f 100644
--- a/src/oat/runtime/support_interpreter.cc
+++ b/src/oat/runtime/support_interpreter.cc
@@ -27,11 +27,11 @@
 namespace art {
 
 // Visits arguments on the stack placing them into the shadow frame.
-class BuildShadowFrameVisitor : public ArgumentVisitor {
+class BuildShadowFrameVisitor : public QuickArgumentVisitor {
  public:
   BuildShadowFrameVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp,
                           ShadowFrame& sf, size_t first_arg_reg) :
-    ArgumentVisitor(caller_mh, sp), sf_(sf), cur_reg_(first_arg_reg) {}
+    QuickArgumentVisitor(caller_mh, sp), sf_(sf), cur_reg_(first_arg_reg) {}
 
   virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     Primitive::Type type = GetParamPrimitiveType();
diff --git a/src/oat/runtime/support_proxy.cc b/src/oat/runtime/support_proxy.cc
index 040a701..d4d0ca1 100644
--- a/src/oat/runtime/support_proxy.cc
+++ b/src/oat/runtime/support_proxy.cc
@@ -32,11 +32,55 @@
 
 // Visits arguments on the stack placing them into the args vector, Object* arguments are converted
 // to jobjects.
-class BuildArgumentVisitor : public ArgumentVisitor {
+class BuildPortableArgumentVisitor : public PortableArgumentVisitor {
  public:
-  BuildArgumentVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp,
-                       ScopedObjectAccessUnchecked& soa, std::vector<jvalue>& args) :
-    ArgumentVisitor(caller_mh, sp), soa_(soa), args_(args) {}
+  BuildPortableArgumentVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp,
+                               ScopedObjectAccessUnchecked& soa, std::vector<jvalue>& args) :
+    PortableArgumentVisitor(caller_mh, sp), soa_(soa), args_(args) {}
+
+  virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    jvalue val;
+    Primitive::Type type = GetParamPrimitiveType();
+    switch (type) {
+      case Primitive::kPrimNot: {
+        mirror::Object* obj = *reinterpret_cast<mirror::Object**>(GetParamAddress());
+        val.l = soa_.AddLocalReference<jobject>(obj);
+        break;
+      }
+      case Primitive::kPrimLong:  // Fall-through.
+      case Primitive::kPrimDouble:
+        val.j = *reinterpret_cast<jlong*>(GetParamAddress());
+        break;
+      case Primitive::kPrimBoolean:  // Fall-through.
+      case Primitive::kPrimByte:     // Fall-through.
+      case Primitive::kPrimChar:     // Fall-through.
+      case Primitive::kPrimShort:    // Fall-through.
+      case Primitive::kPrimInt:      // Fall-through.
+      case Primitive::kPrimFloat:
+        val.i =  *reinterpret_cast<jint*>(GetParamAddress());
+        break;
+      case Primitive::kPrimVoid:
+        LOG(FATAL) << "UNREACHABLE";
+        val.j = 0;
+        break;
+    }
+    args_.push_back(val);
+  }
+
+ private:
+  ScopedObjectAccessUnchecked& soa_;
+  std::vector<jvalue>& args_;
+
+  DISALLOW_COPY_AND_ASSIGN(BuildPortableArgumentVisitor);
+};
+
+// Visits arguments on the stack placing them into the args vector, Object* arguments are converted
+// to jobjects.
+class BuildQuickArgumentVisitor : public QuickArgumentVisitor {
+ public:
+  BuildQuickArgumentVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp,
+                            ScopedObjectAccessUnchecked& soa, std::vector<jvalue>& args) :
+    QuickArgumentVisitor(caller_mh, sp), soa_(soa), args_(args) {}
 
   virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     jvalue val;
@@ -75,14 +119,54 @@
   ScopedObjectAccessUnchecked& soa_;
   std::vector<jvalue>& args_;
 
-  DISALLOW_COPY_AND_ASSIGN(BuildArgumentVisitor);
+  DISALLOW_COPY_AND_ASSIGN(BuildQuickArgumentVisitor);
 };
 
 // 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 place into jobjects the
 // incoming reference arguments (so they survive GC). We invoke the invocation handler, which is a
 // field within the proxy object, which will box the primitive arguments and deal with error cases.
-extern "C" uint64_t artProxyInvokeHandler(mirror::AbstractMethod* proxy_method,
+extern "C" uint64_t artPortableProxyInvokeHandler(mirror::AbstractMethod* proxy_method,
+                                                 mirror::Object* receiver,
+                                                Thread* self, mirror::AbstractMethod** sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Ensure we don't get thread suspension until the object arguments are safely in jobjects.
+  const char* old_cause =
+      self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
+  self->VerifyStack();
+  // Start new JNI local reference state.
+  JNIEnvExt* env = self->GetJniEnv();
+  ScopedObjectAccessUnchecked soa(env);
+  ScopedJniEnvLocalRefState env_state(env);
+  // Create local ref. copies of proxy method and the receiver.
+  jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver);
+
+  // Placing arguments into args vector and remove the receiver.
+  MethodHelper proxy_mh(proxy_method);
+  std::vector<jvalue> args;
+  BuildPortableArgumentVisitor local_ref_visitor(proxy_mh, sp, soa, args);
+  local_ref_visitor.VisitArguments();
+  args.erase(args.begin());
+
+  // Convert proxy method into expected interface method.
+  mirror::AbstractMethod* interface_method = proxy_method->FindOverriddenMethod();
+  DCHECK(interface_method != NULL);
+  DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method);
+  jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method);
+
+  // All naked Object*s should now be in jobjects, so its safe to go into the main invoke code
+  // that performs allocations.
+  self->EndAssertNoThreadSuspension(old_cause);
+  JValue result = InvokeProxyInvocationHandler(soa, proxy_mh.GetShorty(),
+                                               rcvr_jobj, interface_method_jobj, args);
+  return result.GetJ();
+}
+
+// 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 place into jobjects the
+// incoming reference arguments (so they survive GC). We invoke the invocation handler, which is a
+// field within the proxy object, which will box the primitive arguments and deal with error cases.
+extern "C" uint64_t artQuickProxyInvokeHandler(mirror::AbstractMethod* proxy_method,
                                           mirror::Object* receiver,
                                           Thread* self, mirror::AbstractMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -105,7 +189,7 @@
   // Placing arguments into args vector and remove the receiver.
   MethodHelper proxy_mh(proxy_method);
   std::vector<jvalue> args;
-  BuildArgumentVisitor local_ref_visitor(proxy_mh, sp, soa, args);
+  BuildQuickArgumentVisitor local_ref_visitor(proxy_mh, sp, soa, args);
   local_ref_visitor.VisitArguments();
   args.erase(args.begin());
 
diff --git a/src/oat/runtime/x86/runtime_support_x86.S b/src/oat/runtime/x86/runtime_support_x86.S
index f7554a2..4b4689f 100644
--- a/src/oat/runtime/x86/runtime_support_x86.S
+++ b/src/oat/runtime/x86/runtime_support_x86.S
@@ -939,6 +939,22 @@
     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
 END_FUNCTION art_quick_get_obj_static_from_code
 
+DEFINE_FUNCTION art_portable_proxy_invoke_handler
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame and Method*
+    PUSH esp                      // pass SP
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    .cfi_adjust_cfa_offset 4
+    PUSH ecx                      // pass receiver
+    PUSH eax                      // pass proxy method
+    call SYMBOL(artPortableProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
+    movd %eax, %xmm0              // place return value also into floating point return value
+    movd %edx, %xmm1
+    punpckldq %xmm1, %xmm0
+    addl LITERAL(44), %esp        // pop arguments
+    .cfi_adjust_cfa_offset -44
+    ret
+END_FUNCTION art_portable_proxy_invoke_handler
+
 DEFINE_FUNCTION art_quick_proxy_invoke_handler
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame and Method*
     PUSH esp                      // pass SP
@@ -946,7 +962,7 @@
     .cfi_adjust_cfa_offset 4
     PUSH ecx                      // pass receiver
     PUSH eax                      // pass proxy method
-    call SYMBOL(artProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
+    call SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
     movd %eax, %xmm0              // place return value also into floating point return value
     movd %edx, %xmm1
     punpckldq %xmm1, %xmm0
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 1c6c38c..89026c1 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -31,6 +31,7 @@
 #include "thread.h"
 
 extern "C" void art_interpreter_invoke_handler();
+extern "C" void art_portable_proxy_invoke_handler();
 extern "C" void art_quick_proxy_invoke_handler();
 extern "C" void art_work_around_app_jni_bugs();