Revert "Revert "ART: Compiler support for invoke-polymorphic.""

This reverts commit 0fb5af1c8287b1ec85c55c306a1c43820c38a337.

This takes us back to the original change and attempts to fix the
issues encountered:

- Adds transition record push/pop around artInvokePolymorphic.
- Changes X86/X64 relocations for MacSDK.
- Implements MIPS entrypoint for art_quick_invoke_polymorphic.
- Corrects size of returned reference in art_quick_invoke_polymorphic
  on ARM.

Bug: 30550796,33191393
Test: art/test/run-test 953
Test: m test-art-run-test

Change-Id: Ib6b93e00b37b9d4ab743a3470ab3d77fe857cda8
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 61d1607..102c313 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -2010,3 +2010,83 @@
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg09, r9
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg10, r10
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11
+
+.extern artInvokePolymorphic
+ENTRY art_quick_invoke_polymorphic
+    SETUP_SAVE_REFS_AND_ARGS_FRAME r2
+    mov     r2, r9                 @ pass Thread::Current
+    mov     r3, sp                 @ pass SP
+    mov     r0, #0                 @ initialize 64-bit JValue as zero.
+    str     r0, [sp, #-4]!
+    .cfi_adjust_cfa_offset 4
+    str     r0, [sp, #-4]!
+    .cfi_adjust_cfa_offset 4
+    mov     r0, sp                 @ pass JValue for return result as first argument.
+    bl      artInvokePolymorphic   @ artInvokePolymorphic(JValue, receiver, Thread*, SP)
+    sub     r0, 'A'                @ return value is descriptor of handle's return type.
+    cmp     r0, 'Z' - 'A'          @ check if value is in bounds of handler table
+    bgt     .Lcleanup_and_return   @ and clean-up if not.
+    adr     r1, .Lhandler_table
+    tbb     [r0, r1]               @ branch to handler for return value based on return type.
+
+.Lstart_of_handlers:
+.Lstore_boolean_result:
+    ldrb    r0, [sp]               @ Copy boolean value to return value of this function.
+    b       .Lcleanup_and_return
+.Lstore_char_result:
+    ldrh    r0, [sp]               @ Copy char value to return value of this function.
+    b       .Lcleanup_and_return
+.Lstore_float_result:
+    vldr    s0, [sp]               @ Copy float value from JValue result to the context restored by
+    vstr    s0, [sp, #16]          @ RESTORE_SAVE_REFS_AND_ARGS_FRAME.
+    b       .Lcleanup_and_return
+.Lstore_double_result:
+    vldr    d0, [sp]               @ Copy double value from JValue result to the context restored by
+    vstr    d0, [sp, #16]          @ RESTORE_SAVE_REFS_AND_ARGS_FRAME.
+    b       .Lcleanup_and_return
+.Lstore_long_result:
+    ldr     r1, [sp, #4]           @ Copy the upper bits from JValue result to the context restored by
+    str     r1, [sp, #80]          @ RESTORE_SAVE_REFS_AND_ARGS_FRAME.
+    // Fall-through for lower bits.
+.Lstore_int_result:
+    ldr     r0, [sp]               @ Copy int value to return value of this function.
+    // Fall-through to clean up and return.
+.Lcleanup_and_return:
+    add     sp, #8
+    .cfi_adjust_cfa_offset -8
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2
+
+.macro HANDLER_TABLE_OFFSET handler_label
+    .byte (\handler_label - .Lstart_of_handlers) / 2
+.endm
+
+.Lhandler_table:
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // A
+    HANDLER_TABLE_OFFSET(.Lstore_int_result)      // B (byte)
+    HANDLER_TABLE_OFFSET(.Lstore_char_result)     // C (char)
+    HANDLER_TABLE_OFFSET(.Lstore_double_result)   // D (double)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // E
+    HANDLER_TABLE_OFFSET(.Lstore_float_result)    // F (float)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // G
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // H
+    HANDLER_TABLE_OFFSET(.Lstore_int_result)      // I (int)
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // J (long)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // K
+    HANDLER_TABLE_OFFSET(.Lstore_int_result)      // L (object)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // M
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // N
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // O
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // P
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // Q
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // R
+    HANDLER_TABLE_OFFSET(.Lstore_int_result)      // S (short)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // T
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // U
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // V (void)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // W
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // X
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // Y
+    HANDLER_TABLE_OFFSET(.Lstore_boolean_result)  // Z (boolean)
+.purgem HANDLER_TABLE_OFFSET
+END art_quick_invoke_polymorphic
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 8b1e038..3b3783c 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2567,3 +2567,82 @@
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg27, w27, x27
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg28, w28, x28
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, w29, x29
+
+.extern artInvokePolymorphic
+ENTRY art_quick_invoke_polymorphic
+    SETUP_SAVE_REFS_AND_ARGS_FRAME                // Save callee saves in case allocation triggers GC.
+    mov     x2, xSELF
+    mov     x3, sp
+    INCREASE_FRAME 16                             // Reserve space for JValue result.
+    str     xzr, [sp, #0]                         // Initialize result to zero.
+    mov     x0, sp                                // Set r0 to point to result.
+    bl      artInvokePolymorphic                  // ArtInvokePolymorphic(result, receiver, thread, save_area)
+    uxtb    w0, w0                                // Result is the return type descriptor as a char.
+    sub     w0, w0, 'A'                           // Convert to zero based index.
+    cmp     w0, 'Z' - 'A'
+    bhi     .Lcleanup_and_return                  // Clean-up if out-of-bounds.
+    adrp    x1, .Lhandler_table                   // Compute address of handler table.
+    add     x1, x1, :lo12:.Lhandler_table
+    ldrb    w0, [x1, w0, uxtw]                    // Lookup handler offset in handler table.
+    adr     x1, .Lstart_of_handlers
+    add     x0, x1, w0, sxtb #2                   // Convert relative offset to absolute address.
+    br      x0                                    // Branch to handler.
+
+.Lstart_of_handlers:
+.Lstore_boolean_result:
+    ldrb    w0, [sp]
+    b       .Lcleanup_and_return
+.Lstore_char_result:
+    ldrh    w0, [sp]
+    b       .Lcleanup_and_return
+.Lstore_float_result:
+    ldr     s0, [sp]
+    str     s0, [sp, #32]
+    b       .Lcleanup_and_return
+.Lstore_double_result:
+    ldr     d0, [sp]
+    str     d0, [sp, #32]
+    b       .Lcleanup_and_return
+.Lstore_long_result:
+    ldr     x0, [sp]
+    // Fall-through
+.Lcleanup_and_return:
+    DECREASE_FRAME 16
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+
+    .section    .rodata                           // Place handler table in read-only section away from text.
+    .align  2
+.macro HANDLER_TABLE_OFFSET handler_label
+    .byte (\handler_label - .Lstart_of_handlers) / 4
+.endm
+.Lhandler_table:
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // A
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // B (byte)
+    HANDLER_TABLE_OFFSET(.Lstore_char_result)     // C (char)
+    HANDLER_TABLE_OFFSET(.Lstore_double_result)   // D (double)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // E
+    HANDLER_TABLE_OFFSET(.Lstore_float_result)    // F (float)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // G
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // H
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // I (int)
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // J (long)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // K
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // L (object - references are compressed and only 32-bits)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // M
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // N
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // O
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // P
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // Q
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // R
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)     // S (short)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // T
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // U
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // V (void)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // W
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // X
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)    // Y
+    HANDLER_TABLE_OFFSET(.Lstore_boolean_result)  // Z (boolean)
+    .text
+
+END  art_quick_invoke_polymorphic
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 964ea56..3acc0a9 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -2230,7 +2230,7 @@
   li    $v0, -1         #     return -1;
 
   sll   $v0, $a2, 1     # $a0 += $a2 * 2
-  addu  $a0, $a0, $v0   #  "  "   "  " "
+  addu  $a0, $a0, $v0   #  "  ditto  "
   move  $v0, $a2        # Set i to fromIndex.
 
 1:
@@ -2280,3 +2280,65 @@
   j      $ra
   nop
 END art_quick_string_compareto
+
+.extern artInvokePolymorphic
+ENTRY art_quick_invoke_polymorphic
+    SETUP_SAVE_REFS_AND_ARGS_FRAME
+    move  $a2, rSELF                          # Make $a2 an alias for the current Thread.
+    move  $a3, $sp                            # Make $a3 a pointer to the saved frame context.
+    addiu $sp, $sp, -24                       # Reserve space for JValue result and 4 words for callee.
+    .cfi_adjust_cfa_offset 24
+    sw    $zero, 20($sp)                      # Initialize JValue result.
+    sw    $zero, 16($sp)
+    addiu $a0, $sp, 16                        # Make $a0 a pointer to the JValue result
+    la    $t9, artInvokePolymorphic
+    jalr  $t9                                 # (result, receiver, Thread*, context)
+    nop
+.macro MATCH_RETURN_TYPE c, handler
+    li    $t0, \c
+    beq   $v0, $t0, \handler
+.endm
+    MATCH_RETURN_TYPE 'V', .Lcleanup_and_return
+    MATCH_RETURN_TYPE 'L', .Lstore_int_result
+    MATCH_RETURN_TYPE 'I', .Lstore_int_result
+    MATCH_RETURN_TYPE 'J', .Lstore_long_result
+    MATCH_RETURN_TYPE 'B', .Lstore_int_result
+    MATCH_RETURN_TYPE 'C', .Lstore_char_result
+    MATCH_RETURN_TYPE 'D', .Lstore_double_result
+    MATCH_RETURN_TYPE 'F', .Lstore_float_result
+    MATCH_RETURN_TYPE 'S', .Lstore_int_result
+.purgem MATCH_RETURN_TYPE
+    nop
+    b .Lcleanup_and_return
+    nop
+.Lstore_boolean_result:
+    lbu   $v0, 16($sp)                        # Move byte from JValue result to return value register.
+    b .Lcleanup_and_return
+    nop
+.Lstore_char_result:
+    lhu   $v0, 16($sp)                        # Move char from JValue result to return value register.
+    b .Lcleanup_and_return
+    nop
+.Lstore_double_result:
+.Lstore_float_result:
+    LDu   $f0, $f1, 16, $sp, $t0              # Move double/float from JValue result to return value register.
+    b .Lcleanup_and_return
+    nop
+.Lstore_long_result:
+    lw    $v1, 20($sp)                        # Move upper bits from JValue result to return value register.
+    // Fall-through for lower bits.
+.Lstore_int_result:
+    lw    $v0, 16($sp)                        # Move lower bits from JValue result to return value register.
+    // Fall-through to clean up and return.
+.Lcleanup_and_return:
+    addiu $sp, $sp, 24                        # Remove space for JValue result and the 4 words for the callee.
+    .cfi_adjust_cfa_offset -24
+    lw    $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    bnez  $t7, 1f                             # Success if no exception is pending.
+    nop
+    jalr  $zero, $ra
+    nop
+1:
+    DELIVER_PENDING_EXCEPTION
+END art_quick_invoke_polymorphic
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 2a18d53..ae786fe 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -2105,7 +2105,7 @@
   li    $v0,-1          #     return -1;
 
   sll   $v0,$a2,1       # $a0 += $a2 * 2
-  daddu $a0,$a0,$v0     #  "  "   "  " "
+  daddu $a0,$a0,$v0     #  "  ditto  "
   move  $v0,$a2         # Set i to fromIndex.
 
 1:
@@ -2124,4 +2124,65 @@
   nop
 END art_quick_indexof
 
+.extern artInvokePolymorphic
+ENTRY art_quick_invoke_polymorphic
+    SETUP_SAVE_REFS_AND_ARGS_FRAME
+    move   $a2, rSELF                          # Make $a2 an alias for the current Thread.
+    move   $a3, $sp                            # Make $a3 a pointer to the saved frame context.
+    daddiu $sp, $sp, -8                        # Reserve space for JValue result.
+    .cfi_adjust_cfa_offset 8
+    sd     $zero, 0($sp)                       # Initialize JValue result.
+    move   $a0, $sp                            # Make $a0 a pointer to the JValue result
+    jal    artInvokePolymorphic                # (result, receiver, Thread*, context)
+    nop
+.macro MATCH_RETURN_TYPE c, handler
+    li     $t0, \c
+    beq    $v0, $t0, \handler
+.endm
+    MATCH_RETURN_TYPE 'V', .Lcleanup_and_return
+    MATCH_RETURN_TYPE 'L', .Lstore_ref_result
+    MATCH_RETURN_TYPE 'I', .Lstore_long_result
+    MATCH_RETURN_TYPE 'J', .Lstore_long_result
+    MATCH_RETURN_TYPE 'B', .Lstore_long_result
+    MATCH_RETURN_TYPE 'C', .Lstore_char_result
+    MATCH_RETURN_TYPE 'D', .Lstore_double_result
+    MATCH_RETURN_TYPE 'F', .Lstore_float_result
+    MATCH_RETURN_TYPE 'S', .Lstore_long_result
+.purgem MATCH_RETURN_TYPE
+    nop
+    b .Lcleanup_and_return
+    nop
+.Lstore_boolean_result:
+    lbu    $v0, 0($sp)                         # Move byte from JValue result to return value register.
+    b      .Lcleanup_and_return
+    nop
+.Lstore_char_result:
+    lhu    $v0, 0($sp)                         # Move char from JValue result to return value register.
+    b      .Lcleanup_and_return
+    nop
+.Lstore_double_result:
+.Lstore_float_result:
+    l.d    $f0, 0($sp)                         # Move double/float from JValue result to return value register.
+    b      .Lcleanup_and_return
+    nop
+.Lstore_ref_result:
+    lwu    $v0, 0($sp)                         # Move zero extended lower 32-bits to return value register.
+    b      .Lcleanup_and_return
+    nop
+.Lstore_long_result:
+    ld     $v0, 0($sp)                         # Move long from JValue result to return value register.
+    // Fall-through to clean up and return.
+.Lcleanup_and_return:
+    daddiu $sp, $sp, 8                         # Remove space for JValue result.
+    .cfi_adjust_cfa_offset -8
+    ld     $t0, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    bnez   $t0, 1f                             # Success if no exception is pending.
+    nop
+    jalr   $zero, $ra
+    nop
+1:
+    DELIVER_PENDING_EXCEPTION
+END art_quick_invoke_polymorphic
+
   .set pop
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 62c29cf..1d979d8 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -468,7 +468,7 @@
      * The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting
      * of the target Method* in r0 and method->code_ in r1.
      *
-     * If unsuccessful, the helper will return null/null will bea pending exception in the
+     * If unsuccessful, the helper will return null/null and there will be a pending exception in the
      * thread and we branch to another stub to deliver it.
      *
      * On success this wrapper will restore arguments and *jump* to the target, leaving the lr
@@ -2223,5 +2223,99 @@
     jmp *%ebx
 END_FUNCTION art_quick_osr_stub
 
+DEFINE_FUNCTION art_quick_invoke_polymorphic
+    SETUP_SAVE_REFS_AND_ARGS_FRAME  ebx, ebx       // Save frame.
+    mov %esp, %edx                                 // Remember SP.
+    subl LITERAL(16), %esp                         // Make space for JValue result.
+    CFI_ADJUST_CFA_OFFSET(16)
+    movl LITERAL(0), (%esp)                        // Initialize result to zero.
+    movl LITERAL(0), 4(%esp)
+    mov %esp, %eax                                 // Store pointer to JValue result in eax.
+    PUSH edx                                       // pass SP
+    pushl %fs:THREAD_SELF_OFFSET                   // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    PUSH ecx                                       // pass receiver (method handle)
+    PUSH eax                                       // pass JResult
+    call SYMBOL(artInvokePolymorphic)              // (result, receiver, Thread*, SP)
+    subl LITERAL('A'), %eax                        // Eliminate out of bounds options
+    cmpb LITERAL('Z' - 'A'), %al
+    ja .Lcleanup_and_return
+    movzbl %al, %eax
+    call .Lput_eip_in_ecx
+.Lbranch_start:
+    movl %ecx, %edx
+    add $(.Lhandler_table - .Lbranch_start), %edx  // Make EDX point to handler_table.
+    leal (%edx, %eax, 2), %eax                     // Calculate address of entry in table.
+    movzwl (%eax), %eax                            // Lookup relative branch in table.
+    addl %ecx, %eax                                // Add EIP relative offset.
+    jmp *%eax                                      // Branch to handler.
+
+    // Handlers for different return types.
+.Lstore_boolean_result:
+    movzbl 16(%esp), %eax                          // Copy boolean result to the accumulator.
+    jmp .Lcleanup_and_return
+.Lstore_char_result:
+    movzwl 16(%esp), %eax                          // Copy char result to the accumulator.
+    jmp .Lcleanup_and_return
+.Lstore_float_result:
+    movd 16(%esp), %xmm0                           // Copy float result to the context restored by
+    movd %xmm0, 36(%esp)                           // RESTORE_SAVE_REFS_ONLY_FRAME.
+    jmp .Lcleanup_and_return
+.Lstore_double_result:
+    movsd 16(%esp), %xmm0                          // Copy double result to the context restored by
+    movsd %xmm0, 36(%esp)                          // RESTORE_SAVE_REFS_ONLY_FRAME.
+    jmp .Lcleanup_and_return
+.Lstore_long_result:
+    movl 20(%esp), %edx                            // Copy upper-word of result to the context restored by
+    movl %edx, 72(%esp)                            // RESTORE_SAVE_REFS_ONLY_FRAME.
+    // Fall-through for lower bits.
+.Lstore_int_result:
+    movl 16(%esp), %eax                            // Copy int result to the accumulator.
+    // Fall-through to clean up and return.
+.Lcleanup_and_return:
+    addl LITERAL(32), %esp                         // Pop arguments and stack allocated JValue result.
+    CFI_ADJUST_CFA_OFFSET(-32)
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    RETURN_OR_DELIVER_PENDING_EXCEPTION
+
+.Lput_eip_in_ecx:                                  // Internal function that puts address of
+    movl 0(%esp), %ecx                             // next instruction into ECX when CALL
+    ret
+
+    // Handler table to handlers for given type.
+.Lhandler_table:
+MACRO1(HANDLER_TABLE_ENTRY, handler_label)
+    // NB some tools require 16-bits for relocations. Shouldn't need adjusting.
+    .word RAW_VAR(handler_label) - .Lbranch_start
+END_MACRO
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // A
+    HANDLER_TABLE_ENTRY(.Lstore_int_result)        // B (byte)
+    HANDLER_TABLE_ENTRY(.Lstore_char_result)       // C (char)
+    HANDLER_TABLE_ENTRY(.Lstore_double_result)     // D (double)
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // E
+    HANDLER_TABLE_ENTRY(.Lstore_float_result)      // F (float)
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // G
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // H
+    HANDLER_TABLE_ENTRY(.Lstore_int_result)        // I (int)
+    HANDLER_TABLE_ENTRY(.Lstore_long_result)       // J (long)
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // K
+    HANDLER_TABLE_ENTRY(.Lstore_int_result)        // L (object)
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // M
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // N
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // O
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // P
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // Q
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // R
+    HANDLER_TABLE_ENTRY(.Lstore_int_result)        // S (short)
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // T
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // U
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // V (void)
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // W
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // X
+    HANDLER_TABLE_ENTRY(.Lcleanup_and_return)      // Y
+    HANDLER_TABLE_ENTRY(.Lstore_boolean_result)    // Z (boolean)
+
+END_FUNCTION art_quick_invoke_polymorphic
+
     // TODO: implement these!
 UNIMPLEMENTED art_quick_memcmp16
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index facd563..28034c9 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -2394,3 +2394,79 @@
     rep movsb                     // while (rcx--) { *rdi++ = *rsi++ }
     jmp *%rdx
 END_FUNCTION art_quick_osr_stub
+
+DEFINE_FUNCTION art_quick_invoke_polymorphic
+    SETUP_SAVE_REFS_AND_ARGS_FRAME                 // save callee saves
+    movq %gs:THREAD_SELF_OFFSET, %rdx              // pass Thread
+    movq %rsp, %rcx                                // pass SP
+    subq LITERAL(16), %rsp                         // make space for JValue result
+    CFI_ADJUST_CFA_OFFSET(16)
+    movq LITERAL(0), (%rsp)                        // initialize result
+    movq %rsp, %rdi                                // store pointer to JValue result
+    call SYMBOL(artInvokePolymorphic)              // artInvokePolymorphic(result, receiver, Thread*, SP)
+                                                   // save the code pointer
+    subq LITERAL('A'), %rax                        // Convert type descriptor character value to a zero based index.
+    cmpb LITERAL('Z' - 'A'), %al                   // Eliminate out of bounds options
+    ja .Lcleanup_and_return
+    movzbq %al, %rax
+    leaq .Lhandler_table(%rip), %rcx               // Get the address of the handler table
+    movslq (%rcx, %rax, 4), %rax                   // Lookup handler offset relative to table
+    addq %rcx, %rax                                // Add table address to yield handler address.
+    jmpq *%rax                                     // Jump to handler.
+
+.align 4
+.Lhandler_table:                                   // Table of type descriptor to handlers.
+MACRO1(HANDLER_TABLE_OFFSET, handle_label)
+    // NB some tools require 32-bits for relocations. Shouldn't need adjusting.
+    .long RAW_VAR(handle_label) - .Lhandler_table
+END_MACRO
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // A
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // B (byte)
+    HANDLER_TABLE_OFFSET(.Lstore_char_result)      // C (char)
+    HANDLER_TABLE_OFFSET(.Lstore_double_result)    // D (double)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // E
+    HANDLER_TABLE_OFFSET(.Lstore_float_result)     // F (float)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // G
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // H
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // I (int)
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // J (long)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // K
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // L (object - references are compressed and only 32-bits)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // M
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // N
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // O
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // P
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // Q
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // R
+    HANDLER_TABLE_OFFSET(.Lstore_long_result)      // S (short)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // T
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // U
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // V (void)
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // W
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // X
+    HANDLER_TABLE_OFFSET(.Lcleanup_and_return)     // Y
+    HANDLER_TABLE_OFFSET(.Lstore_boolean_result)   // Z (boolean)
+
+.Lstore_boolean_result:
+    movzbq (%rsp), %rax                            // Copy boolean result to the accumulator
+    jmp .Lcleanup_and_return
+.Lstore_char_result:
+    movzwq (%rsp), %rax                            // Copy char result to the accumulator
+    jmp .Lcleanup_and_return
+.Lstore_float_result:
+    movd (%rsp), %xmm0                             // Copy float result to the context restored by
+    movd %xmm0, 32(%rsp)                           // RESTORE_SAVE_REFS_AND_ARGS_FRAME.
+    jmp .Lcleanup_and_return
+.Lstore_double_result:
+    movsd (%rsp), %xmm0                            // Copy double result to the context restored by
+    movsd %xmm0, 32(%rsp)                          // RESTORE_SAVE_REFS_AND_ARGS_FRAME.
+    jmp .Lcleanup_and_return
+.Lstore_long_result:
+    movq (%rsp), %rax                              // Copy long result to the accumulator.
+     // Fall-through
+.Lcleanup_and_return:
+    addq LITERAL(16), %rsp                         // Pop space for JValue result.
+    CFI_ADJUST_CFA_OFFSET(16)
+    RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    RETURN_OR_DELIVER_PENDING_EXCEPTION
+END_FUNCTION art_quick_invoke_polymorphic