Merge "Disassemble x86 opcode 0xc7." into ics-mr1-plus-art
diff --git a/src/compiler/codegen/GenCommon.cc b/src/compiler/codegen/GenCommon.cc
index aeacab8..d2628bb 100644
--- a/src/compiler/codegen/GenCommon.cc
+++ b/src/compiler/codegen/GenCommon.cc
@@ -1392,8 +1392,8 @@
 #endif
     /* branch target here */
     LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
-    branch1->target = (LIR*)target;
-    branch2->target = (LIR*)target;
+    branch1->target = target;
+    branch2->target = target;
 }
 
 /*
@@ -1403,73 +1403,70 @@
 void genArrayObjPut(CompilationUnit* cUnit, MIR* mir, RegLocation rlArray,
                     RegLocation rlIndex, RegLocation rlSrc, int scale)
 {
-    RegisterClass regClass = oatRegClassBySize(kWord);
     int lenOffset = Array::LengthOffset().Int32Value();
     int dataOffset = Array::DataOffset(sizeof(Object*)).Int32Value();
 
-    oatFlushAllRegs(cUnit);
-    /* Make sure it's a legal object Put. Use direct regs at first */
-    loadValueDirectFixed(cUnit, rlArray, rARG1);
-    loadValueDirectFixed(cUnit, rlSrc, rARG0);
+    oatFlushAllRegs(cUnit);  // Use explicit registers
+    oatLockCallTemps(cUnit);
 
-    /* null array object? */
-    genNullCheck(cUnit, rlArray.sRegLow, rARG1, mir);
-    /* Get the array's class */
-    loadWordDisp(cUnit, rARG1, Object::ClassOffset().Int32Value(), rARG1);
+    int rValue = rARG0;  // Register holding value
+    int rArrayClass = rARG1;  // Register holding array's Class
+    int rArray = rARG2;  // Register holding array
+    int rIndex = rARG3;  // Register holding index into array
+
+    loadValueDirectFixed(cUnit, rlArray, rArray);  // Grab array
+    loadValueDirectFixed(cUnit, rlSrc, rValue);    // Grab value
+    loadValueDirectFixed(cUnit, rlIndex, rIndex);  // Grab index
+
+    genNullCheck(cUnit, rlArray.sRegLow, rArray, mir);  // NPE?
+
+    // Store of null?
+    LIR* null_value_check = opCmpImmBranch(cUnit, kCondEq, rValue, 0, NULL);
+
+    // Get the array's class.
+    loadWordDisp(cUnit, rArray, Object::ClassOffset().Int32Value(), rArrayClass);
     callRuntimeHelperRegReg(cUnit, ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode),
-                            rARG0, rARG1);
-    oatFreeTemp(cUnit, rARG0);
-    oatFreeTemp(cUnit, rARG1);
+                            rValue, rArrayClass);
+    // Redo loadValues in case they didn't survive the call.
+    loadValueDirectFixed(cUnit, rlArray, rArray);  // Reload array
+    loadValueDirectFixed(cUnit, rlIndex, rIndex);  // Reload index
+    loadValueDirectFixed(cUnit, rlSrc, rValue);    // Reload value
+    rArrayClass = INVALID_REG;
 
-    // Now, redo loadValues in case they didn't survive the call
-
-    rlArray = loadValue(cUnit, rlArray, kCoreReg);
-    rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+    // Branch here if value to be stored == null
+    LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
+    null_value_check->target = target;
 
 #if defined(TARGET_X86)
+    // make an extra temp available for card mark below
+    oatFreeTemp(cUnit, rARG1);
     if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
         /* if (rlIndex >= [rlArray + lenOffset]) goto kThrowArrayBounds */
-        genRegMemCheck(cUnit, kCondUge, rlIndex.lowReg, rlArray.lowReg,
+        genRegMemCheck(cUnit, kCondUge, rIndex, rArray,
                        lenOffset, mir, kThrowArrayBounds);
     }
-    rlSrc = loadValue(cUnit, rlSrc, regClass);
-    storeBaseIndexedDisp(cUnit, NULL, rlArray.lowReg, rlIndex.lowReg, scale,
-                         dataOffset, rlSrc.lowReg, INVALID_REG, kWord,
+    storeBaseIndexedDisp(cUnit, NULL, rArray, rIndex, scale,
+                         dataOffset, rValue, INVALID_REG, kWord,
                          INVALID_SREG);
-    if (oatIsTemp(cUnit, rlIndex.lowReg)) {
-        oatFreeTemp(cUnit, rlIndex.lowReg);
-    }
 #else
-    int regPtr;
-    if (oatIsTemp(cUnit, rlArray.lowReg)) {
-        oatClobber(cUnit, rlArray.lowReg);
-        regPtr = rlArray.lowReg;
-    } else {
-        regPtr = oatAllocTemp(cUnit);
-        opRegCopy(cUnit, regPtr, rlArray.lowReg);
-    }
-
     bool needsRangeCheck = (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK));
     int regLen = INVALID_REG;
     if (needsRangeCheck) {
-        regLen = oatAllocTemp(cUnit);
-        //NOTE: max live temps(4) here.
-        /* Get len */
-        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+        regLen = rARG1;
+        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);  // Get len
     }
-    /* regPtr -> array data */
-    opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
-    /* at this point, regPtr points to array, 2 live temps */
-    rlSrc = loadValue(cUnit, rlSrc, regClass);
+    /* rPtr -> array data */
+    int rPtr = oatAllocTemp(cUnit);
+    opRegRegImm(cUnit, kOpAdd, rPtr, rArray, dataOffset);
     if (needsRangeCheck) {
-        genRegRegCheck(cUnit, kCondCs, rlIndex.lowReg, regLen, mir,
+        genRegRegCheck(cUnit, kCondCs, rIndex, regLen, mir,
                        kThrowArrayBounds);
-        oatFreeTemp(cUnit, regLen);
     }
-    storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
-                     scale, kWord);
+    storeBaseIndexed(cUnit, rPtr, rIndex, rValue, scale, kWord);
+    oatFreeTemp(cUnit, rPtr);
 #endif
-    markGCCard(cUnit, rlSrc.lowReg, rlArray.lowReg);
+    oatFreeTemp(cUnit, rIndex);
+    markGCCard(cUnit, rValue, rArray);
 }
 
 /*
diff --git a/src/compiler/codegen/x86/X86/Factory.cc b/src/compiler/codegen/x86/X86/Factory.cc
index 9421744..2bd5b42 100644
--- a/src/compiler/codegen/x86/X86/Factory.cc
+++ b/src/compiler/codegen/x86/X86/Factory.cc
@@ -173,6 +173,7 @@
 LIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1, int rSrc2)
 {
     X86OpCode opcode = kX86Nop;
+    bool src2_must_be_cx = false;
     switch (op) {
         // X86 unary opcodes
       case kOpMvn:
@@ -184,9 +185,9 @@
         // X86 binary opcodes
       case kOpSub: opcode = kX86Sub32RR; break;
       case kOpSbc: opcode = kX86Sbb32RR; break;
-      case kOpLsl: opcode = kX86Sal32RC; break;
-      case kOpLsr: opcode = kX86Shr32RC; break;
-      case kOpAsr: opcode = kX86Sar32RC; break;
+      case kOpLsl: opcode = kX86Sal32RC; src2_must_be_cx = true; break;
+      case kOpLsr: opcode = kX86Shr32RC; src2_must_be_cx = true; break;
+      case kOpAsr: opcode = kX86Sar32RC; src2_must_be_cx = true; break;
       case kOpMov: opcode = kX86Mov32RR; break;
       case kOpCmp: opcode = kX86Cmp32RR; break;
       case kOpAdd: opcode = kX86Add32RR; break;
@@ -202,6 +203,7 @@
         LOG(FATAL) << "Bad case in opRegReg " << op;
         break;
     }
+    CHECK(!src2_must_be_cx || rSrc2 == rCX);
     return newLIR2(cUnit, opcode, rDestSrc1, rSrc2);
 }
 
diff --git a/src/compiler/codegen/x86/X86RallocUtil.cc b/src/compiler/codegen/x86/X86RallocUtil.cc
index 2971632..a85cb8a 100644
--- a/src/compiler/codegen/x86/X86RallocUtil.cc
+++ b/src/compiler/codegen/x86/X86RallocUtil.cc
@@ -134,6 +134,7 @@
     oatLockTemp(cUnit, rARG0);
     oatLockTemp(cUnit, rARG1);
     oatLockTemp(cUnit, rARG2);
+    oatLockTemp(cUnit, rARG3);
 }
 
 /* To be used when explicitly managing register use */
@@ -142,6 +143,7 @@
     oatFreeTemp(cUnit, rARG0);
     oatFreeTemp(cUnit, rARG1);
     oatFreeTemp(cUnit, rARG2);
+    oatFreeTemp(cUnit, rARG3);
 }
 
 /* Convert an instruction to a NOP */
diff --git a/src/oat/runtime/arm/runtime_support_arm.S b/src/oat/runtime/arm/runtime_support_arm.S
index 9c55e66..e1323d6 100644
--- a/src/oat/runtime/arm/runtime_support_arm.S
+++ b/src/oat/runtime/arm/runtime_support_arm.S
@@ -313,8 +313,6 @@
      */
     ALIGN_FUNCTION_ENTRY
 art_can_put_array_element_from_code:
-    cmp    r0, #0                         @ return if element == NULL
-    bxeq   lr
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME    @ save callee saves in case exception allocation triggers GC
     mov    r2, r9                         @ pass Thread::Current
     mov    r3, sp                         @ pass SP
diff --git a/src/oat/runtime/mips/runtime_support_mips.S b/src/oat/runtime/mips/runtime_support_mips.S
index 12cebfd..9b082bf 100644
--- a/src/oat/runtime/mips/runtime_support_mips.S
+++ b/src/oat/runtime/mips/runtime_support_mips.S
@@ -473,11 +473,6 @@
      */
     ALIGN_FUNCTION_ENTRY
 art_can_put_array_element_from_code:
-    bnez   a0, 1f                       @ return if element == NULL
-    nop
-    jr     ra
-    nop
-1:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME    @ save callee saves in case exception allocation triggers GC
     move    a2, rSELF                      @ pass Thread::Current
     jal     artCanPutArrayElementFromCode  @ (Object* element, Class* array_class, Thread*, SP)
diff --git a/src/oat/runtime/x86/runtime_support_x86.S b/src/oat/runtime/x86/runtime_support_x86.S
index 4f3d334..fd41376 100644
--- a/src/oat/runtime/x86/runtime_support_x86.S
+++ b/src/oat/runtime/x86/runtime_support_x86.S
@@ -21,6 +21,7 @@
     #define MACRO0(macro_name) .macro macro_name
     #define MACRO1(macro_name, macro_arg1) .macro macro_name
     #define MACRO2(macro_name, macro_arg1, macro_args2) .macro macro_name
+    #define MACRO3(macro_name, macro_arg1, macro_args2, macro_args3) .macro macro_name
     #define END_MACRO .endmacro
 
     // Mac OS' as(1) uses $0, $1, and so on for macro arguments, and function names
@@ -35,6 +36,7 @@
     #define MACRO0(macro_name) .macro macro_name
     #define MACRO1(macro_name, macro_arg1) .macro macro_name macro_arg1
     #define MACRO2(macro_name, macro_arg1, macro_arg2) .macro macro_name macro_arg1, macro_arg2
+    #define MACRO3(macro_name, macro_arg1, macro_arg2, macro_arg3) .macro macro_name macro_arg1, macro_arg2, macro_arg3
     #define END_MACRO .endm
 
     // Regular gas(1) uses \argument_name for macro arguments.
@@ -268,7 +270,40 @@
 INVOKE_TRAMPOLINE art_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck
 INVOKE_TRAMPOLINE art_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
-MACRO2(TWO_ARG_ALLOC, c_name, cxx_name)
+MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
+    .globl VAR(c_name, 0)
+    ALIGN_FUNCTION_ENTRY
+VAR(c_name, 0):
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
+    mov %esp, %edx                // remember SP
+    // Outgoing argument set up
+    subl MACRO_LITERAL(8), %esp   // push padding
+    pushl %edx                    // pass SP
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    call VAR(cxx_name, 1)         // cxx_name(Thread*, SP)
+    addl MACRO_LITERAL(16), %esp  // pop arguments
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    VAR(return_macro, 2)          // return or deliver exception
+END_MACRO
+
+MACRO3(ONE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
+    .globl VAR(c_name, 0)
+    ALIGN_FUNCTION_ENTRY
+VAR(c_name, 0):
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
+    mov %esp, %edx                // remember SP
+    // Outgoing argument set up
+    pushl %eax                    // push padding
+    pushl %edx                    // pass SP
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    pushl %eax                    // pass arg1
+    call VAR(cxx_name, 1)         // cxx_name(arg1, Thread*, SP)
+    addl MACRO_LITERAL(16), %esp  // pop arguments
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    VAR(return_macro, 2)          // return or deliver exception
+END_MACRO
+
+MACRO3(TWO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
     .globl VAR(c_name, 0)
     ALIGN_FUNCTION_ENTRY
 VAR(c_name, 0):
@@ -279,17 +314,13 @@
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     pushl %ecx                    // pass arg2
     pushl %eax                    // pass arg1
-    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, arg3, Thread*, SP)
+    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, Thread*, SP)
     addl MACRO_LITERAL(16), %esp  // pop arguments
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
-    testl %eax, %eax              // eax == 0 ?
-    jz  1f
-    ret
-1:
-    DELIVER_PENDING_EXCEPTION
+    VAR(return_macro, 2)          // return or deliver exception
 END_MACRO
 
-MACRO2(THREE_ARG_ALLOC, c_name, cxx_name)
+MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
     .globl VAR(c_name, 0)
     ALIGN_FUNCTION_ENTRY
 VAR(c_name, 0):
@@ -302,72 +333,42 @@
     pushl %edx                    // pass arg3
     pushl %ecx                    // pass arg2
     pushl %eax                    // pass arg1
-    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, Thread*, SP)
+    call VAR(cxx_name, 1)         // cxx_name(arg1, arg2, arg3, Thread*, SP)
     addl MACRO_LITERAL(32), %esp  // pop arguments
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    VAR(return_macro, 2)          // return or deliver exception
+END_MACRO
+
+MACRO0(RETURN_IF_EAX_NOT_ZERO)
     testl %eax, %eax               // eax == 0 ?
-    jz  1f
-    ret
-1:
+    jz  1f                         // if eax == 0 goto 1
+    ret                            // return
+1:                                 // deliver exception on current thread
     DELIVER_PENDING_EXCEPTION
 END_MACRO
 
-TWO_ARG_ALLOC art_alloc_object_from_code, artAllocObjectFromCode
-TWO_ARG_ALLOC art_alloc_object_from_code_with_access_check, artAllocObjectFromCodeWithAccessCheck
-THREE_ARG_ALLOC art_alloc_array_from_code, artAllocArrayFromCode
-THREE_ARG_ALLOC art_alloc_array_from_code_with_access_check, artAllocArrayFromCodeWithAccessCheck
-THREE_ARG_ALLOC art_check_and_alloc_array_from_code, artCheckAndAllocArrayFromCode
-THREE_ARG_ALLOC art_check_and_alloc_array_from_code_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck
-
-TWO_ARG_ALLOC art_resolve_string_from_code, artResolveStringFromCode
-TWO_ARG_ALLOC art_initialize_static_storage_from_code, artInitializeStaticStorageFromCode
-
-DEFINE_FUNCTION art_lock_object_from_code
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    // Outgoing argument set up
-    pushl %eax                    // alignment padding
-    pushl %edx                    // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    pushl %eax                    // pass arg1
-    call SYMBOL(artLockObjectFromCode)    // (Object*, Thread*, SP)
-    addl LITERAL(16), %esp        // pop arguments
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
-    ret
-
-DEFINE_FUNCTION art_unlock_object_from_code
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    // Outgoing argument set up
-    pushl %eax                    // alignment padding
-    pushl %edx                    // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    pushl %eax                    // pass arg1
-    call SYMBOL(artUnlockObjectFromCode)  // (Object*, Thread*, SP)
-    addl LITERAL(16), %esp        // pop arguments
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
-    testl %eax, %eax              // eax == 0 ?
-    jnz 1f
-    ret
-1:
+MACRO0(RETURN_IF_EAX_ZERO)
+    testl %eax, %eax               // eax == 0 ?
+    jnz  1f                        // if eax != 0 goto 1
+    ret                            // return
+1:                                 // deliver exception on current thread
     DELIVER_PENDING_EXCEPTION
+END_MACRO
 
-DEFINE_FUNCTION art_handle_fill_data_from_code
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    // Outgoing argument set up
-    pushl %edx                    // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    pushl %ecx                    // pass arg2
-    pushl %eax                    // pass arg1
-    call SYMBOL(artHandleFillArrayDataFromCode)  // (Array* array, const uint16_t* table, Thread*, SP)
-    addl LITERAL(16), %esp        // pop arguments
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
-    testl %eax, %eax              // eax == 0 ?
-    jnz 1f
-    ret
-1:
-    DELIVER_PENDING_EXCEPTION
+TWO_ARG_DOWNCALL art_alloc_object_from_code, artAllocObjectFromCode, RETURN_IF_EAX_NOT_ZERO
+TWO_ARG_DOWNCALL art_alloc_object_from_code_with_access_check, artAllocObjectFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_alloc_array_from_code, artAllocArrayFromCode, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_alloc_array_from_code_with_access_check, artAllocArrayFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_check_and_alloc_array_from_code, artCheckAndAllocArrayFromCode, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_check_and_alloc_array_from_code_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO
+
+TWO_ARG_DOWNCALL art_resolve_string_from_code, artResolveStringFromCode, RETURN_IF_EAX_NOT_ZERO
+TWO_ARG_DOWNCALL art_initialize_static_storage_from_code, artInitializeStaticStorageFromCode, RETURN_IF_EAX_NOT_ZERO
+
+ONE_ARG_DOWNCALL art_lock_object_from_code, artLockObjectFromCode, ret
+ONE_ARG_DOWNCALL art_unlock_object_from_code, artUnlockObjectFromCode, RETURN_IF_EAX_ZERO
+
+TWO_ARG_DOWNCALL art_handle_fill_data_from_code, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
 
 DEFINE_FUNCTION art_is_assignable_from_code
     pushl %eax                    // alignment padding
@@ -385,22 +386,10 @@
     addl LITERAL(12), %esp        // pop arguments
     ret
 
-DEFINE_FUNCTION art_check_cast_from_code
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    // Outgoing argument set up
-    pushl %edx                    // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    pushl %ecx                    // pass arg2
-    pushl %eax                    // pass arg1
-    call SYMBOL(artCheckCastFromCode)  // (Class* a, Class* b, Thread*, SP)
-    addl LITERAL(16), %esp        // pop arguments
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
-    testl %eax, %eax              // eax == 0 ?
-    jnz 1f
-    ret
-1:
-    DELIVER_PENDING_EXCEPTION
+TWO_ARG_DOWNCALL art_check_cast_from_code, artCheckCastFromCode, RETURN_IF_EAX_ZERO
+TWO_ARG_DOWNCALL art_can_put_array_element_from_code, artCanPutArrayElementFromCode, RETURN_IF_EAX_ZERO
+
+NO_ARG_DOWNCALL art_test_suspend, artTestSuspendFromCode, ret
 
 DEFINE_FUNCTION art_idiv_from_code
     cdq         // edx:eax = sign extend eax
@@ -469,26 +458,6 @@
 1:
     ret
 
-DEFINE_FUNCTION art_can_put_array_element_from_code
-    test %eax, %eax               // Null is trivially storable
-    jz   1f
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    // Outgoing argument set up
-    pushl %edx                    // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    pushl %ecx                    // pass arg2
-    pushl %eax                    // pass arg1
-    call SYMBOL(artCanPutArrayElementFromCode)  // (Object* element, Class* array_class, Thread*, SP)
-    addl LITERAL(16), %esp        // pop arguments
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
-    testl %eax, %eax              // eax == 0 ?
-    jnz 2f
-1:
-    ret
-2:
-    DELIVER_PENDING_EXCEPTION
-
 MACRO1(UNIMPLEMENTED,name)
     .globl VAR(name, 0)
     ALIGN_FUNCTION_ENTRY
@@ -499,7 +468,6 @@
     // TODO: implement these!
 UNIMPLEMENTED art_proxy_invoke_handler
 UNIMPLEMENTED art_update_debugger
-UNIMPLEMENTED art_test_suspend
 UNIMPLEMENTED art_initialize_type_and_verify_access_from_code
 UNIMPLEMENTED art_initialize_type_from_code
 UNIMPLEMENTED art_set32_instance_from_code