diff options
| -rw-r--r-- | runtime/arch/mips/quick_entrypoints_mips.S | 109 | ||||
| -rw-r--r-- | runtime/interpreter/mterp/arm/footer.S | 5 | ||||
| -rw-r--r-- | runtime/interpreter/mterp/arm/op_aget.S | 8 | ||||
| -rw-r--r-- | runtime/interpreter/mterp/arm/op_iget_object_quick.S | 18 | ||||
| -rw-r--r-- | runtime/interpreter/mterp/arm/op_iget_quick.S | 8 | ||||
| -rw-r--r-- | runtime/interpreter/mterp/mterp.cc | 9 | ||||
| -rw-r--r-- | runtime/interpreter/mterp/out/mterp_arm.S | 87 | ||||
| -rw-r--r-- | test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc | 59 | ||||
| -rw-r--r-- | test/136-daemon-jni-shutdown/expected.txt | 5 | ||||
| -rw-r--r-- | test/136-daemon-jni-shutdown/info.txt | 1 | ||||
| -rw-r--r-- | test/136-daemon-jni-shutdown/src/Main.java | 47 | ||||
| -rw-r--r-- | test/Android.libarttest.mk | 1 |
12 files changed, 275 insertions, 82 deletions
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 0691f2a620..699ab3e65a 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1312,7 +1312,114 @@ END \name .endm // Generate the allocation entrypoints for each allocator. -GENERATE_ALL_ALLOC_ENTRYPOINTS +GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). +ENTRY art_quick_alloc_object_rosalloc + + # Fast path rosalloc allocation + # a0: type_idx + # a1: ArtMethod* + # s1: Thread::Current + # ----------------------------- + # t0: class + # t1: object size + # t2: rosalloc run + # t3: thread stack top offset + # t4: thread stack bottom offset + # v0: free list head + # + # t5, t6 : temps + + lw $t0, ART_METHOD_DEX_CACHE_TYPES_OFFSET_32($a1) # Load dex cache resolved types + # array. + + sll $t5, $a0, COMPRESSED_REFERENCE_SIZE_SHIFT # Shift the value. + addu $t5, $t0, $t5 # Compute the index. + lw $t0, 0($t5) # Load class (t0). + beqz $t0, .Lart_quick_alloc_object_rosalloc_slow_path + + li $t6, MIRROR_CLASS_STATUS_INITIALIZED + lw $t5, MIRROR_CLASS_STATUS_OFFSET($t0) # Check class status. + bne $t5, $t6, .Lart_quick_alloc_object_rosalloc_slow_path + + # Add a fake dependence from the following access flag and size loads to the status load. This + # is to prevent those loads from being reordered above the status load and reading wrong values. + xor $t5, $t5, $t5 + addu $t0, $t0, $t5 + + lw $t5, MIRROR_CLASS_ACCESS_FLAGS_OFFSET($t0) # Check if access flags has + li $t6, ACCESS_FLAGS_CLASS_IS_FINALIZABLE # kAccClassIsFinalizable. + and $t6, $t5, $t6 + bnez $t6, .Lart_quick_alloc_object_rosalloc_slow_path + + lw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) # Check if thread local allocation + lw $t4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1) # stack has any room left. + bgeu $t3, $t4, .Lart_quick_alloc_object_rosalloc_slow_path + + lw $t1, MIRROR_CLASS_OBJECT_SIZE_OFFSET($t0) # Load object size (t1). + li $t5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE # Check if size is for a thread local + # allocation. + bgtu $t1, $t5, .Lart_quick_alloc_object_rosalloc_slow_path + + # Compute the rosalloc bracket index from the size. Allign up the size by the rosalloc bracket + # quantum size and divide by the quantum size and subtract by 1. + + addiu $t1, $t1, -1 # Decrease obj size and shift right + srl $t1, $t1, ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT # by quantum. + + sll $t2, $t1, POINTER_SIZE_SHIFT + addu $t2, $t2, $s1 + lw $t2, THREAD_ROSALLOC_RUNS_OFFSET($t2) # Load rosalloc run (t2). + + # Load the free list head (v0). + # NOTE: this will be the return val. + + lw $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + beqz $v0, .Lart_quick_alloc_object_rosalloc_slow_path + nop + + # Load the next pointer of the head and update the list head with the next pointer. + + lw $t5, ROSALLOC_SLOT_NEXT_OFFSET($v0) + sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + + # Store the class pointer in the header. This also overwrites the first pointer. The offsets are + # asserted to match. + +#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET +#error "Class pointer needs to overwrite next pointer." +#endif + + POISON_HEAP_REF $t0 + sw $t0, MIRROR_OBJECT_CLASS_OFFSET($v0) + + # Push the new object onto the thread local allocation stack and increment the thread local + # allocation stack top. + + sw $v0, 0($t3) + addiu $t3, $t3, COMPRESSED_REFERENCE_SIZE + sw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) + + # Decrement the size of the free list. + + lw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + addiu $t5, $t5, -1 + sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + + sync # Fence. + + jalr $zero, $ra + nop + + .Lart_quick_alloc_object_rosalloc_slow_path: + + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + jal artAllocObjectFromCodeRosAlloc + move $a2 ,$s1 # Pass self as argument. + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER + +END art_quick_alloc_object_rosalloc /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an diff --git a/runtime/interpreter/mterp/arm/footer.S b/runtime/interpreter/mterp/arm/footer.S index 75e0037145..617f572c0f 100644 --- a/runtime/interpreter/mterp/arm/footer.S +++ b/runtime/interpreter/mterp/arm/footer.S @@ -128,9 +128,11 @@ MterpCheckSuspendAndContinue: */ MterpFallback: EXPORT_PC +#if MTERP_LOGGING mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME bl MterpLogFallback +#endif MterpCommonFallback: mov r0, #0 @ signal retry with reference interpreter. b MterpDone @@ -144,9 +146,6 @@ MterpCommonFallback: * uint32_t* rFP (should still be live, pointer to base of vregs) */ MterpExceptionReturn: - ldr r2, [rFP, #OFF_FP_RESULT_REGISTER] - str r0, [r2] - str r1, [r2, #4] mov r0, #1 @ signal return to caller. b MterpDone MterpReturn: diff --git a/runtime/interpreter/mterp/arm/op_aget.S b/runtime/interpreter/mterp/arm/op_aget.S index 2cc4d66565..11f7079c3f 100644 --- a/runtime/interpreter/mterp/arm/op_aget.S +++ b/runtime/interpreter/mterp/arm/op_aget.S @@ -1,11 +1,11 @@ -%default { "load":"ldr", "shift":"2", "is_object":"0", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } +%default { "load":"ldr", "shift":"2", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } /* * Array get, 32 bits or less. vAA <- vBB[vCC]. * * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 * instructions. We use a pair of FETCH_Bs instead. * - * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short + * for: aget, aget-boolean, aget-byte, aget-char, aget-short * * NOTE: assumes data offset for arrays is the same for all non-wide types. * If this changes, specialize. @@ -25,9 +25,5 @@ FETCH_ADVANCE_INST 2 @ advance rPC, load rINST $load r2, [r0, #$data_offset] @ r2<- vBB[vCC] GET_INST_OPCODE ip @ extract opcode from rINST - .if $is_object - SET_VREG_OBJECT r2, r9 @ vAA<- r2 - .else SET_VREG r2, r9 @ vAA<- r2 - .endif GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_iget_object_quick.S b/runtime/interpreter/mterp/arm/op_iget_object_quick.S index 1f8dc5afb2..fe29106d47 100644 --- a/runtime/interpreter/mterp/arm/op_iget_object_quick.S +++ b/runtime/interpreter/mterp/arm/op_iget_object_quick.S @@ -1 +1,17 @@ -%include "arm/op_iget_quick.S" {"is_object":"1"} + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + mov r2, rINST, lsr #12 @ r2<- B + FETCH r1, 1 @ r1<- field byte offset + GET_VREG r0, r2 @ r0<- object we're operating on + cmp r0, #0 @ check object for null + beq common_errNullObject @ object was null + bl artIGetObjectFromMterp @ (obj, offset) + ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET] + ubfx r2, rINST, #8, #4 @ r2<- A + PREFETCH_INST 2 + cmp r3, #0 + bne MterpPossibleException @ bail out + SET_VREG_OBJECT r0, r2 @ fp[A]<- r0 + ADVANCE 2 @ advance rPC + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_iget_quick.S b/runtime/interpreter/mterp/arm/op_iget_quick.S index 9229afcd1e..0eaf364f6b 100644 --- a/runtime/interpreter/mterp/arm/op_iget_quick.S +++ b/runtime/interpreter/mterp/arm/op_iget_quick.S @@ -1,5 +1,5 @@ -%default { "load":"ldr", "is_object":"0" } - /* For: iget-quick, iget-object-quick */ +%default { "load":"ldr" } + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B FETCH r1, 1 @ r1<- field byte offset @@ -9,10 +9,6 @@ beq common_errNullObject @ object was null $load r0, [r3, r1] @ r0<- obj.field FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - .if $is_object - SET_VREG_OBJECT r0, r2 @ fp[A]<- r0 - .else SET_VREG r0, r2 @ fp[A]<- r0 - .endif GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 060fe76aab..9975458b85 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -607,5 +607,14 @@ extern "C" mirror::Object* artAGetObjectFromMterp(mirror::Object* arr, int32_t i } } +extern "C" mirror::Object* artIGetObjectFromMterp(mirror::Object* obj, uint32_t field_offset) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (UNLIKELY(obj == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + return nullptr; + } + return obj->GetFieldObject<mirror::Object>(MemberOffset(field_offset)); +} + } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 33036e6cd7..2d6f057aa7 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -2013,7 +2013,7 @@ artMterpAsmInstructionStart = .L_op_nop * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 * instructions. We use a pair of FETCH_Bs instead. * - * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short + * for: aget, aget-boolean, aget-byte, aget-char, aget-short * * NOTE: assumes data offset for arrays is the same for all non-wide types. * If this changes, specialize. @@ -2033,11 +2033,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldr r2, [r0, #MIRROR_INT_ARRAY_DATA_OFFSET] @ r2<- vBB[vCC] GET_INST_OPCODE ip @ extract opcode from rINST - .if 0 - SET_VREG_OBJECT r2, r9 @ vAA<- r2 - .else SET_VREG r2, r9 @ vAA<- r2 - .endif GOTO_OPCODE ip @ jump to next instruction /* ------------------------------ */ @@ -2106,7 +2102,7 @@ artMterpAsmInstructionStart = .L_op_nop * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 * instructions. We use a pair of FETCH_Bs instead. * - * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short + * for: aget, aget-boolean, aget-byte, aget-char, aget-short * * NOTE: assumes data offset for arrays is the same for all non-wide types. * If this changes, specialize. @@ -2126,11 +2122,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldrb r2, [r0, #MIRROR_BOOLEAN_ARRAY_DATA_OFFSET] @ r2<- vBB[vCC] GET_INST_OPCODE ip @ extract opcode from rINST - .if 0 - SET_VREG_OBJECT r2, r9 @ vAA<- r2 - .else SET_VREG r2, r9 @ vAA<- r2 - .endif GOTO_OPCODE ip @ jump to next instruction @@ -2145,7 +2137,7 @@ artMterpAsmInstructionStart = .L_op_nop * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 * instructions. We use a pair of FETCH_Bs instead. * - * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short + * for: aget, aget-boolean, aget-byte, aget-char, aget-short * * NOTE: assumes data offset for arrays is the same for all non-wide types. * If this changes, specialize. @@ -2165,11 +2157,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldrsb r2, [r0, #MIRROR_BYTE_ARRAY_DATA_OFFSET] @ r2<- vBB[vCC] GET_INST_OPCODE ip @ extract opcode from rINST - .if 0 - SET_VREG_OBJECT r2, r9 @ vAA<- r2 - .else SET_VREG r2, r9 @ vAA<- r2 - .endif GOTO_OPCODE ip @ jump to next instruction @@ -2184,7 +2172,7 @@ artMterpAsmInstructionStart = .L_op_nop * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 * instructions. We use a pair of FETCH_Bs instead. * - * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short + * for: aget, aget-boolean, aget-byte, aget-char, aget-short * * NOTE: assumes data offset for arrays is the same for all non-wide types. * If this changes, specialize. @@ -2204,11 +2192,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldrh r2, [r0, #MIRROR_CHAR_ARRAY_DATA_OFFSET] @ r2<- vBB[vCC] GET_INST_OPCODE ip @ extract opcode from rINST - .if 0 - SET_VREG_OBJECT r2, r9 @ vAA<- r2 - .else SET_VREG r2, r9 @ vAA<- r2 - .endif GOTO_OPCODE ip @ jump to next instruction @@ -2223,7 +2207,7 @@ artMterpAsmInstructionStart = .L_op_nop * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 * instructions. We use a pair of FETCH_Bs instead. * - * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short + * for: aget, aget-boolean, aget-byte, aget-char, aget-short * * NOTE: assumes data offset for arrays is the same for all non-wide types. * If this changes, specialize. @@ -2243,11 +2227,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldrsh r2, [r0, #MIRROR_SHORT_ARRAY_DATA_OFFSET] @ r2<- vBB[vCC] GET_INST_OPCODE ip @ extract opcode from rINST - .if 0 - SET_VREG_OBJECT r2, r9 @ vAA<- r2 - .else SET_VREG r2, r9 @ vAA<- r2 - .endif GOTO_OPCODE ip @ jump to next instruction @@ -7127,7 +7107,7 @@ constvalop_long_to_double: .balign 128 .L_op_iget_quick: /* 0xe3 */ /* File: arm/op_iget_quick.S */ - /* For: iget-quick, iget-object-quick */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B FETCH r1, 1 @ r1<- field byte offset @@ -7137,11 +7117,7 @@ constvalop_long_to_double: beq common_errNullObject @ object was null ldr r0, [r3, r1] @ r0<- obj.field FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - .if 0 - SET_VREG_OBJECT r0, r2 @ fp[A]<- r0 - .else SET_VREG r0, r2 @ fp[A]<- r0 - .endif GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction @@ -7167,26 +7143,24 @@ constvalop_long_to_double: .balign 128 .L_op_iget_object_quick: /* 0xe5 */ /* File: arm/op_iget_object_quick.S */ -/* File: arm/op_iget_quick.S */ - /* For: iget-quick, iget-object-quick */ + /* For: iget-object-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B FETCH r1, 1 @ r1<- field byte offset - GET_VREG r3, r2 @ r3<- object we're operating on - ubfx r2, rINST, #8, #4 @ r2<- A - cmp r3, #0 @ check object for null + GET_VREG r0, r2 @ r0<- object we're operating on + cmp r0, #0 @ check object for null beq common_errNullObject @ object was null - ldr r0, [r3, r1] @ r0<- obj.field - FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - .if 1 + bl artIGetObjectFromMterp @ (obj, offset) + ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET] + ubfx r2, rINST, #8, #4 @ r2<- A + PREFETCH_INST 2 + cmp r3, #0 + bne MterpPossibleException @ bail out SET_VREG_OBJECT r0, r2 @ fp[A]<- r0 - .else - SET_VREG r0, r2 @ fp[A]<- r0 - .endif + ADVANCE 2 @ advance rPC GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction - /* ------------------------------ */ .balign 128 .L_op_iput_quick: /* 0xe6 */ @@ -7373,7 +7347,7 @@ constvalop_long_to_double: .L_op_iget_boolean_quick: /* 0xef */ /* File: arm/op_iget_boolean_quick.S */ /* File: arm/op_iget_quick.S */ - /* For: iget-quick, iget-object-quick */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B FETCH r1, 1 @ r1<- field byte offset @@ -7383,11 +7357,7 @@ constvalop_long_to_double: beq common_errNullObject @ object was null ldrb r0, [r3, r1] @ r0<- obj.field FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - .if 0 - SET_VREG_OBJECT r0, r2 @ fp[A]<- r0 - .else SET_VREG r0, r2 @ fp[A]<- r0 - .endif GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction @@ -7397,7 +7367,7 @@ constvalop_long_to_double: .L_op_iget_byte_quick: /* 0xf0 */ /* File: arm/op_iget_byte_quick.S */ /* File: arm/op_iget_quick.S */ - /* For: iget-quick, iget-object-quick */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B FETCH r1, 1 @ r1<- field byte offset @@ -7407,11 +7377,7 @@ constvalop_long_to_double: beq common_errNullObject @ object was null ldrsb r0, [r3, r1] @ r0<- obj.field FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - .if 0 - SET_VREG_OBJECT r0, r2 @ fp[A]<- r0 - .else SET_VREG r0, r2 @ fp[A]<- r0 - .endif GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction @@ -7421,7 +7387,7 @@ constvalop_long_to_double: .L_op_iget_char_quick: /* 0xf1 */ /* File: arm/op_iget_char_quick.S */ /* File: arm/op_iget_quick.S */ - /* For: iget-quick, iget-object-quick */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B FETCH r1, 1 @ r1<- field byte offset @@ -7431,11 +7397,7 @@ constvalop_long_to_double: beq common_errNullObject @ object was null ldrh r0, [r3, r1] @ r0<- obj.field FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - .if 0 - SET_VREG_OBJECT r0, r2 @ fp[A]<- r0 - .else SET_VREG r0, r2 @ fp[A]<- r0 - .endif GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction @@ -7445,7 +7407,7 @@ constvalop_long_to_double: .L_op_iget_short_quick: /* 0xf2 */ /* File: arm/op_iget_short_quick.S */ /* File: arm/op_iget_quick.S */ - /* For: iget-quick, iget-object-quick */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ /* op vA, vB, offset@CCCC */ mov r2, rINST, lsr #12 @ r2<- B FETCH r1, 1 @ r1<- field byte offset @@ -7455,11 +7417,7 @@ constvalop_long_to_double: beq common_errNullObject @ object was null ldrsh r0, [r3, r1] @ r0<- obj.field FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - .if 0 - SET_VREG_OBJECT r0, r2 @ fp[A]<- r0 - .else SET_VREG r0, r2 @ fp[A]<- r0 - .endif GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction @@ -12204,9 +12162,11 @@ MterpCheckSuspendAndContinue: */ MterpFallback: EXPORT_PC +#if MTERP_LOGGING mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME bl MterpLogFallback +#endif MterpCommonFallback: mov r0, #0 @ signal retry with reference interpreter. b MterpDone @@ -12220,9 +12180,6 @@ MterpCommonFallback: * uint32_t* rFP (should still be live, pointer to base of vregs) */ MterpExceptionReturn: - ldr r2, [rFP, #OFF_FP_RESULT_REGISTER] - str r0, [r2] - str r1, [r2, #4] mov r0, #1 @ signal return to caller. b MterpDone MterpReturn: diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc new file mode 100644 index 0000000000..54879fbad9 --- /dev/null +++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <iostream> + +#include "base/casts.h" +#include "base/macros.h" +#include "java_vm_ext.h" +#include "jni_env_ext.h" +#include "thread-inl.h" + +namespace art { +namespace { + +static volatile std::atomic<bool> vm_was_shutdown(false); + +extern "C" JNIEXPORT void JNICALL Java_Main_waitAndCallIntoJniEnv(JNIEnv* env, jclass) { + // Wait until the runtime is shutdown. + while (!vm_was_shutdown.load()) { + usleep(1000); + } + std::cout << "About to call exception check\n"; + env->ExceptionCheck(); + LOG(ERROR) << "Should not be reached!"; +} + +// NO_RETURN does not work with extern "C" for target builds. +extern "C" JNIEXPORT void JNICALL Java_Main_destroyJavaVMAndExit(JNIEnv* env, jclass) { + // Fake up the managed stack so we can detach. + Thread* const self = Thread::Current(); + self->SetTopOfStack(nullptr); + self->SetTopOfShadowStack(nullptr); + JavaVM* vm = down_cast<JNIEnvExt*>(env)->vm; + vm->DetachCurrentThread(); + vm->DestroyJavaVM(); + vm_was_shutdown.store(true); + // Give threads some time to get stuck in ExceptionCheck. + usleep(1000000); + if (env != nullptr) { + // Use env != nullptr to trick noreturn. + exit(0); + } +} + +} // namespace +} // namespace art diff --git a/test/136-daemon-jni-shutdown/expected.txt b/test/136-daemon-jni-shutdown/expected.txt new file mode 100644 index 0000000000..f0b6353e9f --- /dev/null +++ b/test/136-daemon-jni-shutdown/expected.txt @@ -0,0 +1,5 @@ +JNI_OnLoad called +About to call exception check +About to call exception check +About to call exception check +About to call exception check diff --git a/test/136-daemon-jni-shutdown/info.txt b/test/136-daemon-jni-shutdown/info.txt new file mode 100644 index 0000000000..06a12dff9e --- /dev/null +++ b/test/136-daemon-jni-shutdown/info.txt @@ -0,0 +1 @@ +Test that daemon threads that call into a JNI env after the runtime is shutdown do not crash.
\ No newline at end of file diff --git a/test/136-daemon-jni-shutdown/src/Main.java b/test/136-daemon-jni-shutdown/src/Main.java new file mode 100644 index 0000000000..6eceb757b1 --- /dev/null +++ b/test/136-daemon-jni-shutdown/src/Main.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Test that daemon threads that call into a JNI env after the runtime is shutdown do not crash. + */ +public class Main { + + public final static int THREAD_COUNT = 4; + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + for (int i = 0; i < THREAD_COUNT; i++) { + Thread t = new Thread(new DaemonRunnable()); + t.setDaemon(true); + t.start(); + } + // Give threads time to start and become stuck in waitAndCallIntoJniEnv. + Thread.sleep(1000); + destroyJavaVMAndExit(); + } + + static native void waitAndCallIntoJniEnv(); + static native void destroyJavaVMAndExit(); + + private static class DaemonRunnable implements Runnable { + public void run() { + for (;;) { + waitAndCallIntoJniEnv(); + } + } + } +} diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index f74a516486..b922b4576e 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -30,6 +30,7 @@ LIBARTTEST_COMMON_SRC_FILES := \ 051-thread/thread_test.cc \ 117-nopatchoat/nopatchoat.cc \ 1337-gc-coverage/gc_coverage.cc \ + 136-daemon-jni-shutdown/daemon_jni_shutdown.cc \ 137-cfi/cfi.cc \ 139-register-natives/regnative.cc \ 141-class-unload/jni_unload.cc \ |