diff options
79 files changed, 2083 insertions, 833 deletions
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index c98a5f8ba8..88dc29e6ab 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -183,7 +183,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(28U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(112 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); + EXPECT_EQ(113 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); } TEST_F(OatTest, OatHeaderIsValid) { diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 701dbb019b..40502c173b 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -225,7 +225,7 @@ void SsaLivenessAnalysis::ComputeLiveRanges() { // SsaLivenessAnalysis. for (size_t i = 0, e = environment->Size(); i < e; ++i) { HInstruction* instruction = environment->GetInstructionAt(i); - bool should_be_live = ShouldBeLiveForEnvironment(instruction); + bool should_be_live = ShouldBeLiveForEnvironment(current, instruction); if (should_be_live) { DCHECK(instruction->HasSsaIndex()); live_in->SetBit(instruction->GetSsaIndex()); diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 220ee6a8d0..a7044de850 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -1201,8 +1201,14 @@ class SsaLivenessAnalysis : public ValueObject { // Update the live_out set of the block and returns whether it has changed. bool UpdateLiveOut(const HBasicBlock& block); - static bool ShouldBeLiveForEnvironment(HInstruction* instruction) { + // Returns whether `instruction` in an HEnvironment held by `env_holder` + // should be kept live by the HEnvironment. + static bool ShouldBeLiveForEnvironment(HInstruction* env_holder, + HInstruction* instruction) { if (instruction == nullptr) return false; + // A value that's not live in compiled code may still be needed in interpreter, + // due to code motion, etc. + if (env_holder->IsDeoptimize()) return true; if (instruction->GetBlock()->GetGraph()->IsDebuggable()) return true; return instruction->GetType() == Primitive::kPrimNot; } diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index fa85ada864..44efc65e3f 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -1515,6 +1515,14 @@ void X86Assembler::repne_scasw() { } +void X86Assembler::repe_cmpsw() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0xF3); + EmitUint8(0xA7); +} + + X86Assembler* X86Assembler::lock() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF0); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index d1b4e1dc5f..e2abcde624 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -465,6 +465,7 @@ class X86Assembler FINAL : public Assembler { void jmp(Label* label); void repne_scasw(); + void repe_cmpsw(); X86Assembler* lock(); void cmpxchgl(const Address& address, Register reg); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index aacc57bb0c..0e8c4aee0c 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -196,4 +196,10 @@ TEST_F(AssemblerX86Test, Repnescasw) { DriverStr(expected, "Repnescasw"); } +TEST_F(AssemblerX86Test, Repecmpsw) { + GetAssembler()->repe_cmpsw(); + const char* expected = "repe cmpsw\n"; + DriverStr(expected, "Repecmpsw"); +} + } // namespace art diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index f35f51c494..93c90db5d3 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -2073,6 +2073,14 @@ void X86_64Assembler::repne_scasw() { } +void X86_64Assembler::repe_cmpsw() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0xF3); + EmitUint8(0xA7); +} + + void X86_64Assembler::LoadDoubleConstant(XmmRegister dst, double value) { // TODO: Need to have a code constants table. int64_t constant = bit_cast<int64_t, double>(value); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 61ffeab1e8..0cd31971b4 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -603,6 +603,7 @@ class X86_64Assembler FINAL : public Assembler { void bswapq(CpuRegister dst); void repne_scasw(); + void repe_cmpsw(); // // Macros for High-level operations. diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 6da5c35731..422138ce8e 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1263,4 +1263,10 @@ TEST_F(AssemblerX86_64Test, Repnescasw) { DriverStr(expected, "Repnescasw"); } +TEST_F(AssemblerX86_64Test, Repecmpsw) { + GetAssembler()->repe_cmpsw(); + const char* expected = "repe cmpsw\n"; + DriverStr(expected, "Repecmpsw"); +} + } // namespace art diff --git a/runtime/Android.mk b/runtime/Android.mk index fe79e72031..4a944963a2 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -39,6 +39,7 @@ LIBART_COMMON_SRC_FILES := \ base/unix_file/random_access_file_utils.cc \ check_jni.cc \ class_linker.cc \ + class_table.cc \ common_throws.cc \ debugger.cc \ dex_file.cc \ diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 2f2654d4f6..be9af9871d 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -171,6 +171,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Read barrier qpoints->pReadBarrierJni = ReadBarrierJni; + qpoints->pReadBarrierSlow = artReadBarrierSlow; } } // namespace art diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 20001109a6..f6d954f4f1 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -51,7 +51,6 @@ sub sp, #12 @ 3 words of space, bottom word will hold Method* .cfi_adjust_cfa_offset 12 RUNTIME_CURRENT1 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. - THIS_LOAD_REQUIRES_READ_BARRIER ldr \rTemp1, [\rTemp1, #RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kSaveAll Method*. str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. @@ -79,7 +78,6 @@ sub sp, #4 @ bottom word will hold Method* .cfi_adjust_cfa_offset 4 RUNTIME_CURRENT2 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. - THIS_LOAD_REQUIRES_READ_BARRIER ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kRefsOnly Method*. str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. @@ -139,7 +137,6 @@ .macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME rTemp1, rTemp2 SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY RUNTIME_CURRENT3 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. - THIS_LOAD_REQUIRES_READ_BARRIER @ rTemp1 is kRefsAndArgs Method*. ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET] str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. @@ -171,7 +168,6 @@ .cfi_adjust_cfa_offset -40 .endm - .macro RETURN_IF_RESULT_IS_ZERO cbnz r0, 1f @ result non-zero branch over bx lr @ return @@ -588,6 +584,59 @@ ENTRY art_quick_check_cast bkpt END art_quick_check_cast +// Restore rReg's value from [sp, #offset] if rReg is not the same as rExclude. +.macro POP_REG_NE rReg, offset, rExclude + .ifnc \rReg, \rExclude + ldr \rReg, [sp, #\offset] @ restore rReg + .cfi_restore \rReg + .endif +.endm + + /* + * Macro to insert read barrier, only used in art_quick_aput_obj. + * rObj and rDest are registers, offset is a defined literal such as MIRROR_OBJECT_CLASS_OFFSET. + * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path. + */ +.macro READ_BARRIER rDest, rObj, offset +#ifdef USE_READ_BARRIER + push {r0-r3, ip, lr} @ 6 words for saved registers (used in art_quick_aput_obj) + .cfi_adjust_cfa_offset 24 + .cfi_rel_offset r0, 0 + .cfi_rel_offset r1, 4 + .cfi_rel_offset r2, 8 + .cfi_rel_offset r3, 12 + .cfi_rel_offset ip, 16 + .cfi_rel_offset lr, 20 + sub sp, #8 @ push padding + .cfi_adjust_cfa_offset 8 + @ mov r0, r0 @ pass ref in r0 (no-op for now since parameter ref is unused) + .ifnc \rObj, r1 + mov r1, \rObj @ pass rObj + .endif + mov r2, #\offset @ pass offset + bl artReadBarrierSlow @ artReadBarrierSlow(ref, rObj, offset) + @ No need to unpoison return value in r0, artReadBarrierSlow() would do the unpoisoning. + .ifnc \rDest, r0 + mov \rDest, r0 @ save return value in rDest + .endif + add sp, #8 @ pop padding + .cfi_adjust_cfa_offset -8 + POP_REG_NE r0, 0, \rDest @ conditionally restore saved registers + POP_REG_NE r1, 4, \rDest + POP_REG_NE r2, 8, \rDest + POP_REG_NE r3, 12, \rDest + POP_REG_NE ip, 16, \rDest + add sp, #20 + .cfi_adjust_cfa_offset -20 + pop {lr} @ restore lr + .cfi_adjust_cfa_offset -4 + .cfi_restore lr +#else + ldr \rDest, [\rObj, #\offset] + UNPOISON_HEAP_REF \rDest +#endif // USE_READ_BARRIER +.endm + /* * Entry from managed code for array put operations of objects where the value being stored * needs to be checked for compatibility. @@ -609,15 +658,21 @@ ENTRY art_quick_aput_obj_with_bound_check b art_quick_throw_array_bounds END art_quick_aput_obj_with_bound_check +#ifdef USE_READ_BARRIER + .extern artReadBarrierSlow +#endif .hidden art_quick_aput_obj ENTRY art_quick_aput_obj +#ifdef USE_READ_BARRIER + @ The offset to .Ldo_aput_null is too large to use cbz due to expansion from READ_BARRIER macro. + tst r2, r2 + beq .Ldo_aput_null +#else cbz r2, .Ldo_aput_null - ldr r3, [r0, #MIRROR_OBJECT_CLASS_OFFSET] - UNPOISON_HEAP_REF r3 - ldr ip, [r2, #MIRROR_OBJECT_CLASS_OFFSET] - UNPOISON_HEAP_REF ip - ldr r3, [r3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] - UNPOISON_HEAP_REF r3 +#endif // USE_READ_BARRIER + READ_BARRIER r3, r0, MIRROR_OBJECT_CLASS_OFFSET + READ_BARRIER ip, r2, MIRROR_OBJECT_CLASS_OFFSET + READ_BARRIER r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET cmp r3, ip @ value's type == array's component type - trivial assignability bne .Lcheck_assignability .Ldo_aput: diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc index 2ce2a29bbf..0f06727d0d 100644 --- a/runtime/arch/arm64/entrypoints_init_arm64.cc +++ b/runtime/arch/arm64/entrypoints_init_arm64.cc @@ -155,6 +155,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Read barrier qpoints->pReadBarrierJni = ReadBarrierJni; + qpoints->pReadBarrierSlow = artReadBarrierSlow; }; } // namespace art diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 6d9b44a1d2..548ab47f82 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -31,8 +31,6 @@ ldr xIP0, [xIP0] // xIP0 = & (art::Runtime * art::Runtime.instance_) . // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] . - THIS_LOAD_REQUIRES_READ_BARRIER - // Loads appropriate callee-save-method. ldr xIP0, [xIP0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET ] @@ -95,8 +93,6 @@ ldr xIP0, [xIP0] // xIP0 = & (art::Runtime * art::Runtime.instance_) . // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefOnly] . - THIS_LOAD_REQUIRES_READ_BARRIER - // Loads appropriate callee-save-method. ldr xIP0, [xIP0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET ] @@ -251,7 +247,6 @@ ldr xIP0, [xIP0] // xIP0 = & (art::Runtime * art::Runtime.instance_) . // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] . - THIS_LOAD_REQUIRES_READ_BARRIER ldr xIP0, [xIP0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ] SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL @@ -1119,6 +1114,62 @@ ENTRY art_quick_check_cast brk 0 // We should not return here... END art_quick_check_cast +// Restore xReg's value from [sp, #offset] if xReg is not the same as xExclude. +.macro POP_REG_NE xReg, offset, xExclude + .ifnc \xReg, \xExclude + ldr \xReg, [sp, #\offset] // restore xReg + .cfi_restore \xReg + .endif +.endm + + /* + * Macro to insert read barrier, only used in art_quick_aput_obj. + * xDest, wDest and xObj are registers, offset is a defined literal such as + * MIRROR_OBJECT_CLASS_OFFSET. Dest needs both x and w versions of the same register to handle + * name mismatch between instructions. This macro uses the lower 32b of register when possible. + * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path. + */ +.macro READ_BARRIER xDest, wDest, xObj, offset +#ifdef USE_READ_BARRIER + // Store registers used in art_quick_aput_obj (x0-x4, LR), stack is 16B aligned. + stp x0, x1, [sp, #-48]! + .cfi_adjust_cfa_offset 48 + .cfi_rel_offset x0, 0 + .cfi_rel_offset x1, 8 + stp x2, x3, [sp, #16] + .cfi_rel_offset x2, 16 + .cfi_rel_offset x3, 24 + stp x4, xLR, [sp, #32] + .cfi_rel_offset x4, 32 + .cfi_rel_offset x30, 40 + + // mov x0, x0 // pass ref in x0 (no-op for now since parameter ref is unused) + .ifnc \xObj, x1 + mov x1, \xObj // pass xObj + .endif + mov w2, #\offset // pass offset + bl artReadBarrierSlow // artReadBarrierSlow(ref, xObj, offset) + // No need to unpoison return value in w0, artReadBarrierSlow() would do the unpoisoning. + .ifnc \wDest, w0 + mov \wDest, w0 // save return value in wDest + .endif + + // Conditionally restore saved registers + POP_REG_NE x0, 0, \xDest + POP_REG_NE x1, 8, \xDest + POP_REG_NE x2, 16, \xDest + POP_REG_NE x3, 24, \xDest + POP_REG_NE x4, 32, \xDest + ldr xLR, [sp, #40] + .cfi_restore x30 + add sp, sp, #48 + .cfi_adjust_cfa_offset -48 +#else + ldr \wDest, [\xObj, #\offset] // Heap reference = 32b. This also zero-extends to \xDest. + UNPOISON_HEAP_REF \wDest +#endif // USE_READ_BARRIER +.endm + /* * Entry from managed code for array put operations of objects where the value being stored * needs to be checked for compatibility. @@ -1146,17 +1197,17 @@ ENTRY art_quick_aput_obj_with_bound_check b art_quick_throw_array_bounds END art_quick_aput_obj_with_bound_check +#ifdef USE_READ_BARRIER + .extern artReadBarrierSlow +#endif ENTRY art_quick_aput_obj cbz x2, .Ldo_aput_null - ldr w3, [x0, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b + READ_BARRIER x3, w3, x0, MIRROR_OBJECT_CLASS_OFFSET // Heap reference = 32b // This also zero-extends to x3 - UNPOISON_HEAP_REF w3 - ldr w4, [x2, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b + READ_BARRIER x4, w4, x2, MIRROR_OBJECT_CLASS_OFFSET // Heap reference = 32b // This also zero-extends to x4 - UNPOISON_HEAP_REF w4 - ldr w3, [x3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] // Heap reference = 32b + READ_BARRIER x3, w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET // Heap reference = 32b // This also zero-extends to x3 - UNPOISON_HEAP_REF w3 cmp w3, w4 // value's type == array's component type - trivial assignability bne .Lcheck_assignability .Ldo_aput: diff --git a/runtime/arch/mips/entrypoints_direct_mips.h b/runtime/arch/mips/entrypoints_direct_mips.h index b1aa3ee63f..f9c53152f6 100644 --- a/runtime/arch/mips/entrypoints_direct_mips.h +++ b/runtime/arch/mips/entrypoints_direct_mips.h @@ -44,7 +44,8 @@ static constexpr bool IsDirectEntrypoint(QuickEntrypointEnum entrypoint) { entrypoint == kQuickCmpgDouble || entrypoint == kQuickCmpgFloat || entrypoint == kQuickCmplDouble || - entrypoint == kQuickCmplFloat; + entrypoint == kQuickCmplFloat || + entrypoint == kQuickReadBarrierSlow; } } // namespace art diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 09a018ebc6..4e4b91fdcd 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -279,6 +279,8 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pReadBarrierJni = ReadBarrierJni; static_assert(!IsDirectEntrypoint(kQuickReadBarrierJni), "Non-direct C stub marked direct."); + qpoints->pReadBarrierSlow = artReadBarrierSlow; + static_assert(IsDirectEntrypoint(kQuickReadBarrierSlow), "Direct C stub not marked direct."); }; } // namespace art diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 2819f92a0d..4d5004f444 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -79,7 +79,6 @@ lw $t0, %got(_ZN3art7Runtime9instance_E)($gp) lw $t0, 0($t0) - THIS_LOAD_REQUIRES_READ_BARRIER lw $t0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($t0) sw $t0, 0($sp) # Place Method* at bottom of stack. sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. @@ -127,7 +126,6 @@ lw $t0, %got(_ZN3art7Runtime9instance_E)($gp) lw $t0, 0($t0) - THIS_LOAD_REQUIRES_READ_BARRIER lw $t0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($t0) sw $t0, 0($sp) # Place Method* at bottom of stack. sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. @@ -219,7 +217,6 @@ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY lw $t0, %got(_ZN3art7Runtime9instance_E)($gp) lw $t0, 0($t0) - THIS_LOAD_REQUIRES_READ_BARRIER lw $t0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($t0) sw $t0, 0($sp) # Place Method* at bottom of stack. sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. @@ -627,6 +624,76 @@ ENTRY art_quick_check_cast END art_quick_check_cast /* + * Restore rReg's value from offset($sp) if rReg is not the same as rExclude. + * nReg is the register number for rReg. + */ +.macro POP_REG_NE rReg, nReg, offset, rExclude + .ifnc \rReg, \rExclude + lw \rReg, \offset($sp) # restore rReg + .cfi_restore \nReg + .endif +.endm + + /* + * Macro to insert read barrier, only used in art_quick_aput_obj. + * rObj and rDest are registers, offset is a defined literal such as MIRROR_OBJECT_CLASS_OFFSET. + * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path. + */ +.macro READ_BARRIER rDest, rObj, offset +#ifdef USE_READ_BARRIER + # saved registers used in art_quick_aput_obj: a0-a2, t0-t1, t9, ra. 8 words for 16B alignment. + addiu $sp, $sp, -32 + .cfi_adjust_cfa_offset 32 + sw $ra, 28($sp) + .cfi_rel_offset 31, 28 + sw $t9, 24($sp) + .cfi_rel_offset 25, 24 + sw $t1, 20($sp) + .cfi_rel_offset 9, 20 + sw $t0, 16($sp) + .cfi_rel_offset 8, 16 + sw $a2, 8($sp) # padding slot at offset 12 (padding can be any slot in the 32B) + .cfi_rel_offset 6, 8 + sw $a1, 4($sp) + .cfi_rel_offset 5, 4 + sw $a0, 0($sp) + .cfi_rel_offset 4, 0 + + # move $a0, $a0 # pass ref in a0 (no-op for now since parameter ref is unused) + .ifnc \rObj, $a1 + move $a1, \rObj # pass rObj + .endif + addiu $a2, $zero, \offset # pass offset + jal artReadBarrierSlow # artReadBarrierSlow(ref, rObj, offset) + addiu $sp, $sp, -16 # Use branch delay slot to reserve argument slots on the stack + # before the call to artReadBarrierSlow. + addiu $sp, $sp, 16 # restore stack after call to artReadBarrierSlow + # No need to unpoison return value in v0, artReadBarrierSlow() would do the unpoisoning. + move \rDest, $v0 # save return value in rDest + # (rDest cannot be v0 in art_quick_aput_obj) + + lw $a0, 0($sp) # restore registers except rDest + # (rDest can only be t0 or t1 in art_quick_aput_obj) + .cfi_restore 4 + lw $a1, 4($sp) + .cfi_restore 5 + lw $a2, 8($sp) + .cfi_restore 6 + POP_REG_NE $t0, 8, 16, \rDest + POP_REG_NE $t1, 9, 20, \rDest + lw $t9, 24($sp) + .cfi_restore 25 + lw $ra, 28($sp) # restore $ra + .cfi_restore 31 + addiu $sp, $sp, 32 + .cfi_adjust_cfa_offset -32 +#else + lw \rDest, \offset(\rObj) + UNPOISON_HEAP_REF \rDest +#endif // USE_READ_BARRIER +.endm + + /* * Entry from managed code for array put operations of objects where the value being stored * needs to be checked for compatibility. * a0 = array, a1 = index, a2 = value @@ -648,15 +715,15 @@ ENTRY art_quick_aput_obj_with_bound_check move $a1, $t0 END art_quick_aput_obj_with_bound_check +#ifdef USE_READ_BARRIER + .extern artReadBarrierSlow +#endif ENTRY art_quick_aput_obj beqz $a2, .Ldo_aput_null nop - lw $t0, MIRROR_OBJECT_CLASS_OFFSET($a0) - UNPOISON_HEAP_REF $t0 - lw $t1, MIRROR_OBJECT_CLASS_OFFSET($a2) - UNPOISON_HEAP_REF $t1 - lw $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($t0) - UNPOISON_HEAP_REF $t0 + READ_BARRIER $t0, $a0, MIRROR_OBJECT_CLASS_OFFSET + READ_BARRIER $t1, $a2, MIRROR_OBJECT_CLASS_OFFSET + READ_BARRIER $t0, $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET bne $t1, $t0, .Lcheck_assignability # value's type == array's component type - trivial assignability nop .Ldo_aput: diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc index 4904af9cfc..ec02d5ab69 100644 --- a/runtime/arch/mips64/entrypoints_init_mips64.cc +++ b/runtime/arch/mips64/entrypoints_init_mips64.cc @@ -186,6 +186,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Read barrier qpoints->pReadBarrierJni = ReadBarrierJni; + qpoints->pReadBarrierSlow = artReadBarrierSlow; }; } // namespace art diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index abca70b363..c30e6ca93f 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -89,7 +89,6 @@ # load appropriate callee-save-method ld $t1, %got(_ZN3art7Runtime9instance_E)($gp) ld $t1, 0($t1) - THIS_LOAD_REQUIRES_READ_BARRIER ld $t1, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($t1) sd $t1, 0($sp) # Place ArtMethod* at bottom of stack. sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. @@ -132,7 +131,6 @@ # load appropriate callee-save-method ld $t1, %got(_ZN3art7Runtime9instance_E)($gp) ld $t1, 0($t1) - THIS_LOAD_REQUIRES_READ_BARRIER ld $t1, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($t1) sd $t1, 0($sp) # Place Method* at bottom of stack. sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. @@ -255,7 +253,6 @@ # load appropriate callee-save-method ld $t1, %got(_ZN3art7Runtime9instance_E)($gp) ld $t1, 0($t1) - THIS_LOAD_REQUIRES_READ_BARRIER ld $t1, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($t1) sd $t1, 0($sp) # Place Method* at bottom of stack. sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. @@ -888,6 +885,77 @@ ENTRY art_quick_check_cast move $a2, rSELF # pass Thread::Current END art_quick_check_cast + + /* + * Restore rReg's value from offset($sp) if rReg is not the same as rExclude. + * nReg is the register number for rReg. + */ +.macro POP_REG_NE rReg, nReg, offset, rExclude + .ifnc \rReg, \rExclude + ld \rReg, \offset($sp) # restore rReg + .cfi_restore \nReg + .endif +.endm + + /* + * Macro to insert read barrier, only used in art_quick_aput_obj. + * rObj and rDest are registers, offset is a defined literal such as MIRROR_OBJECT_CLASS_OFFSET. + * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path. + */ +.macro READ_BARRIER rDest, rObj, offset +#ifdef USE_READ_BARRIER + # saved registers used in art_quick_aput_obj: a0-a2, t0-t1, t9, ra. 16B-aligned. + daddiu $sp, $sp, -64 + .cfi_adjust_cfa_offset 64 + sd $ra, 56($sp) + .cfi_rel_offset 31, 56 + sd $t9, 48($sp) + .cfi_rel_offset 25, 48 + sd $t1, 40($sp) + .cfi_rel_offset 13, 40 + sd $t0, 32($sp) + .cfi_rel_offset 12, 32 + sd $a2, 16($sp) # padding slot at offset 24 (padding can be any slot in the 64B) + .cfi_rel_offset 6, 16 + sd $a1, 8($sp) + .cfi_rel_offset 5, 8 + sd $a0, 0($sp) + .cfi_rel_offset 4, 0 + + # move $a0, $a0 # pass ref in a0 (no-op for now since parameter ref is unused) + .ifnc \rObj, $a1 + move $a1, \rObj # pass rObj + .endif + daddiu $a2, $zero, \offset # pass offset + jal artReadBarrierSlow # artReadBarrierSlow(ref, rObj, offset) + .cpreturn # Restore gp from t8 in branch delay slot. + # t8 may be clobbered in artReadBarrierSlow. + # No need to unpoison return value in v0, artReadBarrierSlow() would do the unpoisoning. + move \rDest, $v0 # save return value in rDest + # (rDest cannot be v0 in art_quick_aput_obj) + + ld $a0, 0($sp) # restore registers except rDest + # (rDest can only be t0 or t1 in art_quick_aput_obj) + .cfi_restore 4 + ld $a1, 8($sp) + .cfi_restore 5 + ld $a2, 16($sp) + .cfi_restore 6 + POP_REG_NE $t0, 12, 32, \rDest + POP_REG_NE $t1, 13, 40, \rDest + ld $t9, 48($sp) + .cfi_restore 25 + ld $ra, 56($sp) # restore $ra + .cfi_restore 31 + daddiu $sp, $sp, 64 + .cfi_adjust_cfa_offset -64 + SETUP_GP # set up gp because we are not returning +#else + lwu \rDest, \offset(\rObj) + UNPOISON_HEAP_REF \rDest +#endif // USE_READ_BARRIER +.endm + /* * Entry from managed code for array put operations of objects where the value being stored * needs to be checked for compatibility. @@ -913,12 +981,9 @@ END art_quick_aput_obj_with_bound_check ENTRY art_quick_aput_obj beq $a2, $zero, .Ldo_aput_null nop - lwu $t0, MIRROR_OBJECT_CLASS_OFFSET($a0) - UNPOISON_HEAP_REF $t0 - lwu $t1, MIRROR_OBJECT_CLASS_OFFSET($a2) - UNPOISON_HEAP_REF $t1 - lwu $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($t0) - UNPOISON_HEAP_REF $t0 + READ_BARRIER $t0, $a0, MIRROR_OBJECT_CLASS_OFFSET + READ_BARRIER $t1, $a2, MIRROR_OBJECT_CLASS_OFFSET + READ_BARRIER $t0, $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET bne $t1, $t0, .Lcheck_assignability # value's type == array's component type - trivial assignability nop .Ldo_aput: diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index 0831c26e9a..cf7db34ca1 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -1124,8 +1124,6 @@ TEST_F(StubTest, CheckCast) { TEST_F(StubTest, APutObj) { - TEST_DISABLED_FOR_READ_BARRIER(); - #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ (defined(__x86_64__) && !defined(__APPLE__)) Thread* self = Thread::Current(); @@ -1258,8 +1256,6 @@ TEST_F(StubTest, APutObj) { } TEST_F(StubTest, AllocObject) { - TEST_DISABLED_FOR_READ_BARRIER(); - #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ (defined(__x86_64__) && !defined(__APPLE__)) // This will lead to OOM error messages in the log. @@ -1385,8 +1381,6 @@ TEST_F(StubTest, AllocObject) { } TEST_F(StubTest, AllocObjectArray) { - TEST_DISABLED_FOR_READ_BARRIER(); - #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ (defined(__x86_64__) && !defined(__APPLE__)) // TODO: Check the "Unresolved" allocation stubs @@ -1474,8 +1468,6 @@ TEST_F(StubTest, AllocObjectArray) { TEST_F(StubTest, StringCompareTo) { - TEST_DISABLED_FOR_READ_BARRIER(); - #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) // TODO: Check the "Unresolved" allocation stubs @@ -2152,8 +2144,6 @@ static void TestFields(Thread* self, StubTest* test, Primitive::Type test_type) } TEST_F(StubTest, Fields8) { - TEST_DISABLED_FOR_READ_BARRIER(); - Thread* self = Thread::Current(); self->TransitionFromSuspendedToRunnable(); @@ -2166,8 +2156,6 @@ TEST_F(StubTest, Fields8) { } TEST_F(StubTest, Fields16) { - TEST_DISABLED_FOR_READ_BARRIER(); - Thread* self = Thread::Current(); self->TransitionFromSuspendedToRunnable(); @@ -2180,8 +2168,6 @@ TEST_F(StubTest, Fields16) { } TEST_F(StubTest, Fields32) { - TEST_DISABLED_FOR_READ_BARRIER(); - Thread* self = Thread::Current(); self->TransitionFromSuspendedToRunnable(); @@ -2193,8 +2179,6 @@ TEST_F(StubTest, Fields32) { } TEST_F(StubTest, FieldsObj) { - TEST_DISABLED_FOR_READ_BARRIER(); - Thread* self = Thread::Current(); self->TransitionFromSuspendedToRunnable(); @@ -2206,8 +2190,6 @@ TEST_F(StubTest, FieldsObj) { } TEST_F(StubTest, Fields64) { - TEST_DISABLED_FOR_READ_BARRIER(); - Thread* self = Thread::Current(); self->TransitionFromSuspendedToRunnable(); @@ -2221,8 +2203,6 @@ TEST_F(StubTest, Fields64) { TEST_F(StubTest, IMT) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ (defined(__x86_64__) && !defined(__APPLE__)) - TEST_DISABLED_FOR_READ_BARRIER(); - Thread* self = Thread::Current(); ScopedObjectAccess soa(self); @@ -2342,8 +2322,6 @@ TEST_F(StubTest, IMT) { TEST_F(StubTest, StringIndexOf) { #if defined(__arm__) || defined(__aarch64__) - TEST_DISABLED_FOR_READ_BARRIER(); - Thread* self = Thread::Current(); ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init @@ -2416,4 +2394,40 @@ TEST_F(StubTest, StringIndexOf) { #endif } +TEST_F(StubTest, ReadBarrier) { +#if defined(ART_USE_READ_BARRIER) && (defined(__i386__) || defined(__arm__) || \ + defined(__aarch64__) || defined(__mips__) || (defined(__x86_64__) && !defined(__APPLE__))) + Thread* self = Thread::Current(); + + const uintptr_t readBarrierSlow = StubTest::GetEntrypoint(self, kQuickReadBarrierSlow); + + // Create an object + ScopedObjectAccess soa(self); + // garbage is created during ClassLinker::Init + + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> c( + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); + + // Build an object instance + Handle<mirror::Object> obj(hs.NewHandle(c->AllocObject(soa.Self()))); + + EXPECT_FALSE(self->IsExceptionPending()); + + size_t result = Invoke3(0U, reinterpret_cast<size_t>(obj.Get()), + mirror::Object::ClassOffset().SizeValue(), readBarrierSlow, self); + + EXPECT_FALSE(self->IsExceptionPending()); + EXPECT_NE(reinterpret_cast<size_t>(nullptr), result); + mirror::Class* klass = reinterpret_cast<mirror::Class*>(result); + EXPECT_EQ(klass, obj->GetClass()); + + // Tests done. +#else + LOG(INFO) << "Skipping read_barrier_slow"; + // Force-print to std::cout so it's also outside the logcat. + std::cout << "Skipping read_barrier_slow" << std::endl; +#endif +} + } // namespace art diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index 737f4d1c5b..e2632c103b 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -28,6 +28,9 @@ namespace art { extern "C" uint32_t art_quick_is_assignable(const mirror::Class* klass, const mirror::Class* ref_class); +// Read barrier entrypoints. +extern "C" mirror::Object* art_quick_read_barrier_slow(mirror::Object*, mirror::Object*, uint32_t); + void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { // Interpreter @@ -141,6 +144,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Read barrier qpoints->pReadBarrierJni = ReadBarrierJni; + qpoints->pReadBarrierSlow = art_quick_read_barrier_slow; }; } // namespace art diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index ebfb3faf4b..1da5a2ff17 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -33,7 +33,6 @@ MACRO2(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME, got_reg, temp_reg) movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg)), REG_VAR(temp_reg) movl (REG_VAR(temp_reg)), REG_VAR(temp_reg) // Push save all callee-save method. - THIS_LOAD_REQUIRES_READ_BARRIER pushl RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg)) CFI_ADJUST_CFA_OFFSET(4) // Store esp as the top quick frame. @@ -60,7 +59,6 @@ MACRO2(SETUP_REFS_ONLY_CALLEE_SAVE_FRAME, got_reg, temp_reg) movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg)), REG_VAR(temp_reg) movl (REG_VAR(temp_reg)), REG_VAR(temp_reg) // Push save all callee-save method. - THIS_LOAD_REQUIRES_READ_BARRIER pushl RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg)) CFI_ADJUST_CFA_OFFSET(4) // Store esp as the top quick frame. @@ -106,7 +104,6 @@ MACRO2(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME, got_reg, temp_reg) movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg)), REG_VAR(temp_reg) movl (REG_VAR(temp_reg)), REG_VAR(temp_reg) // Push save all callee-save method. - THIS_LOAD_REQUIRES_READ_BARRIER pushl RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg)) CFI_ADJUST_CFA_OFFSET(4) // Store esp as the stop quick frame. @@ -1126,6 +1123,53 @@ DEFINE_FUNCTION art_quick_check_cast UNREACHABLE END_FUNCTION art_quick_check_cast +// Restore reg's value if reg is not the same as exclude_reg, otherwise just adjust stack. +MACRO2(POP_REG_NE, reg, exclude_reg) + .ifc RAW_VAR(reg), RAW_VAR(exclude_reg) + addl MACRO_LITERAL(4), %esp + CFI_ADJUST_CFA_OFFSET(-4) + .else + POP RAW_VAR(reg) + .endif +END_MACRO + + /* + * Macro to insert read barrier, only used in art_quick_aput_obj. + * obj_reg and dest_reg are registers, offset is a defined literal such as + * MIRROR_OBJECT_CLASS_OFFSET. + * pop_eax is a boolean flag, indicating if eax is popped after the call. + * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path. + */ +MACRO4(READ_BARRIER, obj_reg, offset, dest_reg, pop_eax) +#ifdef USE_READ_BARRIER + PUSH eax // save registers used in art_quick_aput_obj + PUSH ebx + PUSH edx + PUSH ecx + // Outgoing argument set up + pushl MACRO_LITERAL((RAW_VAR(offset))) // pass offset, double parentheses are necessary + CFI_ADJUST_CFA_OFFSET(4) + PUSH RAW_VAR(obj_reg) // pass obj_reg + PUSH eax // pass ref, just pass eax for now since parameter ref is unused + call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj_reg, offset) + // No need to unpoison return value in eax, artReadBarrierSlow() would do the unpoisoning. + .ifnc RAW_VAR(dest_reg), eax + movl %eax, REG_VAR(dest_reg) // save loaded ref in dest_reg + .endif + addl MACRO_LITERAL(12), %esp // pop arguments + CFI_ADJUST_CFA_OFFSET(-12) + POP_REG_NE ecx, RAW_VAR(dest_reg) // Restore args except dest_reg + POP_REG_NE edx, RAW_VAR(dest_reg) + POP_REG_NE ebx, RAW_VAR(dest_reg) + .ifc RAW_VAR(pop_eax), true + POP_REG_NE eax, RAW_VAR(dest_reg) + .endif +#else + movl RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg) + UNPOISON_HEAP_REF RAW_VAR(dest_reg) +#endif // USE_READ_BARRIER +END_MACRO + /* * Entry from managed code for array put operations of objects where the value being stored * needs to be checked for compatibility. @@ -1149,17 +1193,20 @@ END_FUNCTION art_quick_aput_obj_with_bound_check DEFINE_FUNCTION art_quick_aput_obj test %edx, %edx // store of null jz .Ldo_aput_null - movl MIRROR_OBJECT_CLASS_OFFSET(%eax), %ebx - UNPOISON_HEAP_REF ebx - movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx - UNPOISON_HEAP_REF ebx + READ_BARRIER eax, MIRROR_OBJECT_CLASS_OFFSET, ebx, true + READ_BARRIER ebx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ebx, true // value's type == array's component type - trivial assignability -#ifdef USE_HEAP_POISONING - PUSH eax // save eax +#if defined(USE_READ_BARRIER) + READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, false + cmpl %eax, %ebx + POP eax // restore eax from the push in the beginning of READ_BARRIER macro +#elif defined(USE_HEAP_POISONING) + PUSH eax // save eax + // Cannot call READ_BARRIER macro here, because the above push messes up stack alignment. movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax UNPOISON_HEAP_REF eax cmpl %eax, %ebx - POP eax // restore eax + POP eax // restore eax #else cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ebx #endif @@ -1181,6 +1228,8 @@ DEFINE_FUNCTION art_quick_aput_obj subl LITERAL(8), %esp // alignment padding CFI_ADJUST_CFA_OFFSET(8) #ifdef USE_HEAP_POISONING + // This load does not need read barrier, since edx is unchanged and there's no GC safe point + // from last read of MIRROR_OBJECT_CLASS_OFFSET(%edx). movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax // pass arg2 - type of the value to be stored UNPOISON_HEAP_REF eax PUSH eax @@ -1696,5 +1745,15 @@ DEFINE_FUNCTION art_nested_signal_return UNREACHABLE END_FUNCTION art_nested_signal_return +DEFINE_FUNCTION art_quick_read_barrier_slow + PUSH edx // pass arg3 - offset + PUSH ecx // pass arg2 - obj + PUSH eax // pass arg1 - ref + call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj, offset) + addl LITERAL(12), %esp // pop arguments + CFI_ADJUST_CFA_OFFSET(-12) + ret +END_FUNCTION art_quick_read_barrier_slow + // TODO: implement these! UNIMPLEMENTED art_quick_memcmp16 diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S index 706ae58d91..cf0039c84e 100644 --- a/runtime/arch/x86_64/asm_support_x86_64.S +++ b/runtime/arch/x86_64/asm_support_x86_64.S @@ -24,6 +24,7 @@ #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 MACRO4(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4) .macro macro_name macro_arg1, macro_arg2, macro_arg3, macro_arg4 #define END_MACRO .endm #if defined(__clang__) diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc index d0ab9d5d49..ef1bb5f9a7 100644 --- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc +++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc @@ -29,6 +29,9 @@ namespace art { extern "C" uint32_t art_quick_assignable_from_code(const mirror::Class* klass, const mirror::Class* ref_class); +// Read barrier entrypoints. +extern "C" mirror::Object* art_quick_read_barrier_slow(mirror::Object*, mirror::Object*, uint32_t); + void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { #if defined(__APPLE__) @@ -145,6 +148,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Read barrier qpoints->pReadBarrierJni = ReadBarrierJni; + qpoints->pReadBarrierSlow = art_quick_read_barrier_slow; #endif // __APPLE__ }; diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 0eeb03a526..f4c9488260 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -66,7 +66,6 @@ MACRO0(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME) movq %xmm14, 24(%rsp) movq %xmm15, 32(%rsp) // R10 := ArtMethod* for save all callee save frame method. - THIS_LOAD_REQUIRES_READ_BARRIER movq RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 // Store ArtMethod* to bottom of stack. movq %r10, 0(%rsp) @@ -109,7 +108,6 @@ MACRO0(SETUP_REFS_ONLY_CALLEE_SAVE_FRAME) movq %xmm14, 24(%rsp) movq %xmm15, 32(%rsp) // R10 := ArtMethod* for refs only callee save frame method. - THIS_LOAD_REQUIRES_READ_BARRIER movq RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 // Store ArtMethod* to bottom of stack. movq %r10, 0(%rsp) @@ -168,7 +166,6 @@ MACRO0(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME) subq MACRO_LITERAL(80 + 4 * 8), %rsp CFI_ADJUST_CFA_OFFSET(80 + 4 * 8) // R10 := ArtMethod* for ref and args callee save frame method. - THIS_LOAD_REQUIRES_READ_BARRIER movq RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 // Save FPRs. movq %xmm0, 16(%rsp) @@ -920,8 +917,12 @@ DEFINE_FUNCTION art_quick_alloc_object_tlab // Fast path tlab allocation. // RDI: uint32_t type_idx, RSI: ArtMethod* // RDX, RCX, R8, R9: free. RAX: return val. + // TODO: Add read barrier when this function is used. + // Might need a special macro since rsi and edx is 32b/64b mismatched. movl ART_METHOD_DEX_CACHE_TYPES_OFFSET(%rsi), %edx // Load dex cache resolved types array UNPOISON_HEAP_REF edx + // TODO: Add read barrier when this function is used. + // Might need to break down into multiple instructions to get the base address in a register. // Load the class movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdx, %rdi, MIRROR_OBJECT_ARRAY_COMPONENT_SIZE), %edx UNPOISON_HEAP_REF edx @@ -1153,6 +1154,60 @@ DEFINE_FUNCTION art_quick_check_cast END_FUNCTION art_quick_check_cast +// Restore reg's value if reg is not the same as exclude_reg, otherwise just adjust stack. +MACRO2(POP_REG_NE, reg, exclude_reg) + .ifc RAW_VAR(reg), RAW_VAR(exclude_reg) + addq MACRO_LITERAL(8), %rsp + CFI_ADJUST_CFA_OFFSET(-8) + .else + POP RAW_VAR(reg) + .endif +END_MACRO + + /* + * Macro to insert read barrier, used in art_quick_aput_obj and art_quick_alloc_object_tlab. + * obj_reg and dest_reg{32|64} are registers, offset is a defined literal such as + * MIRROR_OBJECT_CLASS_OFFSET. dest_reg needs two versions to handle the mismatch between + * 64b PUSH/POP and 32b argument. + * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path. + * + * As with art_quick_aput_obj* functions, the 64b versions are in comments. + */ +MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64) +#ifdef USE_READ_BARRIER + PUSH rax // save registers that might be used + PUSH rdi + PUSH rsi + PUSH rdx + PUSH rcx + SETUP_FP_CALLEE_SAVE_FRAME + // Outgoing argument set up + // movl %edi, %edi // pass ref, no-op for now since parameter ref is unused + // // movq %rdi, %rdi + movl REG_VAR(obj_reg), %esi // pass obj_reg + // movq REG_VAR(obj_reg), %rsi + movl MACRO_LITERAL((RAW_VAR(offset))), %edx // pass offset, double parentheses are necessary + // movq MACRO_LITERAL((RAW_VAR(offset))), %rdx + call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj_reg, offset) + // No need to unpoison return value in rax, artReadBarrierSlow() would do the unpoisoning. + .ifnc RAW_VAR(dest_reg32), eax + // .ifnc RAW_VAR(dest_reg64), rax + movl %eax, REG_VAR(dest_reg32) // save loaded ref in dest_reg + // movq %rax, REG_VAR(dest_reg64) + .endif + RESTORE_FP_CALLEE_SAVE_FRAME + POP_REG_NE rcx, RAW_VAR(dest_reg64) // Restore registers except dest_reg + POP_REG_NE rdx, RAW_VAR(dest_reg64) + POP_REG_NE rsi, RAW_VAR(dest_reg64) + POP_REG_NE rdi, RAW_VAR(dest_reg64) + POP_REG_NE rax, RAW_VAR(dest_reg64) +#else + movl RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg32) + // movq RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg64) + UNPOISON_HEAP_REF RAW_VAR(dest_reg32) // UNPOISON_HEAP_REF only takes a 32b register +#endif // USE_READ_BARRIER +END_MACRO + /* * Entry from managed code for array put operations of objects where the value being stored * needs to be checked for compatibility. @@ -1197,15 +1252,13 @@ DEFINE_FUNCTION art_quick_aput_obj testl %edx, %edx // store of null // test %rdx, %rdx jz .Ldo_aput_null - movl MIRROR_OBJECT_CLASS_OFFSET(%edi), %ecx -// movq MIRROR_OBJECT_CLASS_OFFSET(%rdi), %rcx - UNPOISON_HEAP_REF ecx - movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ecx), %ecx -// movq MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %rcx - UNPOISON_HEAP_REF ecx -#ifdef USE_HEAP_POISONING - movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax // rax is free. - UNPOISON_HEAP_REF eax + READ_BARRIER edi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx + // READ_BARRIER rdi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx + READ_BARRIER ecx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx + // READ_BARRIER rcx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx +#if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER) + READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax // rax is free. + // READ_BARRIER rdx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax cmpl %eax, %ecx // value's type == array's component type - trivial assignability #else cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability @@ -1232,9 +1285,14 @@ DEFINE_FUNCTION art_quick_aput_obj PUSH rdx SETUP_FP_CALLEE_SAVE_FRAME - // "Uncompress" = do nothing, as already zero-extended on load. - movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class. - UNPOISON_HEAP_REF esi +#if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER) + // The load of MIRROR_OBJECT_CLASS_OFFSET(%edx) is redundant, eax still holds the value. + movl %eax, %esi // Pass arg2 = value's class. + // movq %rax, %rsi +#else + // "Uncompress" = do nothing, as already zero-extended on load. + movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class. +#endif movq %rcx, %rdi // Pass arg1 = array's component type. call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b) @@ -1735,3 +1793,14 @@ DEFINE_FUNCTION art_nested_signal_return call PLT_SYMBOL(longjmp) UNREACHABLE END_FUNCTION art_nested_signal_return + +DEFINE_FUNCTION art_quick_read_barrier_slow + SETUP_FP_CALLEE_SAVE_FRAME + subq LITERAL(8), %rsp // Alignment padding. + CFI_ADJUST_CFA_OFFSET(8) + call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj, offset) + addq LITERAL(8), %rsp + CFI_ADJUST_CFA_OFFSET(-8) + RESTORE_FP_CALLEE_SAVE_FRAME + ret +END_FUNCTION art_quick_read_barrier_slow diff --git a/runtime/asm_support.h b/runtime/asm_support.h index f4f8eaf759..350a0d4c15 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -109,7 +109,7 @@ ADD_TEST_EQ(THREAD_SELF_OFFSET, art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_pos. -#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 150 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 151 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_end. diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 40b3669299..38bc8186d5 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -16,6 +16,7 @@ #include "check_jni.h" +#include <iomanip> #include <sys/mman.h> #include <zlib.h> @@ -1083,10 +1084,29 @@ class ScopedCheck { } const char* errorKind = nullptr; - uint8_t utf8 = CheckUtfBytes(bytes, &errorKind); + const uint8_t* utf8 = CheckUtfBytes(bytes, &errorKind); if (errorKind != nullptr) { + // This is an expensive loop that will resize often, but this isn't supposed to hit in + // practice anyways. + std::ostringstream oss; + oss << std::hex; + const uint8_t* tmp = reinterpret_cast<const uint8_t*>(bytes); + while (*tmp != 0) { + if (tmp == utf8) { + oss << "<"; + } + oss << "0x" << std::setfill('0') << std::setw(2) << static_cast<uint32_t>(*tmp); + if (tmp == utf8) { + oss << '>'; + } + tmp++; + if (*tmp != 0) { + oss << ' '; + } + } + AbortF("input is not valid Modified UTF-8: illegal %s byte %#x\n" - " string: '%s'", errorKind, utf8, bytes); + " string: '%s'\n input: '%s'", errorKind, *utf8, bytes, oss.str().c_str()); return false; } return true; @@ -1094,11 +1114,11 @@ class ScopedCheck { // Checks whether |bytes| is valid modified UTF-8. We also accept 4 byte UTF // sequences in place of encoded surrogate pairs. - static uint8_t CheckUtfBytes(const char* bytes, const char** errorKind) { + static const uint8_t* CheckUtfBytes(const char* bytes, const char** errorKind) { while (*bytes != '\0') { - uint8_t utf8 = *(bytes++); + const uint8_t* utf8 = reinterpret_cast<const uint8_t*>(bytes++); // Switch on the high four bits. - switch (utf8 >> 4) { + switch (*utf8 >> 4) { case 0x00: case 0x01: case 0x02: @@ -1118,11 +1138,11 @@ class ScopedCheck { return utf8; case 0x0f: // Bit pattern 1111, which might be the start of a 4 byte sequence. - if ((utf8 & 0x08) == 0) { + if ((*utf8 & 0x08) == 0) { // Bit pattern 1111 0xxx, which is the start of a 4 byte sequence. // We consume one continuation byte here, and fall through to consume two more. - utf8 = *(bytes++); - if ((utf8 & 0xc0) != 0x80) { + utf8 = reinterpret_cast<const uint8_t*>(bytes++); + if ((*utf8 & 0xc0) != 0x80) { *errorKind = "continuation"; return utf8; } @@ -1135,8 +1155,8 @@ class ScopedCheck { FALLTHROUGH_INTENDED; case 0x0e: // Bit pattern 1110, so there are two additional bytes. - utf8 = *(bytes++); - if ((utf8 & 0xc0) != 0x80) { + utf8 = reinterpret_cast<const uint8_t*>(bytes++); + if ((*utf8 & 0xc0) != 0x80) { *errorKind = "continuation"; return utf8; } @@ -1146,8 +1166,8 @@ class ScopedCheck { case 0x0c: case 0x0d: // Bit pattern 110x, so there is one additional byte. - utf8 = *(bytes++); - if ((utf8 & 0xc0) != 0x80) { + utf8 = reinterpret_cast<const uint8_t*>(bytes++); + if ((*utf8 & 0xc0) != 0x80) { *errorKind = "continuation"; return utf8; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 6a76bf7f07..ce54c14d5d 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1236,11 +1236,8 @@ void ClassLinker::InitFromImage() { bool ClassLinker::ClassInClassTable(mirror::Class* klass) { ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - auto it = class_table_.Find(GcRoot<mirror::Class>(klass)); - if (it == class_table_.end()) { - return false; - } - return it->Read() == klass; + ClassTable* const class_table = ClassTableForClassLoader(klass->GetClassLoader()); + return class_table != nullptr && class_table->Contains(klass); } void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { @@ -1263,26 +1260,30 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { // Moving concurrent: // Need to make sure to not copy ArtMethods without doing read barriers since the roots are // marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy. - for (GcRoot<mirror::Class>& root : class_table_) { - buffered_visitor.VisitRoot(root); + std::vector<std::pair<GcRoot<mirror::ClassLoader>, ClassTable*>> reinsert; + for (auto it = classes_.begin(); it != classes_.end(); ) { + it->second->VisitRoots(visitor, flags); + const GcRoot<mirror::ClassLoader>& root = it->first; + mirror::ClassLoader* old_ref = root.Read<kWithoutReadBarrier>(); + root.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + mirror::ClassLoader* new_ref = root.Read<kWithoutReadBarrier>(); + if (new_ref != old_ref) { + reinsert.push_back(*it); + it = classes_.erase(it); + } else { + ++it; + } } - // PreZygote classes can't move so we won't need to update fields' declaring classes. - for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) { - buffered_visitor.VisitRoot(root); + for (auto& pair : reinsert) { + classes_.Put(pair.first, pair.second); } } else if ((flags & kVisitRootFlagNewRoots) != 0) { for (auto& root : new_class_roots_) { mirror::Class* old_ref = root.Read<kWithoutReadBarrier>(); root.VisitRoot(visitor, RootInfo(kRootStickyClass)); mirror::Class* new_ref = root.Read<kWithoutReadBarrier>(); - if (UNLIKELY(new_ref != old_ref)) { - // Uh ohes, GC moved a root in the log. Need to search the class_table and update the - // corresponding object. This is slow, but luckily for us, this may only happen with a - // concurrent moving GC. - auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref)); - DCHECK(it != class_table_.end()); - *it = GcRoot<mirror::Class>(new_ref); - } + // Concurrent moving GC marked new roots through the to-space invariant. + CHECK_EQ(new_ref, old_ref); } } buffered_visitor.Flush(); // Flush before clearing new_class_roots_. @@ -1331,21 +1332,27 @@ void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { } } +void ClassLinker::VisitClassesInternal(ClassVisitor* visitor, void* arg) { + for (auto& pair : classes_) { + ClassTable* const class_table = pair.second; + if (!class_table->Visit(visitor, arg)) { + return; + } + } +} + void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) { if (dex_cache_image_class_lookup_required_) { MoveImageClassesToClassTable(); } - // TODO: why isn't this a ReaderMutexLock? - WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - for (GcRoot<mirror::Class>& root : class_table_) { - if (!visitor(root.Read(), arg)) { - return; - } - } - for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) { - if (!visitor(root.Read(), arg)) { - return; - } + Thread* const self = Thread::Current(); + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + // Not safe to have thread suspension when we are holding a lock. + if (self != nullptr) { + ScopedAssertNoThreadSuspension nts(self, __FUNCTION__); + VisitClassesInternal(visitor, arg); + } else { + VisitClassesInternal(visitor, arg); } } @@ -1399,7 +1406,7 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* ar size_t class_table_size; { ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); - class_table_size = class_table_.Size() + pre_zygote_class_table_.Size(); + class_table_size = NumZygoteClasses() + NumNonZygoteClasses(); } mirror::Class* class_type = mirror::Class::GetJavaLangClass(); mirror::Class* array_of_class = FindArrayClass(self, &class_type); @@ -1443,6 +1450,7 @@ ClassLinker::~ClassLinker() { mirror::LongArray::ResetArrayClass(); mirror::ShortArray::ResetArrayClass(); STLDeleteElements(&oat_files_); + STLDeleteValues(&classes_); } mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) { @@ -2458,8 +2466,8 @@ void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, bool ClassLinker::IsDexFileRegisteredLocked(const DexFile& dex_file) { dex_lock_.AssertSharedHeld(Thread::Current()); - for (size_t i = 0; i != dex_caches_.size(); ++i) { - mirror::DexCache* dex_cache = GetDexCache(i); + for (GcRoot<mirror::DexCache>& root : dex_caches_) { + mirror::DexCache* dex_cache = root.Read(); if (dex_cache->GetDexFile() == &dex_file) { return true; } @@ -2757,8 +2765,7 @@ mirror::Class* ClassLinker::FindPrimitiveClass(char type) { return nullptr; } -mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* klass, - size_t hash) { +mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* klass, size_t hash) { if (VLOG_IS_ON(class_linker)) { mirror::DexCache* dex_cache = klass->GetDexCache(); std::string source; @@ -2769,11 +2776,13 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k LOG(INFO) << "Loaded class " << descriptor << source; } WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - mirror::Class* existing = LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash); + mirror::ClassLoader* const class_loader = klass->GetClassLoader(); + ClassTable* const class_table = InsertClassTableForClassLoader(class_loader); + mirror::Class* existing = class_table->Lookup(descriptor, hash); if (existing != nullptr) { return existing; } - if (kIsDebugBuild && !klass->IsTemp() && klass->GetClassLoader() == nullptr && + if (kIsDebugBuild && !klass->IsTemp() && class_loader == nullptr && dex_cache_image_class_lookup_required_) { // Check a class loaded with the system class loader matches one in the image if the class // is in the image. @@ -2783,7 +2792,7 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k } } VerifyObject(klass); - class_table_.InsertWithHash(GcRoot<mirror::Class>(klass), hash); + class_table->InsertWithHash(klass, hash); if (log_new_class_table_roots_) { new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); } @@ -2802,95 +2811,41 @@ void ClassLinker::UpdateClassVirtualMethods(mirror::Class* klass, ArtMethod* new } } -mirror::Class* ClassLinker::UpdateClass(const char* descriptor, mirror::Class* klass, - size_t hash) { - WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - auto existing_it = class_table_.FindWithHash(std::make_pair(descriptor, klass->GetClassLoader()), - hash); - CHECK(existing_it != class_table_.end()); - mirror::Class* existing = existing_it->Read(); - CHECK_NE(existing, klass) << descriptor; - CHECK(!existing->IsResolved()) << descriptor; - CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor; - - CHECK(!klass->IsTemp()) << descriptor; - if (kIsDebugBuild && klass->GetClassLoader() == nullptr && - dex_cache_image_class_lookup_required_) { - // Check a class loaded with the system class loader matches one in the image if the class - // is in the image. - existing = LookupClassFromImage(descriptor); - if (existing != nullptr) { - CHECK_EQ(klass, existing) << descriptor; - } - } - VerifyObject(klass); - - // Update the element in the hash set. - *existing_it = GcRoot<mirror::Class>(klass); - if (log_new_class_table_roots_) { - new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); - } - - return existing; -} - bool ClassLinker::RemoveClass(const char* descriptor, mirror::ClassLoader* class_loader) { WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - auto pair = std::make_pair(descriptor, class_loader); - auto it = class_table_.Find(pair); - if (it != class_table_.end()) { - class_table_.Erase(it); - return true; - } - it = pre_zygote_class_table_.Find(pair); - if (it != pre_zygote_class_table_.end()) { - pre_zygote_class_table_.Erase(it); - return true; - } - return false; + ClassTable* const class_table = ClassTableForClassLoader(class_loader); + return class_table != nullptr && class_table->Remove(descriptor); } mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash, mirror::ClassLoader* class_loader) { { ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); - mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash); - if (result != nullptr) { - return result; + ClassTable* const class_table = ClassTableForClassLoader(class_loader); + if (class_table != nullptr) { + mirror::Class* result = class_table->Lookup(descriptor, hash); + if (result != nullptr) { + return result; + } } } if (class_loader != nullptr || !dex_cache_image_class_lookup_required_) { return nullptr; - } else { - // Lookup failed but need to search dex_caches_. - mirror::Class* result = LookupClassFromImage(descriptor); - if (result != nullptr) { - InsertClass(descriptor, result, hash); - } else { - // Searching the image dex files/caches failed, we don't want to get into this situation - // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image - // classes into the class table. - constexpr uint32_t kMaxFailedDexCacheLookups = 1000; - if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) { - MoveImageClassesToClassTable(); - } - } - return result; } -} - -mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor, - mirror::ClassLoader* class_loader, - size_t hash) { - auto descriptor_pair = std::make_pair(descriptor, class_loader); - auto it = pre_zygote_class_table_.FindWithHash(descriptor_pair, hash); - if (it == pre_zygote_class_table_.end()) { - it = class_table_.FindWithHash(descriptor_pair, hash); - if (it == class_table_.end()) { - return nullptr; + // Lookup failed but need to search dex_caches_. + mirror::Class* result = LookupClassFromImage(descriptor); + if (result != nullptr) { + result = InsertClass(descriptor, result, hash); + } else { + // Searching the image dex files/caches failed, we don't want to get into this situation + // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image + // classes into the class table. + constexpr uint32_t kMaxFailedDexCacheLookups = 1000; + if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) { + MoveImageClassesToClassTable(); } } - return it->Read(); + return result; } static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches() @@ -2910,6 +2865,7 @@ void ClassLinker::MoveImageClassesToClassTable() { ScopedAssertNoThreadSuspension ants(self, "Moving image classes to class table"); mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(); std::string temp; + ClassTable* const class_table = InsertClassTableForClassLoader(nullptr); for (int32_t i = 0; i < dex_caches->GetLength(); i++) { mirror::DexCache* dex_cache = dex_caches->Get(i); mirror::ObjectArray<mirror::Class>* types = dex_cache->GetResolvedTypes(); @@ -2919,12 +2875,12 @@ void ClassLinker::MoveImageClassesToClassTable() { DCHECK(klass->GetClassLoader() == nullptr); const char* descriptor = klass->GetDescriptor(&temp); size_t hash = ComputeModifiedUtf8Hash(descriptor); - mirror::Class* existing = LookupClassFromTableLocked(descriptor, nullptr, hash); + mirror::Class* existing = class_table->Lookup(descriptor, hash); if (existing != nullptr) { CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != " << PrettyClassAndClassLoader(klass); } else { - class_table_.Insert(GcRoot<mirror::Class>(klass)); + class_table->Insert(klass); if (log_new_class_table_roots_) { new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); } @@ -2937,9 +2893,9 @@ void ClassLinker::MoveImageClassesToClassTable() { void ClassLinker::MoveClassTableToPreZygote() { WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - DCHECK(pre_zygote_class_table_.Empty()); - pre_zygote_class_table_ = std::move(class_table_); - class_table_.Clear(); + for (auto& class_table : classes_) { + class_table.second->FreezeSnapshot(); + } } mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) { @@ -2971,31 +2927,13 @@ void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Clas MoveImageClassesToClassTable(); } WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - while (true) { - auto it = class_table_.Find(descriptor); - if (it == class_table_.end()) { - break; + for (auto& pair : classes_) { + // There can only be one class with the same descriptor per class loader. + ClassTable* const class_table = pair.second; + mirror::Class* klass = class_table->Lookup(descriptor, ComputeModifiedUtf8Hash(descriptor)); + if (klass != nullptr) { + result.push_back(klass); } - result.push_back(it->Read()); - class_table_.Erase(it); - } - for (mirror::Class* k : result) { - class_table_.Insert(GcRoot<mirror::Class>(k)); - } - size_t pre_zygote_start = result.size(); - // Now handle the pre zygote table. - // Note: This dirties the pre-zygote table but shouldn't be an issue since LookupClasses is only - // called from the debugger. - while (true) { - auto it = pre_zygote_class_table_.Find(descriptor); - if (it == pre_zygote_class_table_.end()) { - break; - } - result.push_back(it->Read()); - pre_zygote_class_table_.Erase(it); - } - for (size_t i = pre_zygote_start; i < result.size(); ++i) { - pre_zygote_class_table_.Insert(GcRoot<mirror::Class>(result[i])); } } @@ -3303,7 +3241,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache()); mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self); std::string descriptor(GetDescriptorForProxy(klass.Get())); - size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str()); + const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str()); // Insert the class before loading the fields as the field roots // (ArtField::declaring_class_) are only visited from the class @@ -4046,6 +3984,25 @@ void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, } } +ClassTable* ClassLinker::InsertClassTableForClassLoader(mirror::ClassLoader* class_loader) { + auto it = classes_.find(GcRoot<mirror::ClassLoader>(class_loader)); + if (it != classes_.end()) { + return it->second; + } + // Class table for loader not found, add it to the table. + auto* const class_table = new ClassTable; + classes_.Put(GcRoot<mirror::ClassLoader>(class_loader), class_table); + return class_table; +} + +ClassTable* ClassLinker::ClassTableForClassLoader(mirror::ClassLoader* class_loader) { + auto it = classes_.find(GcRoot<mirror::ClassLoader>(class_loader)); + if (it != classes_.end()) { + return it->second; + } + return nullptr; +} + bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, MutableHandle<mirror::Class>* h_new_class_out) { @@ -4096,9 +4053,26 @@ bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror: CHECK_EQ(h_new_class->GetClassSize(), class_size); ObjectLock<mirror::Class> lock(self, h_new_class); FixupTemporaryDeclaringClass(klass.Get(), h_new_class.Get()); - mirror::Class* existing = UpdateClass(descriptor, h_new_class.Get(), - ComputeModifiedUtf8Hash(descriptor)); - CHECK(existing == nullptr || existing == klass.Get()); + + { + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + mirror::ClassLoader* const class_loader = h_new_class.Get()->GetClassLoader(); + ClassTable* const table = InsertClassTableForClassLoader(class_loader); + mirror::Class* existing = table->UpdateClass(descriptor, h_new_class.Get(), + ComputeModifiedUtf8Hash(descriptor)); + CHECK_EQ(existing, klass.Get()); + if (kIsDebugBuild && class_loader == nullptr && dex_cache_image_class_lookup_required_) { + // Check a class loaded with the system class loader matches one in the image if the class + // is in the image. + mirror::Class* const image_class = LookupClassFromImage(descriptor); + if (image_class != nullptr) { + CHECK_EQ(klass.Get(), existing) << descriptor; + } + } + if (log_new_class_table_roots_) { + new_class_roots_.push_back(GcRoot<mirror::Class>(h_new_class.Get())); + } + } // This will notify waiters on temp class that saw the not yet resolved class in the // class_table_ during EnsureResolved. @@ -5589,23 +5563,13 @@ const char* ClassLinker::MethodShorty(uint32_t method_idx, ArtMethod* referrer, return dex_file.GetMethodShorty(method_id, length); } -void ClassLinker::DumpAllClasses(int flags) { - if (dex_cache_image_class_lookup_required_) { - MoveImageClassesToClassTable(); - } - // TODO: at the time this was written, it wasn't safe to call PrettyField with the ClassLinker - // lock held, because it might need to resolve a field's type, which would try to take the lock. - std::vector<mirror::Class*> all_classes; - { - ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - for (GcRoot<mirror::Class>& it : class_table_) { - all_classes.push_back(it.Read()); - } - } +bool DumpClassVisitor(mirror::Class* klass, void* arg) SHARED_REQUIRES(Locks::mutator_lock_) { + klass->DumpClass(LOG(ERROR), reinterpret_cast<ssize_t>(arg)); + return true; +} - for (size_t i = 0; i < all_classes.size(); ++i) { - all_classes[i]->DumpClass(std::cerr, flags); - } +void ClassLinker::DumpAllClasses(int flags) { + VisitClasses(&DumpClassVisitor, reinterpret_cast<void*>(flags)); } static OatFile::OatMethod CreateOatMethod(const void* code) { @@ -5658,8 +5622,24 @@ void ClassLinker::DumpForSigQuit(std::ostream& os) { MoveImageClassesToClassTable(); } ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); - os << "Zygote loaded classes=" << pre_zygote_class_table_.Size() << " post zygote classes=" - << class_table_.Size() << "\n"; + os << "Zygote loaded classes=" << NumZygoteClasses() << " post zygote classes=" + << NumNonZygoteClasses() << "\n"; +} + +size_t ClassLinker::NumZygoteClasses() const { + size_t sum = 0; + for (auto& pair : classes_) { + sum += pair.second->NumZygoteClasses(); + } + return sum; +} + +size_t ClassLinker::NumNonZygoteClasses() const { + size_t sum = 0; + for (auto& pair : classes_) { + sum += pair.second->NumNonZygoteClasses(); + } + return sum; } size_t ClassLinker::NumLoadedClasses() { @@ -5668,7 +5648,7 @@ size_t ClassLinker::NumLoadedClasses() { } ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); // Only return non zygote classes since these are the ones which apps which care about. - return class_table_.Size(); + return NumNonZygoteClasses(); } pid_t ClassLinker::GetClassesLockOwner() { @@ -5739,43 +5719,6 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { return descriptor; } -std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& root) - const { - std::string temp; - return ComputeModifiedUtf8Hash(root.Read()->GetDescriptor(&temp)); -} - -bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a, - const GcRoot<mirror::Class>& b) const { - if (a.Read()->GetClassLoader() != b.Read()->GetClassLoader()) { - return false; - } - std::string temp; - return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp)); -} - -std::size_t ClassLinker::ClassDescriptorHashEquals::operator()( - const std::pair<const char*, mirror::ClassLoader*>& element) const { - return ComputeModifiedUtf8Hash(element.first); -} - -bool ClassLinker::ClassDescriptorHashEquals::operator()( - const GcRoot<mirror::Class>& a, const std::pair<const char*, mirror::ClassLoader*>& b) const { - if (a.Read()->GetClassLoader() != b.second) { - return false; - } - return a.Read()->DescriptorEquals(b.first); -} - -bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a, - const char* descriptor) const { - return a.Read()->DescriptorEquals(descriptor); -} - -std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const char* descriptor) const { - return ComputeModifiedUtf8Hash(descriptor); -} - bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) { if (Runtime::Current()->UseJit()) { // JIT can have direct code pointers from any method to any other method. diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 05a809e524..13375639b7 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -25,6 +25,7 @@ #include "base/hash_set.h" #include "base/macros.h" #include "base/mutex.h" +#include "class_table.h" #include "dex_file.h" #include "gc_root.h" #include "jni.h" @@ -56,8 +57,6 @@ class Runtime; class ScopedObjectAccessAlreadyRunnable; template<size_t kNumReferences> class PACKED(4) StackHandleScope; -typedef bool (ClassVisitor)(mirror::Class* c, void* arg); - enum VisitRootFlags : uint8_t; class ClassLinker { @@ -476,9 +475,28 @@ class ClassLinker { void DropFindArrayClassCache() SHARED_REQUIRES(Locks::mutator_lock_); private: + class CompareClassLoaderGcRoot { + public: + bool operator()(const GcRoot<mirror::ClassLoader>& a, const GcRoot<mirror::ClassLoader>& b) + const SHARED_REQUIRES(Locks::mutator_lock_) { + return a.Read() < b.Read(); + } + }; + + typedef SafeMap<GcRoot<mirror::ClassLoader>, ClassTable*, CompareClassLoaderGcRoot> + ClassLoaderClassTable; + + void VisitClassesInternal(ClassVisitor* visitor, void* arg) + REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + + // Returns the number of zygote and image classes. + size_t NumZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_); + + // Returns the number of non zygote nor image classes. + size_t NumNonZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_); + OatFile& GetImageOatFile(gc::space::ImageSpace* space) - REQUIRES(!dex_lock_) - SHARED_REQUIRES(Locks::mutator_lock_); + REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_); void FinishInit(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); @@ -543,8 +561,7 @@ class ClassLinker { SHARED_REQUIRES(Locks::mutator_lock_); void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) - REQUIRES(dex_lock_) - SHARED_REQUIRES(Locks::mutator_lock_); + REQUIRES(dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_); bool IsDexFileRegisteredLocked(const DexFile& dex_file) SHARED_REQUIRES(dex_lock_, Locks::mutator_lock_); @@ -568,7 +585,7 @@ class ClassLinker { bool LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, MutableHandle<mirror::Class>* h_new_class_out) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::classlinker_classes_lock_); bool LinkSuperClass(Handle<mirror::Class> klass) SHARED_REQUIRES(Locks::mutator_lock_); @@ -576,7 +593,8 @@ class ClassLinker { bool LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); - bool LinkMethods(Thread* self, Handle<mirror::Class> klass, + bool LinkMethods(Thread* self, + Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, ArtMethod** out_imt) SHARED_REQUIRES(Locks::mutator_lock_); @@ -632,18 +650,16 @@ class ClassLinker { void EnsurePreverifiedMethods(Handle<mirror::Class> c) SHARED_REQUIRES(Locks::mutator_lock_); - mirror::Class* LookupClassFromTableLocked(const char* descriptor, - mirror::ClassLoader* class_loader, - size_t hash) - SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_); - - mirror::Class* UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash) - REQUIRES(!Locks::classlinker_classes_lock_) - SHARED_REQUIRES(Locks::mutator_lock_); - mirror::Class* LookupClassFromImage(const char* descriptor) SHARED_REQUIRES(Locks::mutator_lock_); + // Returns null if not found. + ClassTable* ClassTableForClassLoader(mirror::ClassLoader* class_loader) + SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_); + // Insert a new class table if not found. + ClassTable* InsertClassTableForClassLoader(mirror::ClassLoader* class_loader) + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::classlinker_classes_lock_); + // EnsureResolved is called to make sure that a class in the class_table_ has been resolved // before returning it to the caller. Its the responsibility of the thread that placed the class // in the table to make it resolved. The thread doing resolution must notify on the class' lock @@ -690,43 +706,11 @@ class ClassLinker { std::vector<GcRoot<mirror::DexCache>> dex_caches_ GUARDED_BY(dex_lock_); std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_); - class ClassDescriptorHashEquals { - public: - // Same class loader and descriptor. - std::size_t operator()(const GcRoot<mirror::Class>& root) const NO_THREAD_SAFETY_ANALYSIS; - bool operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b) const - NO_THREAD_SAFETY_ANALYSIS; - // Same class loader and descriptor. - std::size_t operator()(const std::pair<const char*, mirror::ClassLoader*>& element) const - NO_THREAD_SAFETY_ANALYSIS; - bool operator()(const GcRoot<mirror::Class>& a, - const std::pair<const char*, mirror::ClassLoader*>& b) const - NO_THREAD_SAFETY_ANALYSIS; - // Same descriptor. - bool operator()(const GcRoot<mirror::Class>& a, const char* descriptor) const - NO_THREAD_SAFETY_ANALYSIS; - std::size_t operator()(const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS; - }; - class GcRootEmptyFn { - public: - void MakeEmpty(GcRoot<mirror::Class>& item) const { - item = GcRoot<mirror::Class>(); - } - bool IsEmpty(const GcRoot<mirror::Class>& item) const { - return item.IsNull(); - } - }; + // This contains strong roots. To enable concurrent root scanning of the class table. + ClassLoaderClassTable classes_ GUARDED_BY(Locks::classlinker_classes_lock_); - // hash set which hashes class descriptor, and compares descriptors nad class loaders. Results - // should be compared for a matching Class descriptor and class loader. - typedef HashSet<GcRoot<mirror::Class>, GcRootEmptyFn, ClassDescriptorHashEquals, - ClassDescriptorHashEquals, TrackingAllocator<GcRoot<mirror::Class>, kAllocatorTagClassTable>> - Table; - // This contains strong roots. To enable concurrent root scanning of - // the class table, be careful to use a read barrier when accessing this. - Table class_table_ GUARDED_BY(Locks::classlinker_classes_lock_); - Table pre_zygote_class_table_ GUARDED_BY(Locks::classlinker_classes_lock_); - std::vector<GcRoot<mirror::Class>> new_class_roots_; + // New class roots, only used by CMS since the GC needs to mark these in the pause. + std::vector<GcRoot<mirror::Class>> new_class_roots_ GUARDED_BY(Locks::classlinker_classes_lock_); // Do we need to search dex caches to find image classes? bool dex_cache_image_class_lookup_required_; diff --git a/runtime/class_table.cc b/runtime/class_table.cc new file mode 100644 index 0000000000..f775235505 --- /dev/null +++ b/runtime/class_table.cc @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2015 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 "class_table.h" + +#include "mirror/class-inl.h" + +namespace art { + +ClassTable::ClassTable() { + classes_.push_back(ClassSet()); +} + +void ClassTable::FreezeSnapshot() { + classes_.push_back(ClassSet()); +} + +bool ClassTable::Contains(mirror::Class* klass) { + for (ClassSet& class_set : classes_) { + auto it = class_set.Find(GcRoot<mirror::Class>(klass)); + if (it != class_set.end()) { + return it->Read() == klass; + } + } + return false; +} + +mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash) { + // Should only be updating latest table. + auto existing_it = classes_.back().FindWithHash(descriptor, hash); + if (kIsDebugBuild && existing_it == classes_.back().end()) { + for (const ClassSet& class_set : classes_) { + if (class_set.FindWithHash(descriptor, hash) != class_set.end()) { + LOG(FATAL) << "Updating class found in frozen table " << descriptor; + } + } + LOG(FATAL) << "Updating class not found " << descriptor; + } + mirror::Class* const existing = existing_it->Read(); + CHECK_NE(existing, klass) << descriptor; + CHECK(!existing->IsResolved()) << descriptor; + CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor; + CHECK(!klass->IsTemp()) << descriptor; + VerifyObject(klass); + // Update the element in the hash set with the new class. This is safe to do since the descriptor + // doesn't change. + *existing_it = GcRoot<mirror::Class>(klass); + return existing; +} + +void ClassTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags ATTRIBUTE_UNUSED) { + BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor( + visitor, RootInfo(kRootStickyClass)); + for (ClassSet& class_set : classes_) { + for (GcRoot<mirror::Class>& root : class_set) { + buffered_visitor.VisitRoot(root); + } + } +} + +bool ClassTable::Visit(ClassVisitor* visitor, void* arg) { + for (ClassSet& class_set : classes_) { + for (GcRoot<mirror::Class>& root : class_set) { + if (!visitor(root.Read(), arg)) { + return false; + } + } + } + return true; +} + +size_t ClassTable::NumZygoteClasses() const { + size_t sum = 0; + for (size_t i = 0; i < classes_.size() - 1; ++i) { + sum += classes_[i].Size(); + } + return sum; +} + +size_t ClassTable::NumNonZygoteClasses() const { + return classes_.back().Size(); +} + +mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) { + for (ClassSet& class_set : classes_) { + auto it = class_set.FindWithHash(descriptor, hash); + if (it != class_set.end()) { + return it->Read(); + } + } + return nullptr; +} + +void ClassTable::Insert(mirror::Class* klass) { + classes_.back().Insert(GcRoot<mirror::Class>(klass)); +} + +void ClassTable::InsertWithHash(mirror::Class* klass, size_t hash) { + classes_.back().InsertWithHash(GcRoot<mirror::Class>(klass), hash); +} + +bool ClassTable::Remove(const char* descriptor) { + for (ClassSet& class_set : classes_) { + auto it = class_set.Find(descriptor); + if (it != class_set.end()) { + class_set.Erase(it); + return true; + } + } + return false; +} + +std::size_t ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& root) + const { + std::string temp; + return ComputeModifiedUtf8Hash(root.Read()->GetDescriptor(&temp)); +} + +bool ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a, + const GcRoot<mirror::Class>& b) const { + DCHECK_EQ(a.Read()->GetClassLoader(), b.Read()->GetClassLoader()); + std::string temp; + return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp)); +} + +bool ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a, + const char* descriptor) const { + return a.Read()->DescriptorEquals(descriptor); +} + +std::size_t ClassTable::ClassDescriptorHashEquals::operator()(const char* descriptor) const { + return ComputeModifiedUtf8Hash(descriptor); +} + +} // namespace art diff --git a/runtime/class_table.h b/runtime/class_table.h new file mode 100644 index 0000000000..af25131ab4 --- /dev/null +++ b/runtime/class_table.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ART_RUNTIME_CLASS_TABLE_H_ +#define ART_RUNTIME_CLASS_TABLE_H_ + +#include <string> +#include <utility> +#include <vector> + +#include "base/allocator.h" +#include "base/hash_set.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "dex_file.h" +#include "gc_root.h" +#include "object_callbacks.h" +#include "runtime.h" + +namespace art { + +namespace mirror { + class ClassLoader; +} // namespace mirror + +typedef bool (ClassVisitor)(mirror::Class* c, void* arg); + +// Each loader has a ClassTable +class ClassTable { + public: + ClassTable(); + + // Used by image writer for checking. + bool Contains(mirror::Class* klass) + REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + + // Freeze the current class tables by allocating a new table and never updating or modifying the + // existing table. This helps prevents dirty pages after caused by inserting after zygote fork. + void FreezeSnapshot() + REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + + // Returns the number of classes in previous snapshots. + size_t NumZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_); + + // Returns all off the classes in the lastest snapshot. + size_t NumNonZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_); + + // Update a class in the table with the new class. Returns the existing class which was replaced. + mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash) + REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + + void VisitRoots(RootVisitor* visitor, VisitRootFlags flags) + REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + + // Return false if the callback told us to exit. + bool Visit(ClassVisitor* visitor, void* arg) + REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + + mirror::Class* Lookup(const char* descriptor, size_t hash) + SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_); + + void Insert(mirror::Class* klass) + REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + void InsertWithHash(mirror::Class* klass, size_t hash) + REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + + // Returns true if the class was found and removed, false otherwise. + bool Remove(const char* descriptor) + REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + + private: + class ClassDescriptorHashEquals { + public: + // Same class loader and descriptor. + std::size_t operator()(const GcRoot<mirror::Class>& root) const NO_THREAD_SAFETY_ANALYSIS; + bool operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b) const + NO_THREAD_SAFETY_ANALYSIS;; + // Same descriptor. + bool operator()(const GcRoot<mirror::Class>& a, const char* descriptor) const + NO_THREAD_SAFETY_ANALYSIS; + std::size_t operator()(const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS; + }; + class GcRootEmptyFn { + public: + void MakeEmpty(GcRoot<mirror::Class>& item) const { + item = GcRoot<mirror::Class>(); + } + bool IsEmpty(const GcRoot<mirror::Class>& item) const { + return item.IsNull(); + } + }; + // hash set which hashes class descriptor, and compares descriptors nad class loaders. Results + // should be compared for a matching Class descriptor and class loader. + typedef HashSet<GcRoot<mirror::Class>, GcRootEmptyFn, ClassDescriptorHashEquals, + ClassDescriptorHashEquals, TrackingAllocator<GcRoot<mirror::Class>, kAllocatorTagClassTable>> + ClassSet; + + // TODO: shard lock to have one per class loader. + std::vector<ClassSet> classes_ GUARDED_BY(Locks::classlinker_classes_lock_); +}; + +} // namespace art + +#endif // ART_RUNTIME_CLASS_TABLE_H_ diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h index cef2510451..3d3f7a1bdb 100644 --- a/runtime/entrypoints/quick/quick_entrypoints.h +++ b/runtime/entrypoints/quick/quick_entrypoints.h @@ -20,6 +20,7 @@ #include <jni.h> #include "base/macros.h" +#include "base/mutex.h" #include "offsets.h" #define QUICK_ENTRYPOINT_OFFSET(ptr_size, x) \ @@ -71,6 +72,16 @@ extern void ReadBarrierJni(mirror::CompressedReference<mirror::Object>* handle_o Thread* self) NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; +// Read barrier entrypoints. +// Compilers for ARM, ARM64, MIPS, MIPS64 can insert a call to this function directly. +// For x86 and x86_64, compilers need a wrapper assembly function, to handle mismatch in ABI. +// This is the read barrier slow path for instance and static fields and reference-type arrays. +// TODO: Currently the read barrier does not have a fast path for compilers to directly generate. +// Ideally the slow path should only take one parameter "ref". +extern "C" mirror::Object* artReadBarrierSlow(mirror::Object* ref, mirror::Object* obj, + uint32_t offset) + SHARED_REQUIRES(Locks::mutator_lock_) HOT_ATTR; + } // namespace art #endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_H_ diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 60bbf4ac82..73d8ae76ae 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -145,7 +145,8 @@ V(NewStringFromStringBuffer, void) \ V(NewStringFromStringBuilder, void) \ \ - V(ReadBarrierJni, void, mirror::CompressedReference<mirror::Object>*, Thread*) + V(ReadBarrierJni, void, mirror::CompressedReference<mirror::Object>*, Thread*) \ + V(ReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t) #endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_ #undef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_ // #define is only for lint. diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc index 25a943a82a..0a1d80648d 100644 --- a/runtime/entrypoints/quick/quick_field_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc @@ -557,4 +557,16 @@ extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj return -1; // failure } +// TODO: Currently the read barrier does not have a fast path. Ideally the slow path should only +// take one parameter "ref", which is generated by the fast path. +extern "C" mirror::Object* artReadBarrierSlow(mirror::Object* ref ATTRIBUTE_UNUSED, + mirror::Object* obj, uint32_t offset) { + DCHECK(kUseReadBarrier); + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(obj) + offset; + mirror::HeapReference<mirror::Object>* ref_addr = + reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_addr); + return ReadBarrier::Barrier<mirror::Object, kWithReadBarrier, true>(obj, MemberOffset(offset), + ref_addr); +} + } // namespace art diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index c05c93555c..f7a3cd53cd 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -311,8 +311,9 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuilder, pReadBarrierJni, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pReadBarrierJni, pReadBarrierSlow, sizeof(void*)); - CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pReadBarrierJni) + CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pReadBarrierSlow) + sizeof(void*) == sizeof(QuickEntryPoints), QuickEntryPoints_all); } }; diff --git a/runtime/gc/accounting/remembered_set.cc b/runtime/gc/accounting/remembered_set.cc index 251b50d93b..70704c13c8 100644 --- a/runtime/gc/accounting/remembered_set.cc +++ b/runtime/gc/accounting/remembered_set.cc @@ -95,7 +95,11 @@ class RememberedSetReferenceVisitor { void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(!target_space_->HasAddress(root->AsMirrorPtr())); + if (target_space_->HasAddress(root->AsMirrorPtr())) { + *contains_reference_to_target_space_ = true; + root->Assign(collector_->MarkObject(root->AsMirrorPtr())); + DCHECK(!target_space_->HasAddress(root->AsMirrorPtr())); + } } private: diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 01faf3cee5..5bbb0dc45f 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -253,9 +253,9 @@ ALWAYS_INLINE inline static void SetFieldValue(mirror::Object* o, mirror::Field* break; case Primitive::kPrimChar: if (is_volatile) { - o->SetFieldBooleanVolatile<false>(offset, new_value.GetC()); + o->SetFieldCharVolatile<false>(offset, new_value.GetC()); } else { - o->SetFieldBoolean<false>(offset, new_value.GetC()); + o->SetFieldChar<false>(offset, new_value.GetC()); } break; case Primitive::kPrimInt: diff --git a/runtime/oat.h b/runtime/oat.h index ee2f3f60f3..29dd76ce5e 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '6', '7', '\0' }; + static constexpr uint8_t kOatVersion[] = { '0', '6', '8', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/read_barrier_c.h b/runtime/read_barrier_c.h index 4f408dd5c1..710c21f03e 100644 --- a/runtime/read_barrier_c.h +++ b/runtime/read_barrier_c.h @@ -47,9 +47,4 @@ #error "Only one of Baker or Brooks can be enabled at a time." #endif -// A placeholder marker to indicate places to add read barriers in the -// assembly code. This is a development time aid and to be removed -// after read barriers are added. -#define THIS_LOAD_REQUIRES_READ_BARRIER - #endif // ART_RUNTIME_READ_BARRIER_C_H_ diff --git a/runtime/thread.cc b/runtime/thread.cc index 6949b0bd34..2b977af51c 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2304,6 +2304,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuffer) QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuilder) QUICK_ENTRY_POINT_INFO(pReadBarrierJni) + QUICK_ENTRY_POINT_INFO(pReadBarrierSlow) #undef QUICK_ENTRY_POINT_INFO os << offset; diff --git a/test/046-reflect/expected.txt b/test/046-reflect/expected.txt index fa053fb92d..d657d44e61 100644 --- a/test/046-reflect/expected.txt +++ b/test/046-reflect/expected.txt @@ -24,7 +24,7 @@ Method name is myMethod SuperTarget constructor ()V Target constructor ()V Before, float is 3.1415925 -myMethod: hi there 3.1415925 Q ! +myMethod: hi there 3.1415925 ✔ ! Result of invoke: 7 Calling no-arg void-return method myNoargMethod ()V diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java index 0d8e576086..0c90109c69 100644 --- a/test/046-reflect/src/Main.java +++ b/test/046-reflect/src/Main.java @@ -147,7 +147,7 @@ public class Main { Object[] argList = new Object[] { new String[] { "hi there" }, new Float(3.1415926f), - new Character('Q') + new Character('\u2714') }; System.out.println("Before, float is " + ((Float)argList[1]).floatValue()); diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index 7db61a1023..c932761c3b 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -1,6 +1,6 @@ true 8 -x +✔ 3.141592653589793 3.14 32 diff --git a/test/100-reflect2/src/Main.java b/test/100-reflect2/src/Main.java index 72e14b15f3..bf3a574c99 100644 --- a/test/100-reflect2/src/Main.java +++ b/test/100-reflect2/src/Main.java @@ -20,7 +20,7 @@ import java.util.*; class Main { private static boolean z = true; private static byte b = 8; - private static char c = 'x'; + private static char c = '\u2714'; private static double d = Math.PI; private static float f = 3.14f; private static int i = 32; @@ -144,7 +144,7 @@ class Main { /* private static boolean z = true; private static byte b = 8; - private static char c = 'x'; + private static char c = '\u2714'; private static double d = Math.PI; private static float f = 3.14f; private static int i = 32; @@ -263,7 +263,7 @@ class Main { show(ctor.newInstance((Object[]) null)); ctor = String.class.getConstructor(char[].class, int.class, int.class); - show(ctor.newInstance(new char[] { 'x', 'y', 'z', '!' }, 1, 2)); + show(ctor.newInstance(new char[] { '\u2714', 'y', 'z', '!' }, 1, 2)); } private static void testPackagePrivateConstructor() { diff --git a/test/411-optimizing-arith/expected.txt b/test/411-optimizing-arith-mul/expected.txt index e69de29bb2..e69de29bb2 100644 --- a/test/411-optimizing-arith/expected.txt +++ b/test/411-optimizing-arith-mul/expected.txt diff --git a/test/411-optimizing-arith/info.txt b/test/411-optimizing-arith-mul/info.txt index 10155512f0..10155512f0 100644 --- a/test/411-optimizing-arith/info.txt +++ b/test/411-optimizing-arith-mul/info.txt diff --git a/test/411-optimizing-arith/src/Main.java b/test/411-optimizing-arith-mul/src/Main.java index 3a5d7c05c9..3a5d7c05c9 100644 --- a/test/411-optimizing-arith/src/Main.java +++ b/test/411-optimizing-arith-mul/src/Main.java diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java index 87459e2fa6..c108a900e2 100644 --- a/test/441-checker-inliner/src/Main.java +++ b/test/441-checker-inliner/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2014 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. -*/ + * Copyright (C) 2014 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. + */ public class Main { diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java index b7863be6ce..20dac42b91 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2014 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. -*/ + * Copyright (C) 2014 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. + */ public class Main { @@ -46,9 +46,9 @@ public class Main { } } + /** - * Tiny three-register program exercising int constant folding - * on negation. + * Exercise constant folding on negation. */ /// CHECK-START: int Main.IntNegation() constant_folding (before) @@ -60,6 +60,9 @@ public class Main { /// CHECK-DAG: <<ConstN42:i\d+>> IntConstant -42 /// CHECK-DAG: Return [<<ConstN42>>] + /// CHECK-START: int Main.IntNegation() constant_folding (after) + /// CHECK-NOT: Neg + public static int IntNegation() { int x, y; x = 42; @@ -67,9 +70,9 @@ public class Main { return y; } + /** - * Tiny three-register program exercising int constant folding - * on addition. + * Exercise constant folding on addition. */ /// CHECK-START: int Main.IntAddition1() constant_folding (before) @@ -82,6 +85,9 @@ public class Main { /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 /// CHECK-DAG: Return [<<Const3>>] + /// CHECK-START: int Main.IntAddition1() constant_folding (after) + /// CHECK-NOT: Add + public static int IntAddition1() { int a, b, c; a = 1; @@ -90,11 +96,6 @@ public class Main { return c; } - /** - * Small three-register program exercising int constant folding - * on addition. - */ - /// CHECK-START: int Main.IntAddition2() constant_folding (before) /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 @@ -109,6 +110,9 @@ public class Main { /// CHECK-DAG: <<Const14:i\d+>> IntConstant 14 /// CHECK-DAG: Return [<<Const14>>] + /// CHECK-START: int Main.IntAddition2() constant_folding (after) + /// CHECK-NOT: Add + public static int IntAddition2() { int a, b, c; a = 1; @@ -121,9 +125,30 @@ public class Main { return c; } + /// CHECK-START: long Main.LongAddition() constant_folding (before) + /// CHECK-DAG: <<Const1:j\d+>> LongConstant 1 + /// CHECK-DAG: <<Const2:j\d+>> LongConstant 2 + /// CHECK-DAG: <<Add:j\d+>> Add [<<Const1>>,<<Const2>>] + /// CHECK-DAG: Return [<<Add>>] + + /// CHECK-START: long Main.LongAddition() constant_folding (after) + /// CHECK-DAG: <<Const3:j\d+>> LongConstant 3 + /// CHECK-DAG: Return [<<Const3>>] + + /// CHECK-START: long Main.LongAddition() constant_folding (after) + /// CHECK-NOT: Add + + public static long LongAddition() { + long a, b, c; + a = 1L; + b = 2L; + c = a + b; + return c; + } + + /** - * Tiny three-register program exercising int constant folding - * on subtraction. + * Exercise constant folding on subtraction. */ /// CHECK-START: int Main.IntSubtraction() constant_folding (before) @@ -136,6 +161,9 @@ public class Main { /// CHECK-DAG: <<Const4:i\d+>> IntConstant 4 /// CHECK-DAG: Return [<<Const4>>] + /// CHECK-START: int Main.IntSubtraction() constant_folding (after) + /// CHECK-NOT: Sub + public static int IntSubtraction() { int a, b, c; a = 6; @@ -144,54 +172,179 @@ public class Main { return c; } + /// CHECK-START: long Main.LongSubtraction() constant_folding (before) + /// CHECK-DAG: <<Const6:j\d+>> LongConstant 6 + /// CHECK-DAG: <<Const2:j\d+>> LongConstant 2 + /// CHECK-DAG: <<Sub:j\d+>> Sub [<<Const6>>,<<Const2>>] + /// CHECK-DAG: Return [<<Sub>>] + + /// CHECK-START: long Main.LongSubtraction() constant_folding (after) + /// CHECK-DAG: <<Const4:j\d+>> LongConstant 4 + /// CHECK-DAG: Return [<<Const4>>] + + /// CHECK-START: long Main.LongSubtraction() constant_folding (after) + /// CHECK-NOT: Sub + + public static long LongSubtraction() { + long a, b, c; + a = 6L; + b = 2L; + c = a - b; + return c; + } + + /** - * Tiny three-register program exercising long constant folding - * on addition. + * Exercise constant folding on multiplication. */ - /// CHECK-START: long Main.LongAddition() constant_folding (before) - /// CHECK-DAG: <<Const1:j\d+>> LongConstant 1 - /// CHECK-DAG: <<Const2:j\d+>> LongConstant 2 - /// CHECK-DAG: <<Add:j\d+>> Add [<<Const1>>,<<Const2>>] - /// CHECK-DAG: Return [<<Add>>] + /// CHECK-START: int Main.IntMultiplication() constant_folding (before) + /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7 + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Const7>>,<<Const3>>] + /// CHECK-DAG: Return [<<Mul>>] - /// CHECK-START: long Main.LongAddition() constant_folding (after) + /// CHECK-START: int Main.IntMultiplication() constant_folding (after) + /// CHECK-DAG: <<Const21:i\d+>> IntConstant 21 + /// CHECK-DAG: Return [<<Const21>>] + + /// CHECK-START: int Main.IntMultiplication() constant_folding (after) + /// CHECK-NOT: Mul + + public static int IntMultiplication() { + int a, b, c; + a = 7; + b = 3; + c = a * b; + return c; + } + + /// CHECK-START: long Main.LongMultiplication() constant_folding (before) + /// CHECK-DAG: <<Const7:j\d+>> LongConstant 7 /// CHECK-DAG: <<Const3:j\d+>> LongConstant 3 - /// CHECK-DAG: Return [<<Const3>>] + /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Const7>>,<<Const3>>] + /// CHECK-DAG: Return [<<Mul>>] - public static long LongAddition() { + /// CHECK-START: long Main.LongMultiplication() constant_folding (after) + /// CHECK-DAG: <<Const21:j\d+>> LongConstant 21 + /// CHECK-DAG: Return [<<Const21>>] + + /// CHECK-START: long Main.LongMultiplication() constant_folding (after) + /// CHECK-NOT: Mul + + public static long LongMultiplication() { long a, b, c; - a = 1L; - b = 2L; - c = a + b; + a = 7L; + b = 3L; + c = a * b; return c; } + /** - * Tiny three-register program exercising long constant folding - * on subtraction. + * Exercise constant folding on division. */ - /// CHECK-START: long Main.LongSubtraction() constant_folding (before) - /// CHECK-DAG: <<Const6:j\d+>> LongConstant 6 - /// CHECK-DAG: <<Const2:j\d+>> LongConstant 2 - /// CHECK-DAG: <<Sub:j\d+>> Sub [<<Const6>>,<<Const2>>] - /// CHECK-DAG: Return [<<Sub>>] + /// CHECK-START: int Main.IntDivision() constant_folding (before) + /// CHECK-DAG: <<Const8:i\d+>> IntConstant 8 + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Div0Chk:i\d+>> DivZeroCheck [<<Const3>>] + /// CHECK-DAG: <<Div:i\d+>> Div [<<Const8>>,<<Div0Chk>>] + /// CHECK-DAG: Return [<<Div>>] - /// CHECK-START: long Main.LongSubtraction() constant_folding (after) - /// CHECK-DAG: <<Const4:j\d+>> LongConstant 4 - /// CHECK-DAG: Return [<<Const4>>] + /// CHECK-START: int Main.IntDivision() constant_folding (after) + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + /// CHECK-DAG: Return [<<Const2>>] - public static long LongSubtraction() { + /// CHECK-START: int Main.IntDivision() constant_folding (after) + /// CHECK-NOT: DivZeroCheck + /// CHECK-NOT: Div + + public static int IntDivision() { + int a, b, c; + a = 8; + b = 3; + c = a / b; + return c; + } + + /// CHECK-START: long Main.LongDivision() constant_folding (before) + /// CHECK-DAG: <<Const8:j\d+>> LongConstant 8 + /// CHECK-DAG: <<Const3:j\d+>> LongConstant 3 + /// CHECK-DAG: <<Div0Chk:j\d+>> DivZeroCheck [<<Const3>>] + /// CHECK-DAG: <<Div:j\d+>> Div [<<Const8>>,<<Div0Chk>>] + /// CHECK-DAG: Return [<<Div>>] + + /// CHECK-START: long Main.LongDivision() constant_folding (after) + /// CHECK-DAG: <<Const2:j\d+>> LongConstant 2 + /// CHECK-DAG: Return [<<Const2>>] + + /// CHECK-START: long Main.LongDivision() constant_folding (after) + /// CHECK-NOT: DivZeroCheck + /// CHECK-NOT: Div + + public static long LongDivision() { long a, b, c; - a = 6L; - b = 2L; - c = a - b; + a = 8L; + b = 3L; + c = a / b; + return c; + } + + + /** + * Exercise constant folding on remainder. + */ + + /// CHECK-START: int Main.IntRemainder() constant_folding (before) + /// CHECK-DAG: <<Const8:i\d+>> IntConstant 8 + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Div0Chk:i\d+>> DivZeroCheck [<<Const3>>] + /// CHECK-DAG: <<Rem:i\d+>> Rem [<<Const8>>,<<Div0Chk>>] + /// CHECK-DAG: Return [<<Rem>>] + + /// CHECK-START: int Main.IntRemainder() constant_folding (after) + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + /// CHECK-DAG: Return [<<Const2>>] + + /// CHECK-START: int Main.IntRemainder() constant_folding (after) + /// CHECK-NOT: DivZeroCheck + /// CHECK-NOT: Rem + + public static int IntRemainder() { + int a, b, c; + a = 8; + b = 3; + c = a % b; + return c; + } + + /// CHECK-START: long Main.LongRemainder() constant_folding (before) + /// CHECK-DAG: <<Const8:j\d+>> LongConstant 8 + /// CHECK-DAG: <<Const3:j\d+>> LongConstant 3 + /// CHECK-DAG: <<Div0Chk:j\d+>> DivZeroCheck [<<Const3>>] + /// CHECK-DAG: <<Rem:j\d+>> Rem [<<Const8>>,<<Div0Chk>>] + /// CHECK-DAG: Return [<<Rem>>] + + /// CHECK-START: long Main.LongRemainder() constant_folding (after) + /// CHECK-DAG: <<Const2:j\d+>> LongConstant 2 + /// CHECK-DAG: Return [<<Const2>>] + + /// CHECK-START: long Main.LongRemainder() constant_folding (after) + /// CHECK-NOT: DivZeroCheck + /// CHECK-NOT: Rem + + public static long LongRemainder() { + long a, b, c; + a = 8L; + b = 3L; + c = a % b; return c; } + /** - * Three-register program with a constant (static) condition. + * Exercise constant folding on constant (static) condition. */ /// CHECK-START: int Main.StaticCondition() constant_folding (before) @@ -204,6 +357,9 @@ public class Main { /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 /// CHECK-DAG: If [<<Const1>>] + /// CHECK-START: int Main.StaticCondition() constant_folding (after) + /// CHECK-NOT: GreaterThanOrEqual + public static int StaticCondition() { int a, b, c; a = 7; @@ -215,9 +371,10 @@ public class Main { return c; } + /** - * Four-variable program with jumps leading to the creation of many - * blocks. + * Exercise constant folding on a program with condition + * (i.e. jumps) leading to the creation of many blocks. * * The intent of this test is to ensure that all constant expressions * are actually evaluated at compile-time, thanks to the reverse @@ -238,6 +395,10 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const7>>,<<Const3>>] /// CHECK-DAG: Return [<<Phi>>] + /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after) + /// CHECK-NOT: Add + /// CHECK-NOT: Sub + public static int JumpsAndConditionals(boolean cond) { int a, b, c; a = 5; @@ -249,6 +410,7 @@ public class Main { return c; } + /** * Test optimizations of arithmetic identities yielding a constant result. */ @@ -262,9 +424,11 @@ public class Main { /// CHECK-START: int Main.And0(int) constant_folding (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-NOT: And /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: int Main.And0(int) constant_folding (after) + /// CHECK-NOT: And + public static int And0(int arg) { return arg & 0; } @@ -278,9 +442,11 @@ public class Main { /// CHECK-START: long Main.Mul0(long) constant_folding (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0 - /// CHECK-NOT: Mul /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: long Main.Mul0(long) constant_folding (after) + /// CHECK-NOT: Mul + public static long Mul0(long arg) { return arg * 0; } @@ -293,9 +459,11 @@ public class Main { /// CHECK-START: int Main.OrAllOnes(int) constant_folding (after) /// CHECK-DAG: <<ConstF:i\d+>> IntConstant -1 - /// CHECK-NOT: Or /// CHECK-DAG: Return [<<ConstF>>] + /// CHECK-START: int Main.OrAllOnes(int) constant_folding (after) + /// CHECK-NOT: Or + public static int OrAllOnes(int arg) { return arg | -1; } @@ -309,9 +477,11 @@ public class Main { /// CHECK-START: long Main.Rem0(long) constant_folding (after) /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0 - /// CHECK-NOT: Rem /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: long Main.Rem0(long) constant_folding (after) + /// CHECK-NOT: Rem + public static long Rem0(long arg) { return 0 % arg; } @@ -324,9 +494,11 @@ public class Main { /// CHECK-START: int Main.Rem1(int) constant_folding (after) /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-NOT: Rem /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: int Main.Rem1(int) constant_folding (after) + /// CHECK-NOT: Rem + public static int Rem1(int arg) { return arg % 1; } @@ -340,9 +512,11 @@ public class Main { /// CHECK-START: long Main.RemN1(long) constant_folding (after) /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0 - /// CHECK-NOT: Rem /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: long Main.RemN1(long) constant_folding (after) + /// CHECK-NOT: Rem + public static long RemN1(long arg) { return arg % -1; } @@ -356,9 +530,11 @@ public class Main { /// CHECK-START: int Main.Shl0(int) constant_folding (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-NOT: Shl /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: int Main.Shl0(int) constant_folding (after) + /// CHECK-NOT: Shl + public static int Shl0(int arg) { return 0 << arg; } @@ -372,9 +548,11 @@ public class Main { /// CHECK-START: long Main.Shr0(int) constant_folding (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0 - /// CHECK-NOT: Shr /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: long Main.Shr0(int) constant_folding (after) + /// CHECK-NOT: Shr + public static long Shr0(int arg) { return (long)0 >> arg; } @@ -387,9 +565,11 @@ public class Main { /// CHECK-START: long Main.SubSameLong(long) constant_folding (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0 - /// CHECK-NOT: Sub /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: long Main.SubSameLong(long) constant_folding (after) + /// CHECK-NOT: Sub + public static long SubSameLong(long arg) { return arg - arg; } @@ -403,9 +583,11 @@ public class Main { /// CHECK-START: int Main.UShr0(int) constant_folding (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-NOT: UShr /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: int Main.UShr0(int) constant_folding (after) + /// CHECK-NOT: UShr + public static int UShr0(int arg) { return 0 >>> arg; } @@ -418,9 +600,11 @@ public class Main { /// CHECK-START: int Main.XorSameInt(int) constant_folding (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-NOT: Xor /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: int Main.XorSameInt(int) constant_folding (after) + /// CHECK-NOT: Xor + public static int XorSameInt(int arg) { return arg ^ arg; } @@ -473,6 +657,11 @@ public class Main { return arg < Double.NaN; } + + /** + * Exercise constant folding on type conversions. + */ + /// CHECK-START: int Main.ReturnInt33() constant_folding (before) /// CHECK-DAG: <<Const33:j\d+>> LongConstant 33 /// CHECK-DAG: <<Convert:i\d+>> TypeConversion [<<Const33>>] @@ -482,6 +671,9 @@ public class Main { /// CHECK-DAG: <<Const33:i\d+>> IntConstant 33 /// CHECK-DAG: Return [<<Const33>>] + /// CHECK-START: int Main.ReturnInt33() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static int ReturnInt33() { long imm = 33L; return (int) imm; @@ -496,6 +688,9 @@ public class Main { /// CHECK-DAG: <<ConstMax:i\d+>> IntConstant 2147483647 /// CHECK-DAG: Return [<<ConstMax>>] + /// CHECK-START: int Main.ReturnIntMax() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static int ReturnIntMax() { float imm = 1.0e34f; return (int) imm; @@ -510,6 +705,9 @@ public class Main { /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: int Main.ReturnInt0() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static int ReturnInt0() { double imm = Double.NaN; return (int) imm; @@ -524,6 +722,9 @@ public class Main { /// CHECK-DAG: <<Const33:j\d+>> LongConstant 33 /// CHECK-DAG: Return [<<Const33>>] + /// CHECK-START: long Main.ReturnLong33() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static long ReturnLong33() { int imm = 33; return (long) imm; @@ -538,6 +739,9 @@ public class Main { /// CHECK-DAG: <<Const34:j\d+>> LongConstant 34 /// CHECK-DAG: Return [<<Const34>>] + /// CHECK-START: long Main.ReturnLong34() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static long ReturnLong34() { float imm = 34.0f; return (long) imm; @@ -552,6 +756,9 @@ public class Main { /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0 /// CHECK-DAG: Return [<<Const0>>] + /// CHECK-START: long Main.ReturnLong0() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static long ReturnLong0() { double imm = -Double.NaN; return (long) imm; @@ -566,6 +773,9 @@ public class Main { /// CHECK-DAG: <<Const33:f\d+>> FloatConstant 33 /// CHECK-DAG: Return [<<Const33>>] + /// CHECK-START: float Main.ReturnFloat33() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static float ReturnFloat33() { int imm = 33; return (float) imm; @@ -580,6 +790,9 @@ public class Main { /// CHECK-DAG: <<Const34:f\d+>> FloatConstant 34 /// CHECK-DAG: Return [<<Const34>>] + /// CHECK-START: float Main.ReturnFloat34() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static float ReturnFloat34() { long imm = 34L; return (float) imm; @@ -594,6 +807,9 @@ public class Main { /// CHECK-DAG: <<Const:f\d+>> FloatConstant 99.25 /// CHECK-DAG: Return [<<Const>>] + /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static float ReturnFloat99P25() { double imm = 99.25; return (float) imm; @@ -622,6 +838,9 @@ public class Main { /// CHECK-DAG: <<Const34:d\d+>> DoubleConstant 34 /// CHECK-DAG: Return [<<Const34>>] + /// CHECK-START: double Main.ReturnDouble34() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static double ReturnDouble34() { long imm = 34L; return (double) imm; @@ -636,46 +855,70 @@ public class Main { /// CHECK-DAG: <<Const:d\d+>> DoubleConstant 99.25 /// CHECK-DAG: Return [<<Const>>] + /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (after) + /// CHECK-NOT: TypeConversion + public static double ReturnDouble99P25() { float imm = 99.25f; return (double) imm; } + public static void main(String[] args) { - assertIntEquals(IntNegation(), -42); - assertIntEquals(IntAddition1(), 3); - assertIntEquals(IntAddition2(), 14); - assertIntEquals(IntSubtraction(), 4); - assertLongEquals(LongAddition(), 3L); - assertLongEquals(LongSubtraction(), 4L); - assertIntEquals(StaticCondition(), 5); - assertIntEquals(JumpsAndConditionals(true), 7); - assertIntEquals(JumpsAndConditionals(false), 3); + assertIntEquals(-42, IntNegation()); + + assertIntEquals(3, IntAddition1()); + assertIntEquals(14, IntAddition2()); + assertLongEquals(3L, LongAddition()); + + assertIntEquals(4, IntSubtraction()); + assertLongEquals(4L, LongSubtraction()); + + assertIntEquals(21, IntMultiplication()); + assertLongEquals(21L, LongMultiplication()); + + assertIntEquals(2, IntDivision()); + assertLongEquals(2L, LongDivision()); + + assertIntEquals(2, IntRemainder()); + assertLongEquals(2L, LongRemainder()); + + assertIntEquals(5, StaticCondition()); + + assertIntEquals(7, JumpsAndConditionals(true)); + assertIntEquals(3, JumpsAndConditionals(false)); + int arbitrary = 123456; // Value chosen arbitrarily. - assertIntEquals(And0(arbitrary), 0); - assertLongEquals(Mul0(arbitrary), 0); - assertIntEquals(OrAllOnes(arbitrary), -1); - assertLongEquals(Rem0(arbitrary), 0); - assertIntEquals(Rem1(arbitrary), 0); - assertLongEquals(RemN1(arbitrary), 0); - assertIntEquals(Shl0(arbitrary), 0); - assertLongEquals(Shr0(arbitrary), 0); - assertLongEquals(SubSameLong(arbitrary), 0); - assertIntEquals(UShr0(arbitrary), 0); - assertIntEquals(XorSameInt(arbitrary), 0); + + assertIntEquals(0, And0(arbitrary)); + assertLongEquals(0, Mul0(arbitrary)); + assertIntEquals(-1, OrAllOnes(arbitrary)); + assertLongEquals(0, Rem0(arbitrary)); + assertIntEquals(0, Rem1(arbitrary)); + assertLongEquals(0, RemN1(arbitrary)); + assertIntEquals(0, Shl0(arbitrary)); + assertLongEquals(0, Shr0(arbitrary)); + assertLongEquals(0, SubSameLong(arbitrary)); + assertIntEquals(0, UShr0(arbitrary)); + assertIntEquals(0, XorSameInt(arbitrary)); + assertFalse(CmpFloatGreaterThanNaN(arbitrary)); assertFalse(CmpDoubleLessThanNaN(arbitrary)); - assertIntEquals(ReturnInt33(), 33); - assertIntEquals(ReturnIntMax(), 2147483647); - assertIntEquals(ReturnInt0(), 0); - assertLongEquals(ReturnLong33(), 33); - assertLongEquals(ReturnLong34(), 34); - assertLongEquals(ReturnLong0(), 0); - assertFloatEquals(ReturnFloat33(), 33); - assertFloatEquals(ReturnFloat34(), 34); - assertFloatEquals(ReturnFloat99P25(), 99.25f); - assertDoubleEquals(ReturnDouble33(), 33); - assertDoubleEquals(ReturnDouble34(), 34); - assertDoubleEquals(ReturnDouble99P25(), 99.25); + + assertIntEquals(33, ReturnInt33()); + assertIntEquals(2147483647, ReturnIntMax()); + assertIntEquals(0, ReturnInt0()); + + assertLongEquals(33, ReturnLong33()); + assertLongEquals(34, ReturnLong34()); + assertLongEquals(0, ReturnLong0()); + + assertFloatEquals(33, ReturnFloat33()); + assertFloatEquals(34, ReturnFloat34()); + assertFloatEquals(99.25f, ReturnFloat99P25()); + + assertDoubleEquals(33, ReturnDouble33()); + assertDoubleEquals(34, ReturnDouble34()); + assertDoubleEquals(99.25, ReturnDouble99P25()); } } diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java index 42f9a11092..6ee8a4d66f 100644 --- a/test/445-checker-licm/src/Main.java +++ b/test/445-checker-licm/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/446-checker-inliner2/src/Main.java b/test/446-checker-inliner2/src/Main.java index de00a09256..e8168af4e1 100644 --- a/test/446-checker-inliner2/src/Main.java +++ b/test/446-checker-inliner2/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2014 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. -*/ + * Copyright (C) 2014 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. + */ public class Main { diff --git a/test/447-checker-inliner3/src/Main.java b/test/447-checker-inliner3/src/Main.java index e3fdffdd46..0b980d0143 100644 --- a/test/447-checker-inliner3/src/Main.java +++ b/test/447-checker-inliner3/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2014 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. -*/ + * Copyright (C) 2014 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. + */ public class Main { diff --git a/test/449-checker-bce/expected.txt b/test/449-checker-bce/expected.txt index e69de29bb2..e114c50371 100644 --- a/test/449-checker-bce/expected.txt +++ b/test/449-checker-bce/expected.txt @@ -0,0 +1 @@ +java.lang.ArrayIndexOutOfBoundsException: length=5; index=82 diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index ed6fc1ee2b..a746664160 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { @@ -1101,6 +1101,28 @@ public class Main { } + public void testExceptionMessage() { + short[] B1 = new short[5]; + int[] B2 = new int[5]; + Exception err = null; + try { + testExceptionMessage1(B1, B2, null, -1, 6); + } catch (Exception e) { + err = e; + } + System.out.println(err); + } + + void testExceptionMessage1(short[] a1, int[] a2, long a3[], int start, int finish) { + int j = finish + 77; + // Bug: 22665511 + // A deoptimization will be triggered here right before the loop. Need to make + // sure the value of j is preserved for the interpreter. + for (int i = start; i <= finish; i++) { + a2[j - 1] = a1[i + 1]; + } + } + // Make sure this method is compiled with optimizing. /// CHECK-START: void Main.main(java.lang.String[]) register (after) /// CHECK: ParallelMove @@ -1141,6 +1163,7 @@ public class Main { }; testUnknownBounds(); + new Main().testExceptionMessage(); } } diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index 9070627f1c..014f59a506 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ interface Interface { diff --git a/test/451-regression-add-float/src/Main.java b/test/451-regression-add-float/src/Main.java index 0d4bf065ea..093c132abe 100644 --- a/test/451-regression-add-float/src/Main.java +++ b/test/451-regression-add-float/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index aa4dda17b6..a14200e7ce 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java index cee8e0fbe7..171ade875c 100644 --- a/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java +++ b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class OtherDex { public static void emptyMethod() { diff --git a/test/462-checker-inlining-across-dex-files/src/Main.java b/test/462-checker-inlining-across-dex-files/src/Main.java index 64979ca7ab..1fe49a8046 100644 --- a/test/462-checker-inlining-across-dex-files/src/Main.java +++ b/test/462-checker-inlining-across-dex-files/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ // Add a class that will be the first entry in the dex cache, to // avoid having the OtherDex and Main classes share the same cache index. diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java index dd17e770de..61510d80e2 100644 --- a/test/463-checker-boolean-simplifier/src/Main.java +++ b/test/463-checker-boolean-simplifier/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java index 876496fdc4..6dce96c9ca 100644 --- a/test/464-checker-inline-sharpen-calls/src/Main.java +++ b/test/464-checker-inline-sharpen-calls/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public final class Main { diff --git a/test/465-checker-clinit-gvn/src/Main.java b/test/465-checker-clinit-gvn/src/Main.java index 704e9fe123..9c77acc4d1 100644 --- a/test/465-checker-clinit-gvn/src/Main.java +++ b/test/465-checker-clinit-gvn/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ class OtherClass { static { diff --git a/test/473-checker-inliner-constants/src/Main.java b/test/473-checker-inliner-constants/src/Main.java index 85f6565503..8638514919 100644 --- a/test/473-checker-inliner-constants/src/Main.java +++ b/test/473-checker-inliner-constants/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/474-checker-boolean-input/src/Main.java b/test/474-checker-boolean-input/src/Main.java index 86d0f7c916..a2b219dd6d 100644 --- a/test/474-checker-boolean-input/src/Main.java +++ b/test/474-checker-boolean-input/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/475-regression-inliner-ids/src/Main.java b/test/475-regression-inliner-ids/src/Main.java index bf22062bcd..423c3b5c92 100644 --- a/test/475-regression-inliner-ids/src/Main.java +++ b/test/475-regression-inliner-ids/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ import java.lang.reflect.Method; diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java index e709ba0902..41bec057ee 100644 --- a/test/476-checker-ctor-memory-barrier/src/Main.java +++ b/test/476-checker-ctor-memory-barrier/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ // TODO: Add more tests after we can inline functions with calls. diff --git a/test/477-checker-bound-type/src/Main.java b/test/477-checker-bound-type/src/Main.java index fe52e83664..c873702408 100644 --- a/test/477-checker-bound-type/src/Main.java +++ b/test/477-checker-bound-type/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/478-checker-inliner-nested-loop/src/Main.java b/test/478-checker-inliner-nested-loop/src/Main.java index aa023491a5..86c119f3d0 100644 --- a/test/478-checker-inliner-nested-loop/src/Main.java +++ b/test/478-checker-inliner-nested-loop/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/479-regression-implicit-null-check/src/Main.java b/test/479-regression-implicit-null-check/src/Main.java index 6b6f2e4d2a..005ba7fbc2 100644 --- a/test/479-regression-implicit-null-check/src/Main.java +++ b/test/479-regression-implicit-null-check/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java index 4cc16344a4..5adafaf10d 100644 --- a/test/480-checker-dead-blocks/src/Main.java +++ b/test/480-checker-dead-blocks/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/481-regression-phi-cond/src/Main.java b/test/481-regression-phi-cond/src/Main.java index bad9669048..54982f7ec4 100644 --- a/test/481-regression-phi-cond/src/Main.java +++ b/test/481-regression-phi-cond/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java index 18125fad0b..2cfb04d652 100644 --- a/test/482-checker-loop-back-edge-use/src/Main.java +++ b/test/482-checker-loop-back-edge-use/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { diff --git a/test/484-checker-register-hints/src/Main.java b/test/484-checker-register-hints/src/Main.java index 3715ca2b14..6e68f7c91e 100644 --- a/test/484-checker-register-hints/src/Main.java +++ b/test/484-checker-register-hints/src/Main.java @@ -16,6 +16,14 @@ public class Main { + static class Foo { + int field0; + int field1; + int field2; + int field3; + int field4; + }; + /// CHECK-START: void Main.test1(boolean, int, int, int, int, int) register (after) /// CHECK: name "B0" /// CHECK-NOT: ParallelMove @@ -25,7 +33,7 @@ public class Main { /// CHECK-NOT: ParallelMove /// CHECK: name "B3" /// CHECK-NOT: end_block - /// CHECK: ArraySet + /// CHECK: InstanceFieldSet // We could check here that there is a parallel move, but it's only valid // for some architectures (for example x86), as other architectures may // not do move at all. @@ -36,19 +44,19 @@ public class Main { int e = live1; int f = live2; int g = live3; + int j = live0; if (z) { } else { // Create enough live instructions to force spilling on x86. int h = live4; int i = live5; - array[2] = e + i + h; - array[3] = f + i + h; - array[4] = g + i + h; - array[0] = h; - array[1] = i + h; - + foo.field2 = e + i + h; + foo.field3 = f + i + h; + foo.field4 = g + i + h; + foo.field0 = h; + foo.field1 = i + h; } - live1 = e + f + g; + live1 = e + f + g + j; } /// CHECK-START: void Main.test2(boolean, int, int, int, int, int) register (after) @@ -60,7 +68,7 @@ public class Main { /// CHECK-NOT: ParallelMove /// CHECK: name "B3" /// CHECK-NOT: end_block - /// CHECK: ArraySet + /// CHECK: InstanceFieldSet // We could check here that there is a parallel move, but it's only valid // for some architectures (for example x86), as other architectures may // not do move at all. @@ -71,18 +79,19 @@ public class Main { int e = live1; int f = live2; int g = live3; + int j = live0; if (z) { if (y) { int h = live4; int i = live5; - array[2] = e + i + h; - array[3] = f + i + h; - array[4] = g + i + h; - array[0] = h; - array[1] = i + h; + foo.field2 = e + i + h; + foo.field3 = f + i + h; + foo.field4 = g + i + h; + foo.field0 = h; + foo.field1 = i + h; } } - live1 = e + f + g; + live1 = e + f + g + j; } /// CHECK-START: void Main.test3(boolean, int, int, int, int, int) register (after) @@ -94,7 +103,7 @@ public class Main { /// CHECK-NOT: ParallelMove /// CHECK: name "B6" /// CHECK-NOT: end_block - /// CHECK: ArraySet + /// CHECK: InstanceFieldSet // We could check here that there is a parallel move, but it's only valid // for some architectures (for example x86), as other architectures may // not do move at all. @@ -107,6 +116,7 @@ public class Main { int e = live1; int f = live2; int g = live3; + int j = live0; if (z) { live1 = e; } else { @@ -115,24 +125,25 @@ public class Main { } else { int h = live4; int i = live5; - array[2] = e + i + h; - array[3] = f + i + h; - array[4] = g + i + h; - array[0] = h; - array[1] = i + h; + foo.field2 = e + i + h; + foo.field3 = f + i + h; + foo.field4 = g + i + h; + foo.field0 = h; + foo.field1 = i + h; } } - live1 = e + f + g; + live1 = e + f + g + j; } public static void main(String[] args) { } static boolean y; + static int live0; static int live1; static int live2; static int live3; static int live4; static int live5; - static int[] array; + static Foo foo; } diff --git a/test/491-current-method/src/Main.java b/test/491-current-method/src/Main.java index 87ef05218d..51a41a6cc7 100644 --- a/test/491-current-method/src/Main.java +++ b/test/491-current-method/src/Main.java @@ -16,7 +16,7 @@ class Main { - // The code below is written in a way that will crash + // The code below is written in a way that would crash // the generated code at the time of submission of this test. // Therefore, changes to the register allocator may // affect the reproducibility of the crash. @@ -25,8 +25,8 @@ class Main { // to put the ART current method. c = c / 42; // We use the empty string for forcing the slow path. - // The slow path for charAt when it is intrinsified, will - // move the parameter to ECX, and therefore overwrite the ART + // The slow path for charAt, when it is intrinsified, will + // move the parameter to ECX and therefore overwrite the ART // current method. "".charAt(c); diff --git a/test/508-checker-disassembly/src/Main.java b/test/508-checker-disassembly/src/Main.java index 29c9374aed..0805731267 100644 --- a/test/508-checker-disassembly/src/Main.java +++ b/test/508-checker-disassembly/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 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. -*/ + * Copyright (C) 2015 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. + */ public class Main { // A very simple check that disassembly information has been added to the diff --git a/test/525-arrays-and-fields/expected.txt b/test/525-checker-arrays-and-fields/expected.txt index e69de29bb2..e69de29bb2 100644 --- a/test/525-arrays-and-fields/expected.txt +++ b/test/525-checker-arrays-and-fields/expected.txt diff --git a/test/525-arrays-and-fields/info.txt b/test/525-checker-arrays-and-fields/info.txt index 3e16abf204..3e16abf204 100644 --- a/test/525-arrays-and-fields/info.txt +++ b/test/525-checker-arrays-and-fields/info.txt diff --git a/test/525-arrays-and-fields/src/Main.java b/test/525-checker-arrays-and-fields/src/Main.java index cb1e4afeab..a635a5157f 100644 --- a/test/525-arrays-and-fields/src/Main.java +++ b/test/525-checker-arrays-and-fields/src/Main.java @@ -80,56 +80,129 @@ public class Main { // // Loops on static arrays with invariant static field references. + // The checker is used to ensure hoisting occurred. // + /// CHECK-START: void Main.SInvLoopZ() licm (before) + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.SInvLoopZ() licm (after) + /// CHECK-DAG: StaticFieldGet loop:none + /// CHECK-DAG: StaticFieldGet loop:none + private static void SInvLoopZ() { for (int i = 0; i < sArrZ.length; i++) { sArrZ[i] = sZ; } } + /// CHECK-START: void Main.SInvLoopB() licm (before) + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.SInvLoopB() licm (after) + /// CHECK-DAG: StaticFieldGet loop:none + /// CHECK-DAG: StaticFieldGet loop:none + private static void SInvLoopB() { for (int i = 0; i < sArrB.length; i++) { sArrB[i] = sB; } } + /// CHECK-START: void Main.SInvLoopC() licm (before) + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.SInvLoopC() licm (after) + /// CHECK-DAG: StaticFieldGet loop:none + /// CHECK-DAG: StaticFieldGet loop:none + private static void SInvLoopC() { for (int i = 0; i < sArrC.length; i++) { sArrC[i] = sC; } } + /// CHECK-START: void Main.SInvLoopS() licm (before) + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.SInvLoopS() licm (after) + /// CHECK-DAG: StaticFieldGet loop:none + /// CHECK-DAG: StaticFieldGet loop:none + private static void SInvLoopS() { for (int i = 0; i < sArrS.length; i++) { sArrS[i] = sS; } } + /// CHECK-START: void Main.SInvLoopI() licm (before) + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.SInvLoopI() licm (after) + /// CHECK-DAG: StaticFieldGet loop:none + /// CHECK-DAG: StaticFieldGet loop:none + private static void SInvLoopI() { for (int i = 0; i < sArrI.length; i++) { sArrI[i] = sI; } } + /// CHECK-START: void Main.SInvLoopJ() licm (before) + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.SInvLoopJ() licm (after) + /// CHECK-DAG: StaticFieldGet loop:none + /// CHECK-DAG: StaticFieldGet loop:none + private static void SInvLoopJ() { for (int i = 0; i < sArrJ.length; i++) { sArrJ[i] = sJ; } } + /// CHECK-START: void Main.SInvLoopF() licm (before) + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.SInvLoopF() licm (after) + /// CHECK-DAG: StaticFieldGet loop:none + /// CHECK-DAG: StaticFieldGet loop:none + private static void SInvLoopF() { for (int i = 0; i < sArrF.length; i++) { sArrF[i] = sF; } } + /// CHECK-START: void Main.SInvLoopD() licm (before) + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.SInvLoopD() licm (after) + /// CHECK-DAG: StaticFieldGet loop:none + /// CHECK-DAG: StaticFieldGet loop:none + private static void SInvLoopD() { for (int i = 0; i < sArrD.length; i++) { sArrD[i] = sD; } } + /// CHECK-START: void Main.SInvLoopL() licm (before) + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + /// CHECK-DAG: StaticFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.SInvLoopL() licm (after) + /// CHECK-DAG: StaticFieldGet loop:none + /// CHECK-DAG: StaticFieldGet loop:none + private static void SInvLoopL() { for (int i = 0; i < sArrL.length; i++) { sArrL[i] = sL; @@ -138,6 +211,7 @@ public class Main { // // Loops on static arrays with variant static field references. + // Incorrect hoisting is detected by incorrect outcome. // private static void SVarLoopZ() { @@ -214,56 +288,130 @@ public class Main { // // Loops on static arrays with a cross-over reference. + // Incorrect hoisting is detected by incorrect outcome. + // In addition, the checker is used to detect no hoisting. // + /// CHECK-START: void Main.SCrossOverLoopZ() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.SCrossOverLoopZ() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private static void SCrossOverLoopZ() { for (int i = 0; i < sArrZ.length; i++) { sArrZ[i] = !sArrZ[20]; } } + /// CHECK-START: void Main.SCrossOverLoopB() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.SCrossOverLoopB() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private static void SCrossOverLoopB() { for (int i = 0; i < sArrB.length; i++) { sArrB[i] = (byte)(sArrB[20] + 2); } } + /// CHECK-START: void Main.SCrossOverLoopC() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.SCrossOverLoopC() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private static void SCrossOverLoopC() { for (int i = 0; i < sArrC.length; i++) { sArrC[i] = (char)(sArrC[20] + 2); } } + /// CHECK-START: void Main.SCrossOverLoopS() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.SCrossOverLoopS() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private static void SCrossOverLoopS() { for (int i = 0; i < sArrS.length; i++) { sArrS[i] = (short)(sArrS[20] + 2); } } + /// CHECK-START: void Main.SCrossOverLoopI() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.SCrossOverLoopI() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private static void SCrossOverLoopI() { for (int i = 0; i < sArrI.length; i++) { sArrI[i] = sArrI[20] + 2; } } + /// CHECK-START: void Main.SCrossOverLoopJ() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.SCrossOverLoopJ() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private static void SCrossOverLoopJ() { for (int i = 0; i < sArrJ.length; i++) { sArrJ[i] = sArrJ[20] + 2; } } + /// CHECK-START: void Main.SCrossOverLoopF() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.SCrossOverLoopF() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private static void SCrossOverLoopF() { for (int i = 0; i < sArrF.length; i++) { sArrF[i] = sArrF[20] + 2; } } + /// CHECK-START: void Main.SCrossOverLoopD() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.SCrossOverLoopD() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private static void SCrossOverLoopD() { for (int i = 0; i < sArrD.length; i++) { sArrD[i] = sArrD[20] + 2; } } + /// CHECK-START: void Main.SCrossOverLoopL() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.SCrossOverLoopL() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private static void SCrossOverLoopL() { for (int i = 0; i < sArrL.length; i++) { sArrL[i] = (sArrL[20] == anObject) ? anotherObject : anObject; @@ -272,56 +420,129 @@ public class Main { // // Loops on instance arrays with invariant instance field references. + // The checker is used to ensure hoisting occurred. // + /// CHECK-START: void Main.InvLoopZ() licm (before) + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.InvLoopZ() licm (after) + /// CHECK-DAG: InstanceFieldGet loop:none + /// CHECK-DAG: InstanceFieldGet loop:none + private void InvLoopZ() { for (int i = 0; i < mArrZ.length; i++) { mArrZ[i] = mZ; } } + /// CHECK-START: void Main.InvLoopB() licm (before) + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.InvLoopB() licm (after) + /// CHECK-DAG: InstanceFieldGet loop:none + /// CHECK-DAG: InstanceFieldGet loop:none + private void InvLoopB() { for (int i = 0; i < mArrB.length; i++) { mArrB[i] = mB; } } + /// CHECK-START: void Main.InvLoopC() licm (before) + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.InvLoopC() licm (after) + /// CHECK-DAG: InstanceFieldGet loop:none + /// CHECK-DAG: InstanceFieldGet loop:none + private void InvLoopC() { for (int i = 0; i < mArrC.length; i++) { mArrC[i] = mC; } } + /// CHECK-START: void Main.InvLoopS() licm (before) + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.InvLoopS() licm (after) + /// CHECK-DAG: InstanceFieldGet loop:none + /// CHECK-DAG: InstanceFieldGet loop:none + private void InvLoopS() { for (int i = 0; i < mArrS.length; i++) { mArrS[i] = mS; } } + /// CHECK-START: void Main.InvLoopI() licm (before) + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.InvLoopI() licm (after) + /// CHECK-DAG: InstanceFieldGet loop:none + /// CHECK-DAG: InstanceFieldGet loop:none + private void InvLoopI() { for (int i = 0; i < mArrI.length; i++) { mArrI[i] = mI; } } + /// CHECK-START: void Main.InvLoopJ() licm (before) + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.InvLoopJ() licm (after) + /// CHECK-DAG: InstanceFieldGet loop:none + /// CHECK-DAG: InstanceFieldGet loop:none + private void InvLoopJ() { for (int i = 0; i < mArrJ.length; i++) { mArrJ[i] = mJ; } } + /// CHECK-START: void Main.InvLoopF() licm (before) + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.InvLoopF() licm (after) + /// CHECK-DAG: InstanceFieldGet loop:none + /// CHECK-DAG: InstanceFieldGet loop:none + private void InvLoopF() { for (int i = 0; i < mArrF.length; i++) { mArrF[i] = mF; } } + /// CHECK-START: void Main.InvLoopD() licm (before) + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.InvLoopD() licm (after) + /// CHECK-DAG: InstanceFieldGet loop:none + /// CHECK-DAG: InstanceFieldGet loop:none + private void InvLoopD() { for (int i = 0; i < mArrD.length; i++) { mArrD[i] = mD; } } + /// CHECK-START: void Main.InvLoopL() licm (before) + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}} + + /// CHECK-START: void Main.InvLoopL() licm (after) + /// CHECK-DAG: InstanceFieldGet loop:none + /// CHECK-DAG: InstanceFieldGet loop:none + private void InvLoopL() { for (int i = 0; i < mArrL.length; i++) { mArrL[i] = mL; @@ -330,6 +551,7 @@ public class Main { // // Loops on instance arrays with variant instance field references. + // Incorrect hoisting is detected by incorrect outcome. // private void VarLoopZ() { @@ -406,56 +628,130 @@ public class Main { // // Loops on instance arrays with a cross-over reference. + // Incorrect hoisting is detected by incorrect outcome. + // In addition, the checker is used to detect no hoisting. // + /// CHECK-START: void Main.CrossOverLoopZ() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.CrossOverLoopZ() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private void CrossOverLoopZ() { for (int i = 0; i < mArrZ.length; i++) { mArrZ[i] = !mArrZ[20]; } } + /// CHECK-START: void Main.CrossOverLoopB() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.CrossOverLoopB() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private void CrossOverLoopB() { for (int i = 0; i < mArrB.length; i++) { mArrB[i] = (byte)(mArrB[20] + 2); } } + /// CHECK-START: void Main.CrossOverLoopC() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.CrossOverLoopC() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private void CrossOverLoopC() { for (int i = 0; i < mArrC.length; i++) { mArrC[i] = (char)(mArrC[20] + 2); } } + /// CHECK-START: void Main.CrossOverLoopS() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.CrossOverLoopS() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private void CrossOverLoopS() { for (int i = 0; i < mArrS.length; i++) { mArrS[i] = (short)(mArrS[20] + 2); } } + /// CHECK-START: void Main.CrossOverLoopI() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.CrossOverLoopI() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private void CrossOverLoopI() { for (int i = 0; i < mArrI.length; i++) { mArrI[i] = mArrI[20] + 2; } } + /// CHECK-START: void Main.CrossOverLoopJ() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.CrossOverLoopJ() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private void CrossOverLoopJ() { for (int i = 0; i < mArrJ.length; i++) { mArrJ[i] = mArrJ[20] + 2; } } + /// CHECK-START: void Main.CrossOverLoopF() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.CrossOverLoopF() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private void CrossOverLoopF() { for (int i = 0; i < mArrF.length; i++) { mArrF[i] = mArrF[20] + 2; } } + /// CHECK-START: void Main.CrossOverLoopD() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.CrossOverLoopD() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private void CrossOverLoopD() { for (int i = 0; i < mArrD.length; i++) { mArrD[i] = mArrD[20] + 2; } } + /// CHECK-START: void Main.CrossOverLoopL() licm (before) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + + /// CHECK-START: void Main.CrossOverLoopL() licm (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: ArraySet loop:{{B\d+}} + private void CrossOverLoopL() { for (int i = 0; i < mArrL.length; i++) { mArrL[i] = (mArrL[20] == anObject) ? anotherObject : anObject; diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 992a8a6ea1..d58f034f93 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -150,5 +150,12 @@ result: EXEC_FAILED, modes: [device], names: ["org.apache.harmony.tests.java.lang.ClassTest#test_forNameLjava_lang_String"] +}, +{ + description: "TimeZoneTest.testAllDisplayNames times out, needs investigation", + result: EXEC_FAILED, + modes: [device], + names: ["libcore.java.util.TimeZoneTest.testAllDisplayNames"], + bug: 22786792 } ] |