diff options
58 files changed, 1856 insertions, 365 deletions
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 4180e0e6c9..86d92ff0b5 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -487,7 +487,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(20U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(163 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 541a1c5b8f..3009103ac7 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -3936,6 +3936,7 @@ void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { } else { InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } locations->SetOut(Location::RegisterLocation(R0)); } @@ -3953,7 +3954,7 @@ void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } else { codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>(); + CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); } } diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 9aaeadb44a..4b6a9bed61 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -4744,6 +4744,7 @@ void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) { locations->AddTemp(LocationFrom(kArtMethodRegister)); } else { locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); } locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); } @@ -4761,7 +4762,7 @@ void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } else { codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>(); + CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); } } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index c769decaa0..b1f6d599ab 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -3948,6 +3948,7 @@ void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) { } else { InvokeRuntimeCallingConventionARMVIXL calling_convention; locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); } locations->SetOut(LocationFrom(r0)); } @@ -3969,7 +3970,7 @@ void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } else { codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>(); + CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); } } diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index bc62854e5d..9af03e8153 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -5903,6 +5903,7 @@ void LocationsBuilderMIPS::VisitNewInstance(HNewInstance* instruction) { locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); } else { locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); } @@ -5919,7 +5920,7 @@ void InstructionCodeGeneratorMIPS::VisitNewInstance(HNewInstance* instruction) { codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } else { codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>(); + CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); } } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 1b9c6da460..046d59cee7 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -3844,6 +3844,7 @@ void LocationsBuilderMIPS64::VisitNewInstance(HNewInstance* instruction) { locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); } else { locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); } @@ -3861,7 +3862,7 @@ void InstructionCodeGeneratorMIPS64::VisitNewInstance(HNewInstance* instruction) codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } else { codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>(); + CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); } } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index a9b717db4f..f13b60aebf 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -4150,6 +4150,7 @@ void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { } else { InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } } @@ -4165,7 +4166,7 @@ void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) { codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } else { codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>(); + CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); DCHECK(!codegen_->IsLeafMethod()); } } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 261473505f..89f4ae04d7 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -4038,6 +4038,7 @@ void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) { locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); } else { locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } locations->SetOut(Location::RegisterLocation(RAX)); } @@ -4054,7 +4055,7 @@ void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } else { codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>(); + CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); DCHECK(!codegen_->IsLeafMethod()); } } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index c970e5cbba..e5d05e9e6d 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1428,6 +1428,15 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, return false; } + if (current->IsNewInstance() && + (current->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectWithAccessCheck)) { + VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) + << " could not be inlined because it is using an entrypoint" + << " with access checks"; + // Allocation entrypoint does not handle inlined frames. + return false; + } + if (current->IsNewArray() && (current->AsNewArray()->GetEntrypoint() == kQuickAllocArrayWithAccessCheck)) { VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 009d549547..768b1d80a1 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -917,11 +917,11 @@ bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t d bool finalizable; bool needs_access_check = NeedsAccessCheck(type_index, dex_cache, &finalizable); - // Only the access check entrypoint handles the finalizable class case. If we + // Only the non-resolved entrypoint handles the finalizable class case. If we // need access checks, then we haven't resolved the method and the class may // again be finalizable. QuickEntrypointEnum entrypoint = (finalizable || needs_access_check) - ? kQuickAllocObjectWithChecks + ? kQuickAllocObject : kQuickAllocObjectInitialized; if (outer_dex_cache.Get() != dex_cache.Get()) { @@ -946,6 +946,7 @@ bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t d AppendInstruction(new (arena_) HNewInstance( cls, + graph_->GetCurrentMethod(), dex_pc, type_index, *dex_compilation_unit_->GetDexFile(), diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index ea9a94c420..7d6f6164ec 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -3774,9 +3774,10 @@ class HCompare FINAL : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HCompare); }; -class HNewInstance FINAL : public HExpression<1> { +class HNewInstance FINAL : public HExpression<2> { public: HNewInstance(HInstruction* cls, + HCurrentMethod* current_method, uint32_t dex_pc, dex::TypeIndex type_index, const DexFile& dex_file, @@ -3790,6 +3791,7 @@ class HNewInstance FINAL : public HExpression<1> { SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); SetPackedFlag<kFlagFinalizable>(finalizable); SetRawInputAt(0, cls); + SetRawInputAt(1, current_method); } dex::TypeIndex GetTypeIndex() const { return type_index_; } diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index db7c1fbb06..f9ac3a0f72 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -134,6 +134,39 @@ void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) { } } +void PrepareForRegisterAllocation::VisitNewInstance(HNewInstance* instruction) { + HLoadClass* load_class = instruction->InputAt(0)->AsLoadClass(); + const bool has_only_one_use = load_class->HasOnlyOneNonEnvironmentUse(); + // Change the entrypoint to kQuickAllocObject if either: + // - the class is finalizable (only kQuickAllocObject handles finalizable classes), + // - the class needs access checks (we do not know if it's finalizable), + // - or the load class has only one use. + if (instruction->IsFinalizable() || has_only_one_use || load_class->NeedsAccessCheck()) { + instruction->SetEntrypoint(kQuickAllocObject); + instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex().index_), 0); + if (has_only_one_use) { + // We've just removed the only use of the HLoadClass. Since we don't run DCE after this pass, + // do it manually if possible. + if (!load_class->CanThrow()) { + // If the load class can not throw, it has no side effects and can be removed if there is + // only one use. + load_class->GetBlock()->RemoveInstruction(load_class); + } else if (!instruction->GetEnvironment()->IsFromInlinedInvoke() && + CanMoveClinitCheck(load_class, instruction)) { + // The allocation entry point that deals with access checks does not work with inlined + // methods, so we need to check whether this allocation comes from an inlined method. + // We also need to make the same check as for moving clinit check, whether the HLoadClass + // has the clinit check responsibility or not (HLoadClass can throw anyway). + // If it needed access checks, we delegate the access check to the allocation. + if (load_class->NeedsAccessCheck()) { + instruction->SetEntrypoint(kQuickAllocObjectWithAccessCheck); + } + load_class->GetBlock()->RemoveInstruction(load_class); + } + } + } +} + bool PrepareForRegisterAllocation::CanEmitConditionAt(HCondition* condition, HInstruction* user) const { if (condition->GetNext() != user) { diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index c128227654..a6791482a7 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -44,6 +44,7 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { void VisitClinitCheck(HClinitCheck* check) OVERRIDE; void VisitCondition(HCondition* condition) OVERRIDE; void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE; + void VisitNewInstance(HNewInstance* instruction) OVERRIDE; bool CanMoveClinitCheck(HInstruction* input, HInstruction* user) const; bool CanEmitConditionAt(HCondition* condition, HInstruction* user) const; diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index a3fce02970..ab4f9e944c 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -5610,7 +5610,7 @@ const char* const VixlJniHelpersResults[] = { " 214: ecbd 8a10 vpop {s16-s31}\n", " 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n", " 21c: 4660 mov r0, ip\n", - " 21e: f8d9 c2ac ldr.w ip, [r9, #684] ; 0x2ac\n", + " 21e: f8d9 c2b0 ldr.w ip, [r9, #688] ; 0x2b0\n", " 222: 47e0 blx ip\n", nullptr }; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 4d4ebdcad8..a71ab4b53c 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1124,23 +1124,28 @@ END art_quick_resolve_string // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_RESOLVED_OBJECT(_rosalloc, RosAlloc). -ENTRY art_quick_alloc_object_resolved_rosalloc +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). +ENTRY art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. - // r0: type/return value, r9: Thread::Current - // r1, r2, r3, r12: free. + // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current + // r2, r3, r12: free. + ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array + // Load the class (r2) + ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + cbz r2, .Lart_quick_alloc_object_rosalloc_slow_path // Check null class + ldr r3, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Check if the thread local // allocation stack has room. // TODO: consider using ldrd. ldr r12, [r9, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET] cmp r3, r12 - bhs .Lart_quick_alloc_object_resolved_rosalloc_slow_path + bhs .Lart_quick_alloc_object_rosalloc_slow_path - ldr r3, [r0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (r3) + ldr r3, [r2, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (r3) cmp r3, #ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE // Check if the size is for a thread // local allocation. Also does the // initialized and finalizable checks. - bhs .Lart_quick_alloc_object_resolved_rosalloc_slow_path + bhs .Lart_quick_alloc_object_rosalloc_slow_path // Compute the rosalloc bracket index // from the size. Since the size is // already aligned we can combine the @@ -1154,7 +1159,7 @@ ENTRY art_quick_alloc_object_resolved_rosalloc // Load the free list head (r3). This // will be the return val. ldr r3, [r12, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)] - cbz r3, .Lart_quick_alloc_object_resolved_rosalloc_slow_path + cbz r3, .Lart_quick_alloc_object_rosalloc_slow_path // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. ldr r1, [r3, #ROSALLOC_SLOT_NEXT_OFFSET] // Load the next pointer of the head // and update the list head with the @@ -1167,8 +1172,8 @@ ENTRY art_quick_alloc_object_resolved_rosalloc #if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET #error "Class pointer needs to overwrite next pointer." #endif - POISON_HEAP_REF r0 - str r0, [r3, #MIRROR_OBJECT_CLASS_OFFSET] + POISON_HEAP_REF r2 + str r2, [r3, #MIRROR_OBJECT_CLASS_OFFSET] // Fence. This is "ish" not "ishst" so // that it also ensures ordering of // the class status load with respect @@ -1199,20 +1204,20 @@ ENTRY art_quick_alloc_object_resolved_rosalloc mov r0, r3 // Set the return value and return. bx lr -.Lart_quick_alloc_object_resolved_rosalloc_slow_path: +.Lart_quick_alloc_object_rosalloc_slow_path: SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r1, r9 @ pass Thread::Current - bl artAllocObjectFromCodeResolvedRosAlloc @ (mirror::Class* cls, Thread*) + mov r2, r9 @ pass Thread::Current + bl artAllocObjectFromCodeRosAlloc @ (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -END art_quick_alloc_object_resolved_rosalloc +END art_quick_alloc_object_rosalloc -// The common fast path code for art_quick_alloc_object_resolved_tlab -// and art_quick_alloc_object_resolved_region_tlab. +// The common fast path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab. // -// r0: type r9: Thread::Current, r1, r2, r3, r12: free. -// Need to preserve r0 to the slow path. -.macro ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH slowPathLabel +// r0: type_idx/return value, r1: ArtMethod*, r2: class, r9: Thread::Current, r3, r12: free. +// Need to preserve r0 and r1 to the slow path. +.macro ALLOC_OBJECT_TLAB_FAST_PATH slowPathLabel + cbz r2, \slowPathLabel // Check null class // Load thread_local_pos (r12) and // thread_local_end (r3) with ldrd. // Check constraints for ldrd. @@ -1227,14 +1232,14 @@ END art_quick_alloc_object_resolved_rosalloc // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. // Reload old thread_local_pos (r0) // for the return value. - ldr r2, [r9, #THREAD_LOCAL_POS_OFFSET] - add r1, r2, r3 + ldr r0, [r9, #THREAD_LOCAL_POS_OFFSET] + add r1, r0, r3 str r1, [r9, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. ldr r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. add r1, r1, #1 str r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] - POISON_HEAP_REF r0 - str r0, [r2, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. + POISON_HEAP_REF r2 + str r2, [r0, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. // Fence. This is "ish" not "ishst" so // that the code after this allocation // site will see the right values in @@ -1242,46 +1247,71 @@ END art_quick_alloc_object_resolved_rosalloc // Alternatively we could use "ishst" // if we use load-acquire for the // object size load.) - mov r0, r2 dmb ish bx lr .endm -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_RESOLVED_OBJECT(_tlab, TLAB). -ENTRY art_quick_alloc_object_resolved_tlab +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). +ENTRY art_quick_alloc_object_tlab // Fast path tlab allocation. - // r0: type, r9: Thread::Current - // r1, r2, r3, r12: free. + // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current + // r2, r3, r12: free. #if defined(USE_READ_BARRIER) mvn r0, #0 // Read barrier not supported here. bx lr // Return -1. #endif - ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_tlab_slow_path -.Lart_quick_alloc_object_resolved_tlab_slow_path: + ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array + // Load the class (r2) + ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path +.Lart_quick_alloc_object_tlab_slow_path: SETUP_SAVE_REFS_ONLY_FRAME r2 // Save callee saves in case of GC. - mov r1, r9 // Pass Thread::Current. - bl artAllocObjectFromCodeResolvedTLAB // (mirror::Class* klass, Thread*) + mov r2, r9 // Pass Thread::Current. + bl artAllocObjectFromCodeTLAB // (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -END art_quick_alloc_object_resolved_tlab +END art_quick_alloc_object_tlab -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) -ENTRY art_quick_alloc_object_resolved_region_tlab +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) +ENTRY art_quick_alloc_object_region_tlab // Fast path tlab allocation. - // r0: type, r9: Thread::Current, r1, r2, r3, r12: free. + // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current, r2, r3, r12: free. #if !defined(USE_READ_BARRIER) eor r0, r0, r0 // Read barrier must be enabled here. sub r0, r0, #1 // Return -1. bx lr #endif - ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_region_tlab_slow_path -.Lart_quick_alloc_object_resolved_region_tlab_slow_path: + ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array + // Load the class (r2) + ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + // Read barrier for class load. + ldr r3, [r9, #THREAD_IS_GC_MARKING_OFFSET] + cbnz r3, .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit: + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking: + cbz r2, .Lart_quick_alloc_object_region_tlab_slow_path // Null check for loading lock word. + // Check lock word for mark bit, if marked do the allocation. + ldr r3, [r2, MIRROR_OBJECT_LOCK_WORD_OFFSET] + ands r3, #LOCK_WORD_MARK_BIT_MASK_SHIFTED + bne .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path: + // The read barrier slow path. Mark + // the class. + push {r0, r1, r3, lr} // Save registers. r3 is pushed only + // to align sp by 16 bytes. + mov r0, r2 // Pass the class as the first param. + bl artReadBarrierMark + mov r2, r0 // Get the (marked) class back. + pop {r0, r1, r3, lr} + b .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit +.Lart_quick_alloc_object_region_tlab_slow_path: SETUP_SAVE_REFS_ONLY_FRAME r2 // Save callee saves in case of GC. - mov r1, r9 // Pass Thread::Current. - bl artAllocObjectFromCodeResolvedRegionTLAB // (mirror::Class* klass, Thread*) + mov r2, r9 // Pass Thread::Current. + bl artAllocObjectFromCodeRegionTLAB // (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -END art_quick_alloc_object_resolved_region_tlab +END art_quick_alloc_object_region_tlab /* * Called by managed code when the value in rSUSPEND has been decremented to 0. diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 8b1e0388c6..b88515f21f 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1669,6 +1669,7 @@ END art_quick_resolve_string // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_REGION_TLAB_ALLOCATORS // Comment out allocators that have arm64 specific asm. +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) implemented in asm // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) @@ -1681,23 +1682,27 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc). -ENTRY art_quick_alloc_object_resolved_rosalloc +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). +ENTRY art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. - // x0: type, xSELF(x19): Thread::Current - // x1-x7: free. + // x0: type_idx/return value, x1: ArtMethod*, xSELF(x19): Thread::Current + // x2-x7: free. + ldr x2, [x1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64] // Load dex cache resolved types array + // Load the class (x2) + ldr w2, [x2, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + cbz x2, .Lart_quick_alloc_object_rosalloc_slow_path // Check null class ldr x3, [xSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Check if the thread local // allocation stack has room. // ldp won't work due to large offset. ldr x4, [xSELF, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET] cmp x3, x4 - bhs .Lart_quick_alloc_object_resolved_rosalloc_slow_path - ldr w3, [x0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (x3) + bhs .Lart_quick_alloc_object_rosalloc_slow_path + ldr w3, [x2, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (x3) cmp x3, #ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE // Check if the size is for a thread // local allocation. Also does the // finalizable and initialization // checks. - bhs .Lart_quick_alloc_object_resolved_rosalloc_slow_path + bhs .Lart_quick_alloc_object_rosalloc_slow_path // Compute the rosalloc bracket index // from the size. Since the size is // already aligned we can combine the @@ -1710,7 +1715,7 @@ ENTRY art_quick_alloc_object_resolved_rosalloc // Load the free list head (x3). This // will be the return val. ldr x3, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)] - cbz x3, .Lart_quick_alloc_object_resolved_rosalloc_slow_path + cbz x3, .Lart_quick_alloc_object_rosalloc_slow_path // "Point of no slow path". Won't go to the slow path from here on. OK to clobber x0 and x1. ldr x1, [x3, #ROSALLOC_SLOT_NEXT_OFFSET] // Load the next pointer of the head // and update the list head with the @@ -1723,8 +1728,8 @@ ENTRY art_quick_alloc_object_resolved_rosalloc #if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET #error "Class pointer needs to overwrite next pointer." #endif - POISON_HEAP_REF w0 - str w0, [x3, #MIRROR_OBJECT_CLASS_OFFSET] + POISON_HEAP_REF w2 + str w2, [x3, #MIRROR_OBJECT_CLASS_OFFSET] // Fence. This is "ish" not "ishst" so // that it also ensures ordering of // the object size load with respect @@ -1754,13 +1759,13 @@ ENTRY art_quick_alloc_object_resolved_rosalloc mov x0, x3 // Set the return value and return. ret -.Lart_quick_alloc_object_resolved_rosalloc_slow_path: - SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case of GC - mov x1, xSELF // pass Thread::Current - bl artAllocObjectFromCodeResolvedRosAlloc // (mirror::Class* klass, Thread*) +.Lart_quick_alloc_object_rosalloc_slow_path: + SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case of GC + mov x2, xSELF // pass Thread::Current + bl artAllocObjectFromCodeRosAlloc // (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -END art_quick_alloc_object_resolved_rosalloc +END art_quick_alloc_object_rosalloc // The common fast path code for art_quick_alloc_array_region_tlab. @@ -1829,6 +1834,16 @@ END art_quick_alloc_object_resolved_rosalloc ret .endm +// The common fast path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab. +// +// x0: type_idx/return value, x1: ArtMethod*, x2: Class*, xSELF(x19): Thread::Current +// x3-x7: free. +// Need to preserve x0 and x1 to the slow path. +.macro ALLOC_OBJECT_TLAB_FAST_PATH slowPathLabel + cbz x2, \slowPathLabel // Check null class + ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED \slowPathLabel +.endm + // TODO: delete ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED since it is the same as // ALLOC_OBJECT_TLAB_FAST_PATH_INITIALIZED. .macro ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED slowPathLabel @@ -1838,18 +1853,20 @@ END art_quick_alloc_object_resolved_rosalloc .macro ALLOC_OBJECT_TLAB_FAST_PATH_INITIALIZED slowPathLabel ldr x4, [xSELF, #THREAD_LOCAL_POS_OFFSET] ldr x5, [xSELF, #THREAD_LOCAL_END_OFFSET] - ldr w7, [x0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (x7). + ldr w7, [x2, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (x7). add x6, x4, x7 // Add object size to tlab pos. cmp x6, x5 // Check if it fits, overflow works // since the tlab pos and end are 32 // bit values. bhi \slowPathLabel + // "Point of no slow path". Won't go to the slow path from here on. OK to clobber x0 and x1. + mov x0, x4 str x6, [xSELF, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. ldr x5, [xSELF, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. add x5, x5, #1 str x5, [xSELF, #THREAD_LOCAL_OBJECTS_OFFSET] - POISON_HEAP_REF w0 - str w0, [x4, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. + POISON_HEAP_REF w2 + str w2, [x0, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. // Fence. This is "ish" not "ishst" so // that the code after this allocation // site will see the right values in @@ -1857,52 +1874,91 @@ END art_quick_alloc_object_resolved_rosalloc // Alternatively we could use "ishst" // if we use load-acquire for the // object size load.) - mov x0, x4 dmb ish ret .endm -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB). -ENTRY art_quick_alloc_object_resolved_tlab +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). +ENTRY art_quick_alloc_object_tlab // Fast path tlab allocation. - // x0: type, xSELF(x19): Thread::Current - // x1-x7: free. + // x0: type_idx/return value, x1: ArtMethod*, xSELF(x19): Thread::Current + // x2-x7: free. #if defined(USE_READ_BARRIER) mvn x0, xzr // Read barrier not supported here. ret // Return -1. #endif - ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_object_resolved_tlab_slow_path -.Lart_quick_alloc_object_resolved_tlab_slow_path: - SETUP_SAVE_REFS_ONLY_FRAME // Save callee saves in case of GC. - mov x1, xSELF // Pass Thread::Current. - bl artAllocObjectFromCodeResolvedTLAB // (mirror::Class*, Thread*) + ldr x2, [x1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64] // Load dex cache resolved types array + // Load the class (x2) + ldr w2, [x2, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path +.Lart_quick_alloc_object_tlab_slow_path: + SETUP_SAVE_REFS_ONLY_FRAME // Save callee saves in case of GC. + mov x2, xSELF // Pass Thread::Current. + bl artAllocObjectFromCodeTLAB // (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -END art_quick_alloc_object_resolved_tlab +END art_quick_alloc_object_tlab // The common code for art_quick_alloc_object_*region_tlab -.macro GENERATE_ALLOC_OBJECT_RESOLVED_REGION_TLAB name, entrypoint, fast_path +.macro GENERATE_ALLOC_OBJECT_REGION_TLAB name, entrypoint, fast_path, is_resolved, read_barrier ENTRY \name // Fast path region tlab allocation. - // x0: type, xSELF(x19): Thread::Current - // x1-x7: free. + // x0: type_idx/resolved class/return value, x1: ArtMethod*, xSELF(x19): Thread::Current + // If is_resolved is 1 then x0 is the resolved type, otherwise it is the index. + // x2-x7: free. #if !defined(USE_READ_BARRIER) mvn x0, xzr // Read barrier must be enabled here. ret // Return -1. #endif +.if \is_resolved + mov x2, x0 // class is actually stored in x0 already +.else + ldr x2, [x1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64] // Load dex cache resolved types array + // Load the class (x2) + ldr w2, [x2, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + // If the class is null, go slow path. The check is required to read the lock word. + cbz w2, .Lslow_path\name +.endif +.if \read_barrier + // Most common case: GC is not marking. + ldr w3, [xSELF, #THREAD_IS_GC_MARKING_OFFSET] + cbnz x3, .Lmarking\name +.endif .Ldo_allocation\name: \fast_path .Lslow_path\name +.Lmarking\name: +.if \read_barrier + // GC is marking, check the lock word of the class for the mark bit. + // Class is not null, check mark bit in lock word. + ldr w3, [x2, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + // If the bit is not zero, do the allocation. + tbnz w3, #LOCK_WORD_MARK_BIT_SHIFT, .Ldo_allocation\name + // The read barrier slow path. Mark + // the class. + SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32 // Save registers (x0, x1, lr). + SAVE_REG xLR, 24 // Align sp by 16 bytes. + mov x0, x2 // Pass the class as the first param. + bl artReadBarrierMark + mov x2, x0 // Get the (marked) class back. + RESTORE_REG xLR, 24 + RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32 // Restore registers. + b .Ldo_allocation\name +.endif .Lslow_path\name: SETUP_SAVE_REFS_ONLY_FRAME // Save callee saves in case of GC. - mov x1, xSELF // Pass Thread::Current. - bl \entrypoint // (mirror::Class*, Thread*) + mov x2, xSELF // Pass Thread::Current. + bl \entrypoint // (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER END \name .endm -GENERATE_ALLOC_OBJECT_RESOLVED_REGION_TLAB art_quick_alloc_object_resolved_region_tlab, artAllocObjectFromCodeResolvedRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED -GENERATE_ALLOC_OBJECT_RESOLVED_REGION_TLAB art_quick_alloc_object_initialized_region_tlab, artAllocObjectFromCodeInitializedRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_INITIALIZED +// Use ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED since the null check is already done in GENERATE_ALLOC_OBJECT_TLAB. +GENERATE_ALLOC_OBJECT_REGION_TLAB art_quick_alloc_object_region_tlab, artAllocObjectFromCodeRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED, 0, 1 +// No read barrier for the resolved or initialized cases since the caller is responsible for the +// read barrier due to the to-space invariant. +GENERATE_ALLOC_OBJECT_REGION_TLAB art_quick_alloc_object_resolved_region_tlab, artAllocObjectFromCodeResolvedRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED, 1, 0 +GENERATE_ALLOC_OBJECT_REGION_TLAB art_quick_alloc_object_initialized_region_tlab, artAllocObjectFromCodeInitializedRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_INITIALIZED, 1, 0 // TODO: We could use this macro for the normal tlab allocator too. diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 964ea563b0..3e8cdc9374 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1831,10 +1831,116 @@ END \name // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). +ENTRY art_quick_alloc_object_rosalloc + + # Fast path rosalloc allocation + # a0: type_idx + # a1: ArtMethod* + # s1: Thread::Current + # ----------------------------- + # t0: class + # t1: object size + # t2: rosalloc run + # t3: thread stack top offset + # t4: thread stack bottom offset + # v0: free list head + # + # t5, t6 : temps + + lw $t0, ART_METHOD_DEX_CACHE_TYPES_OFFSET_32($a1) # Load dex cache resolved types + # array. + + sll $t5, $a0, COMPRESSED_REFERENCE_SIZE_SHIFT # Shift the value. + addu $t5, $t0, $t5 # Compute the index. + lw $t0, 0($t5) # Load class (t0). + beqz $t0, .Lart_quick_alloc_object_rosalloc_slow_path + + li $t6, MIRROR_CLASS_STATUS_INITIALIZED + lw $t5, MIRROR_CLASS_STATUS_OFFSET($t0) # Check class status. + bne $t5, $t6, .Lart_quick_alloc_object_rosalloc_slow_path + + # Add a fake dependence from the following access flag and size loads to the status load. This + # is to prevent those loads from being reordered above the status load and reading wrong values. + xor $t5, $t5, $t5 + addu $t0, $t0, $t5 + + lw $t5, MIRROR_CLASS_ACCESS_FLAGS_OFFSET($t0) # Check if access flags has + li $t6, ACCESS_FLAGS_CLASS_IS_FINALIZABLE # kAccClassIsFinalizable. + and $t6, $t5, $t6 + bnez $t6, .Lart_quick_alloc_object_rosalloc_slow_path + + lw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) # Check if thread local allocation + lw $t4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1) # stack has any room left. + bgeu $t3, $t4, .Lart_quick_alloc_object_rosalloc_slow_path + + lw $t1, MIRROR_CLASS_OBJECT_SIZE_OFFSET($t0) # Load object size (t1). + li $t5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE # Check if size is for a thread local + # allocation. + bgtu $t1, $t5, .Lart_quick_alloc_object_rosalloc_slow_path + + # Compute the rosalloc bracket index from the size. Allign up the size by the rosalloc bracket + # quantum size and divide by the quantum size and subtract by 1. + + addiu $t1, $t1, -1 # Decrease obj size and shift right + srl $t1, $t1, ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT # by quantum. + + sll $t2, $t1, POINTER_SIZE_SHIFT + addu $t2, $t2, $s1 + lw $t2, THREAD_ROSALLOC_RUNS_OFFSET($t2) # Load rosalloc run (t2). + + # Load the free list head (v0). + # NOTE: this will be the return val. + + lw $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + beqz $v0, .Lart_quick_alloc_object_rosalloc_slow_path + nop + + # Load the next pointer of the head and update the list head with the next pointer. + + lw $t5, ROSALLOC_SLOT_NEXT_OFFSET($v0) + sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + + # Store the class pointer in the header. This also overwrites the first pointer. The offsets are + # asserted to match. + +#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET +#error "Class pointer needs to overwrite next pointer." +#endif + + POISON_HEAP_REF $t0 + sw $t0, MIRROR_OBJECT_CLASS_OFFSET($v0) + + # Push the new object onto the thread local allocation stack and increment the thread local + # allocation stack top. + + sw $v0, 0($t3) + addiu $t3, $t3, COMPRESSED_REFERENCE_SIZE + sw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) + + # Decrement the size of the free list. + + lw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + addiu $t5, $t5, -1 + sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + + sync # Fence. + + jalr $zero, $ra + nop + + .Lart_quick_alloc_object_rosalloc_slow_path: + + SETUP_SAVE_REFS_ONLY_FRAME + la $t9, artAllocObjectFromCodeRosAlloc + jalr $t9 + move $a2, $s1 # Pass self as argument. + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER + +END art_quick_alloc_object_rosalloc -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 2a18d53aea..0861d2d73e 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1775,9 +1775,107 @@ END \name // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). +ENTRY art_quick_alloc_object_rosalloc + + # Fast path rosalloc allocation + # a0: type_idx + # a1: ArtMethod* + # s1: Thread::Current + # ----------------------------- + # t0: class + # t1: object size + # t2: rosalloc run + # t3: thread stack top offset + # a4: thread stack bottom offset + # v0: free list head + # + # a5, a6 : temps + + ld $t0, ART_METHOD_DEX_CACHE_TYPES_OFFSET_64($a1) # Load dex cache resolved types array. + + dsll $a5, $a0, COMPRESSED_REFERENCE_SIZE_SHIFT # Shift the value. + daddu $a5, $t0, $a5 # Compute the index. + lwu $t0, 0($a5) # Load class (t0). + beqzc $t0, .Lart_quick_alloc_object_rosalloc_slow_path + + li $a6, MIRROR_CLASS_STATUS_INITIALIZED + lwu $a5, MIRROR_CLASS_STATUS_OFFSET($t0) # Check class status. + bnec $a5, $a6, .Lart_quick_alloc_object_rosalloc_slow_path + + # Add a fake dependence from the following access flag and size loads to the status load. This + # is to prevent those loads from being reordered above the status load and reading wrong values. + xor $a5, $a5, $a5 + daddu $t0, $t0, $a5 + + lwu $a5, MIRROR_CLASS_ACCESS_FLAGS_OFFSET($t0) # Check if access flags has + li $a6, ACCESS_FLAGS_CLASS_IS_FINALIZABLE # kAccClassIsFinalizable. + and $a6, $a5, $a6 + bnezc $a6, .Lart_quick_alloc_object_rosalloc_slow_path + + ld $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) # Check if thread local allocation stack + ld $a4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1) # has any room left. + bgeuc $t3, $a4, .Lart_quick_alloc_object_rosalloc_slow_path + + lwu $t1, MIRROR_CLASS_OBJECT_SIZE_OFFSET($t0) # Load object size (t1). + li $a5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE # Check if size is for a thread local + # allocation. + bltuc $a5, $t1, .Lart_quick_alloc_object_rosalloc_slow_path + + # Compute the rosalloc bracket index from the size. Allign up the size by the rosalloc bracket + # quantum size and divide by the quantum size and subtract by 1. + daddiu $t1, $t1, -1 # Decrease obj size and shift right by + dsrl $t1, $t1, ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT # quantum. + + dsll $t2, $t1, POINTER_SIZE_SHIFT + daddu $t2, $t2, $s1 + ld $t2, THREAD_ROSALLOC_RUNS_OFFSET($t2) # Load rosalloc run (t2). + + # Load the free list head (v0). + # NOTE: this will be the return val. + ld $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + beqzc $v0, .Lart_quick_alloc_object_rosalloc_slow_path + + # Load the next pointer of the head and update the list head with the next pointer. + ld $a5, ROSALLOC_SLOT_NEXT_OFFSET($v0) + sd $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + + # Store the class pointer in the header. This also overwrites the first pointer. The offsets are + # asserted to match. + +#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET +#error "Class pointer needs to overwrite next pointer." +#endif + + POISON_HEAP_REF $t0 + sw $t0, MIRROR_OBJECT_CLASS_OFFSET($v0) + + # Push the new object onto the thread local allocation stack and increment the thread local + # allocation stack top. + sd $v0, 0($t3) + daddiu $t3, $t3, COMPRESSED_REFERENCE_SIZE + sd $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) + + # Decrement the size of the free list. + lw $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + addiu $a5, $a5, -1 + sw $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + + sync # Fence. + + jalr $zero, $ra + .cpreturn # Restore gp from t8 in branch delay slot. + +.Lart_quick_alloc_object_rosalloc_slow_path: + SETUP_SAVE_REFS_ONLY_FRAME + jal artAllocObjectFromCodeRosAlloc + move $a2 ,$s1 # Pass self as argument. + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER + +END art_quick_alloc_object_rosalloc + +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index abd9046174..db2fdcabea 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -15,13 +15,15 @@ */ .macro GENERATE_ALLOC_ENTRYPOINTS c_suffix, cxx_suffix +// Called by managed code to allocate an object. +TWO_ARG_DOWNCALL art_quick_alloc_object\c_suffix, artAllocObjectFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate an object of a resolved class. -ONE_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +TWO_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate an object of an initialized class. -ONE_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +TWO_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate an object when the caller doesn't know whether it has access // to the created type. -ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks\c_suffix, artAllocObjectFromCodeWithChecks\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check\c_suffix, artAllocObjectFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate an array. THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate an array of a resolve class. @@ -59,12 +61,14 @@ GENERATE_ALLOC_ENTRYPOINTS _region_tlab_instrumented, RegionTLABInstrumented // Generate the allocation entrypoints for each allocator. This is used as an alternative to // GNERATE_ALL_ALLOC_ENTRYPOINTS for selectively implementing allocation fast paths in // hand-written assembly. +#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(c_suffix, cxx_suffix) \ + TWO_ARG_DOWNCALL art_quick_alloc_object ## c_suffix, artAllocObjectFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(c_suffix, cxx_suffix) \ - ONE_ARG_DOWNCALL art_quick_alloc_object_resolved ## c_suffix, artAllocObjectFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER + TWO_ARG_DOWNCALL art_quick_alloc_object_resolved ## c_suffix, artAllocObjectFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(c_suffix, cxx_suffix) \ - ONE_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER + TWO_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \ - ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks ## c_suffix, artAllocObjectFromCodeWithChecks ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER + TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check ## c_suffix, artAllocObjectFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(c_suffix, cxx_suffix) \ THREE_ARG_DOWNCALL art_quick_alloc_array ## c_suffix, artAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(c_suffix, cxx_suffix) \ @@ -89,7 +93,8 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_REGION_TLAB_ALLOCATOR .macro GENERATE_ALLOC_ENTRYPOINTS_FOR_REGION_TLAB_ALLOCATOR // This is to be separately defined for each architecture to allow a hand-written assembly fast path. -// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) @@ -104,7 +109,8 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) .macro GENERATE_ALLOC_ENTRYPOINTS_FOR_TLAB_ALLOCATOR // This is to be separately defined for each architecture to allow a hand-written assembly fast path. -// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB) @@ -123,6 +129,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_TLAB_ALLOCATOR .endm .macro GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) @@ -135,6 +142,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented) @@ -148,7 +156,8 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMal GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented) // This is to be separately defined for each architecture to allow a hand-written assembly fast path. -// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc, RosAlloc) @@ -160,6 +169,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented) @@ -172,6 +182,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAl GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer) @@ -184,6 +195,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented) @@ -196,6 +208,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, B GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented) @@ -208,6 +221,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstr GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region) @@ -220,6 +234,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented) @@ -232,6 +247,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionI GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented) diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index ee65fa8ab0..9e385f839f 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -1062,8 +1062,12 @@ TEST_F(StubTest, AllocObject) { EXPECT_FALSE(self->IsExceptionPending()); { - size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 0u, 0U, - StubTest::GetEntrypoint(self, kQuickAllocObjectWithChecks), + // Use an arbitrary method from c to use as referrer + size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex().index_), // type_idx + // arbitrary + reinterpret_cast<size_t>(c->GetVirtualMethod(0, kRuntimePointerSize)), + 0U, + StubTest::GetEntrypoint(self, kQuickAllocObject), self); EXPECT_FALSE(self->IsExceptionPending()); @@ -1074,6 +1078,8 @@ TEST_F(StubTest, AllocObject) { } { + // We can use null in the second argument as we do not need a method here (not used in + // resolved/initialized cases) size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 0u, 0U, StubTest::GetEntrypoint(self, kQuickAllocObjectResolved), self); @@ -1086,6 +1092,8 @@ TEST_F(StubTest, AllocObject) { } { + // We can use null in the second argument as we do not need a method here (not used in + // resolved/initialized cases) size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 0u, 0U, StubTest::GetEntrypoint(self, kQuickAllocObjectInitialized), self); diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 62c29cf268..c6f4c0346f 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -956,42 +956,52 @@ END_MACRO // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc). -DEFINE_FUNCTION art_quick_alloc_object_resolved_rosalloc +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). +DEFINE_FUNCTION art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. - // eax: type/return value - // ecx, ebx, edx: free + // eax: uint32_t type_idx/return value, ecx: ArtMethod* + // ebx, edx: free + PUSH edi + movl ART_METHOD_DEX_CACHE_TYPES_OFFSET_32(%ecx), %edx // Load dex cache resolved types array + // Load the class (edx) + movl 0(%edx, %eax, COMPRESSED_REFERENCE_SIZE), %edx + testl %edx, %edx // Check null class + jz .Lart_quick_alloc_object_rosalloc_slow_path + movl %fs:THREAD_SELF_OFFSET, %ebx // ebx = thread // Check if the thread local allocation // stack has room - movl THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx), %ecx - cmpl THREAD_LOCAL_ALLOC_STACK_END_OFFSET(%ebx), %ecx - jae .Lart_quick_alloc_object_resolved_rosalloc_slow_path + movl THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx), %edi + cmpl THREAD_LOCAL_ALLOC_STACK_END_OFFSET(%ebx), %edi + jae .Lart_quick_alloc_object_rosalloc_slow_path - movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%eax), %ecx // Load the object size (ecx) + movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%edx), %edi // Load the object size (edi) // Check if the size is for a thread // local allocation. Also does the // finalizable and initialization check. - cmpl LITERAL(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), %ecx - ja .Lart_quick_alloc_object_resolved_rosalloc_slow_path - shrl LITERAL(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT), %ecx // Calculate the rosalloc bracket index + cmpl LITERAL(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), %edi + ja .Lart_quick_alloc_object_rosalloc_slow_path + shrl LITERAL(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT), %edi // Calculate the rosalloc bracket index // from object size. // Load thread local rosalloc run (ebx) // Subtract __SIZEOF_POINTER__ to subtract // one from edi as there is no 0 byte run // and the size is already aligned. - movl (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)(%ebx, %ecx, __SIZEOF_POINTER__), %ebx + movl (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)(%ebx, %edi, __SIZEOF_POINTER__), %ebx // Load free_list head (edi), // this will be the return value. - movl (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%ebx), %ecx - jecxz .Lart_quick_alloc_object_resolved_rosalloc_slow_path + movl (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%ebx), %edi + test %edi, %edi + jz .Lart_quick_alloc_object_rosalloc_slow_path // Point of no slow path. Won't go to - // the slow path from here on. + // the slow path from here on. Ok to + // clobber eax and ecx. + movl %edi, %eax // Load the next pointer of the head // and update head of free list with // next pointer - movl ROSALLOC_SLOT_NEXT_OFFSET(%ecx), %edx - movl %edx, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%ebx) + movl ROSALLOC_SLOT_NEXT_OFFSET(%eax), %edi + movl %edi, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%ebx) // Decrement size of free list by 1 decl (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)(%ebx) // Store the class pointer in the @@ -1001,104 +1011,141 @@ DEFINE_FUNCTION art_quick_alloc_object_resolved_rosalloc #if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET #error "Class pointer needs to overwrite next pointer." #endif - POISON_HEAP_REF eax - movl %eax, MIRROR_OBJECT_CLASS_OFFSET(%ecx) + POISON_HEAP_REF edx + movl %edx, MIRROR_OBJECT_CLASS_OFFSET(%eax) movl %fs:THREAD_SELF_OFFSET, %ebx // ebx = thread // Push the new object onto the thread // local allocation stack and // increment the thread local // allocation stack top. - movl THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx), %eax - movl %ecx, (%eax) - addl LITERAL(COMPRESSED_REFERENCE_SIZE), %eax - movl %eax, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx) + movl THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx), %edi + movl %eax, (%edi) + addl LITERAL(COMPRESSED_REFERENCE_SIZE), %edi + movl %edi, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx) // No fence needed for x86. - movl %ecx, %eax // Move object to return register + POP edi ret -.Lart_quick_alloc_object_resolved_rosalloc_slow_path: +.Lart_quick_alloc_object_rosalloc_slow_path: + POP edi SETUP_SAVE_REFS_ONLY_FRAME ebx, ebx // save ref containing registers for GC // Outgoing argument set up - subl LITERAL(8), %esp // alignment padding + PUSH eax // alignment padding pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) + PUSH ecx PUSH eax - call SYMBOL(artAllocObjectFromCodeResolvedRosAlloc) // cxx_name(arg0, Thread*) + call SYMBOL(artAllocObjectFromCodeRosAlloc) // cxx_name(arg0, arg1, Thread*) addl LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception -END_FUNCTION art_quick_alloc_object_resolved_rosalloc +END_FUNCTION art_quick_alloc_object_rosalloc -// The common fast path code for art_quick_alloc_object_resolved_tlab -// and art_quick_alloc_object_resolved_region_tlab. +// The common fast path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab. // -// EAX: type/return_value -MACRO1(ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH, slowPathLabel) +// EAX: type_idx/return_value, ECX: ArtMethod*, EDX: the class. +MACRO1(ALLOC_OBJECT_TLAB_FAST_PATH, slowPathLabel) + testl %edx, %edx // Check null class + jz VAR(slowPathLabel) movl %fs:THREAD_SELF_OFFSET, %ebx // ebx = thread movl THREAD_LOCAL_END_OFFSET(%ebx), %edi // Load thread_local_end. subl THREAD_LOCAL_POS_OFFSET(%ebx), %edi // Compute the remaining buffer size. - movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%eax), %ecx // Load the object size. - cmpl %edi, %ecx // Check if it fits. + movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%edx), %esi // Load the object size. + cmpl %edi, %esi // Check if it fits. ja VAR(slowPathLabel) - movl THREAD_LOCAL_POS_OFFSET(%ebx), %edx // Load thread_local_pos + movl THREAD_LOCAL_POS_OFFSET(%ebx), %eax // Load thread_local_pos // as allocated object. - addl %edx, %ecx // Add the object size. - movl %ecx, THREAD_LOCAL_POS_OFFSET(%ebx) // Update thread_local_pos. + addl %eax, %esi // Add the object size. + movl %esi, THREAD_LOCAL_POS_OFFSET(%ebx) // Update thread_local_pos. incl THREAD_LOCAL_OBJECTS_OFFSET(%ebx) // Increase thread_local_objects. // Store the class pointer in the header. // No fence needed for x86. - POISON_HEAP_REF eax - movl %eax, MIRROR_OBJECT_CLASS_OFFSET(%edx) - movl %edx, %eax + POISON_HEAP_REF edx + movl %edx, MIRROR_OBJECT_CLASS_OFFSET(%eax) POP edi + POP esi ret // Fast path succeeded. END_MACRO -// The common slow path code for art_quick_alloc_object_resolved_tlab -// and art_quick_alloc_object_resolved_region_tlab. -MACRO1(ALLOC_OBJECT_RESOLVED_TLAB_SLOW_PATH, cxx_name) +// The common slow path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab. +MACRO1(ALLOC_OBJECT_TLAB_SLOW_PATH, cxx_name) POP edi + POP esi SETUP_SAVE_REFS_ONLY_FRAME ebx, ebx // save ref containing registers for GC // Outgoing argument set up - subl LITERAL(8), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(8) + PUSH eax // alignment padding pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) + PUSH ecx PUSH eax - call CALLVAR(cxx_name) // cxx_name(arg0, Thread*) + call CALLVAR(cxx_name) // cxx_name(arg0, arg1, Thread*) addl LITERAL(16), %esp CFI_ADJUST_CFA_OFFSET(-16) RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_MACRO -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB). May be called +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). May be called // for CC if the GC is not marking. -DEFINE_FUNCTION art_quick_alloc_object_resolved_tlab +DEFINE_FUNCTION art_quick_alloc_object_tlab // Fast path tlab allocation. - // EAX: type - // EBX, ECX, EDX: free. + // EAX: uint32_t type_idx/return value, ECX: ArtMethod*. + // EBX, EDX: free. + PUSH esi PUSH edi - ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_tlab_slow_path -.Lart_quick_alloc_object_resolved_tlab_slow_path: - ALLOC_OBJECT_RESOLVED_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedTLAB -END_FUNCTION art_quick_alloc_object_resolved_tlab - -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB). -DEFINE_FUNCTION art_quick_alloc_object_resolved_region_tlab + movl ART_METHOD_DEX_CACHE_TYPES_OFFSET_32(%ecx), %edx // Load dex cache resolved types array + // Might need to break down into multiple instructions to get the base address in a register. + // Load the class + movl 0(%edx, %eax, COMPRESSED_REFERENCE_SIZE), %edx + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path +.Lart_quick_alloc_object_tlab_slow_path: + ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeTLAB +END_FUNCTION art_quick_alloc_object_tlab + +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB). +DEFINE_FUNCTION art_quick_alloc_object_region_tlab // Fast path region tlab allocation. - // EAX: type/return value - // EBX, ECX, EDX: free. + // EAX: uint32_t type_idx/return value, ECX: ArtMethod*. + // EBX, EDX: free. #if !defined(USE_READ_BARRIER) int3 int3 #endif + PUSH esi PUSH edi - ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_region_tlab_slow_path -.Lart_quick_alloc_object_resolved_region_tlab_slow_path: - ALLOC_OBJECT_RESOLVED_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedRegionTLAB -END_FUNCTION art_quick_alloc_object_resolved_region_tlab - + movl ART_METHOD_DEX_CACHE_TYPES_OFFSET_32(%ecx), %edx // Load dex cache resolved types array + // Might need to break down into multiple instructions to get the base address in a register. + // Load the class + movl 0(%edx, %eax, COMPRESSED_REFERENCE_SIZE), %edx + // Read barrier for class load. + cmpl LITERAL(0), %fs:THREAD_IS_GC_MARKING_OFFSET + jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit + // Null check so that we can load the lock word. + testl %edx, %edx + jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit + // Check the mark bit, if it is 1 return. + testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edx) + jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit: + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path: + // The read barrier slow path. Mark the class. + PUSH eax + PUSH ecx + // Outgoing argument set up + subl MACRO_LITERAL(8), %esp // Alignment padding + CFI_ADJUST_CFA_OFFSET(8) + PUSH edx // Pass the class as the first param. + call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj) + movl %eax, %edx + addl MACRO_LITERAL(12), %esp + CFI_ADJUST_CFA_OFFSET(-12) + POP ecx + POP eax + jmp .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit +.Lart_quick_alloc_object_region_tlab_slow_path: + ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeRegionTLAB +END_FUNCTION art_quick_alloc_object_region_tlab DEFINE_FUNCTION art_quick_resolve_string SETUP_SAVE_EVERYTHING_FRAME ebx, ebx diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index facd563428..4c46b08a9e 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -983,6 +983,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // Comment out allocators that have x86_64 specific asm. // Region TLAB: +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) @@ -995,9 +996,11 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // Normal TLAB: +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) @@ -1006,25 +1009,29 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) - -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc). -DEFINE_FUNCTION art_quick_alloc_object_resolved_rosalloc +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). +DEFINE_FUNCTION art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. - // RDI: mirror::Class*, RAX: return value - // RSI, RDX, RCX, R8, R9: free. + // RDI: type_idx, RSI: ArtMethod*, RAX: return value + // RDX, RCX, R8, R9: free. + movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array + // Load the class (edx) + movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx + testl %edx, %edx // Check null class + jz .Lart_quick_alloc_object_rosalloc_slow_path // Check if the thread local // allocation stack has room. movq %gs:THREAD_SELF_OFFSET, %r8 // r8 = thread movq THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%r8), %rcx // rcx = alloc stack top. cmpq THREAD_LOCAL_ALLOC_STACK_END_OFFSET(%r8), %rcx - jae .Lart_quick_alloc_object_resolved_rosalloc_slow_path + jae .Lart_quick_alloc_object_rosalloc_slow_path // Load the object size - movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdi), %eax + movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdx), %eax // Check if the size is for a thread // local allocation. Also does the // initialized and finalizable checks. cmpl LITERAL(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), %eax - ja .Lart_quick_alloc_object_resolved_rosalloc_slow_path + ja .Lart_quick_alloc_object_rosalloc_slow_path // Compute the rosalloc bracket index // from the size. shrq LITERAL(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT), %rax @@ -1038,7 +1045,7 @@ DEFINE_FUNCTION art_quick_alloc_object_resolved_rosalloc // will be the return val. movq (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%r9), %rax testq %rax, %rax - jz .Lart_quick_alloc_object_resolved_rosalloc_slow_path + jz .Lart_quick_alloc_object_rosalloc_slow_path // "Point of no slow path". Won't go to the slow path from here on. OK to clobber rdi and rsi. // Push the new object onto the thread // local allocation stack and @@ -1059,17 +1066,17 @@ DEFINE_FUNCTION art_quick_alloc_object_resolved_rosalloc #if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET #error "Class pointer needs to overwrite next pointer." #endif - POISON_HEAP_REF edi - movl %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax) + POISON_HEAP_REF edx + movl %edx, MIRROR_OBJECT_CLASS_OFFSET(%rax) // Decrement the size of the free list decl (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)(%r9) // No fence necessary for x86. ret -.Lart_quick_alloc_object_resolved_rosalloc_slow_path: +.Lart_quick_alloc_object_rosalloc_slow_path: SETUP_SAVE_REFS_ONLY_FRAME // save ref containing registers for GC // Outgoing argument set up - movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() - call SYMBOL(artAllocObjectFromCodeResolvedRosAlloc) // cxx_name(arg0, Thread*) + movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() + call SYMBOL(artAllocObjectFromCodeRosAlloc) // cxx_name(arg0, arg1, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_FUNCTION art_quick_alloc_object_rosalloc @@ -1088,19 +1095,19 @@ END_MACRO // TODO: delete ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH since it is the same as // ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH. // -// RDI: the class, RAX: return value. -// RCX, RSI, RDX: scratch, r8: Thread::Current(). +// RDI: type_idx, RSI: ArtMethod*, RDX/EDX: the class, RAX: return value. +// RCX: scratch, r8: Thread::Current(). MACRO1(ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH, slowPathLabel) ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH(RAW_VAR(slowPathLabel)) END_MACRO // The fast path code for art_quick_alloc_object_initialized_region_tlab. // -// RDI: the class, RSI: ArtMethod*, RAX: return value. -// RCX, RSI, RDX: scratch, r8: Thread::Current(). +// RDI: type_idx, RSI: ArtMethod*, RDX/EDX: the class, RAX: return value. +// RCX: scratch, r8: Thread::Current(). MACRO1(ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH, slowPathLabel) movq %gs:THREAD_SELF_OFFSET, %r8 // r8 = thread - movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdi), %ecx // Load the object size. + movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdx), %ecx // Load the object size. movq THREAD_LOCAL_POS_OFFSET(%r8), %rax addq %rax, %rcx // Add size to pos, note that these // are both 32 bit ints, overflow @@ -1113,8 +1120,8 @@ MACRO1(ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH, slowPathLabel) // Store the class pointer in the // header. // No fence needed for x86. - POISON_HEAP_REF edi - movl %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax) + POISON_HEAP_REF edx + movl %edx, MIRROR_OBJECT_CLASS_OFFSET(%rax) ret // Fast path succeeded. END_MACRO @@ -1157,14 +1164,12 @@ MACRO1(ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, slowPathLabel) ret // Fast path succeeded. END_MACRO - -// The common slow path code for art_quick_alloc_object_{resolved, initialized}_tlab -// and art_quick_alloc_object_{resolved, initialized}_region_tlab. +// The common slow path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab. MACRO1(ALLOC_OBJECT_TLAB_SLOW_PATH, cxx_name) SETUP_SAVE_REFS_ONLY_FRAME // save ref containing registers for GC // Outgoing argument set up - movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() - call CALLVAR(cxx_name) // cxx_name(arg0, Thread*) + movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() + call CALLVAR(cxx_name) // cxx_name(arg0, arg1, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_MACRO @@ -1179,11 +1184,26 @@ MACRO1(ALLOC_ARRAY_TLAB_SLOW_PATH, cxx_name) RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_MACRO +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). May be +// called with CC if the GC is not active. +DEFINE_FUNCTION art_quick_alloc_object_tlab + // RDI: uint32_t type_idx, RSI: ArtMethod* + // RDX, RCX, R8, R9: free. RAX: return val. + movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array + // Might need to break down into multiple instructions to get the base address in a register. + // Load the class + movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path +.Lart_quick_alloc_object_tlab_slow_path: + ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeTLAB +END_FUNCTION art_quick_alloc_object_tlab + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB). May be // called with CC if the GC is not active. DEFINE_FUNCTION art_quick_alloc_object_resolved_tlab - // RDI: mirror::Class* klass - // RDX, RSI, RCX, R8, R9: free. RAX: return val. + // RDI: mirror::Class* klass, RSI: ArtMethod* + // RDX, RCX, R8, R9: free. RAX: return val. + movq %rdi, %rdx ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_tlab_slow_path .Lart_quick_alloc_object_resolved_tlab_slow_path: ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedTLAB @@ -1192,8 +1212,9 @@ END_FUNCTION art_quick_alloc_object_resolved_tlab // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB). // May be called with CC if the GC is not active. DEFINE_FUNCTION art_quick_alloc_object_initialized_tlab - // RDI: mirror::Class* klass - // RDX, RSI, RCX, R8, R9: free. RAX: return val. + // RDI: mirror::Class* klass, RSI: ArtMethod* + // RDX, RCX, R8, R9: free. RAX: return val. + movq %rdi, %rdx ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH .Lart_quick_alloc_object_initialized_tlab_slow_path .Lart_quick_alloc_object_initialized_tlab_slow_path: ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedTLAB @@ -1271,12 +1292,49 @@ DEFINE_FUNCTION art_quick_alloc_array_resolved_region_tlab ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedRegionTLAB END_FUNCTION art_quick_alloc_array_resolved_region_tlab +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB). +DEFINE_FUNCTION art_quick_alloc_object_region_tlab + // Fast path region tlab allocation. + // RDI: uint32_t type_idx, RSI: ArtMethod* + // RDX, RCX, R8, R9: free. RAX: return val. + ASSERT_USE_READ_BARRIER + movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array + movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx // Load the class + // Null check so that we can load the lock word. + testl %edx, %edx + jz .Lart_quick_alloc_object_region_tlab_slow_path + // Since we have allocation entrypoint switching, we know the GC is marking. + // Check the mark bit, if it is 0, do the read barrier mark. + testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edx) + jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit: + // Use resolved one since we already did the null check. + ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path: + // The read barrier slow path. Mark the class. + PUSH rdi + PUSH rsi + subq LITERAL(8), %rsp // 16 byte alignment + // Outgoing argument set up + movq %rdx, %rdi // Pass the class as the first param. + call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj) + movq %rax, %rdx + addq LITERAL(8), %rsp + POP rsi + POP rdi + jmp .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit +.Lart_quick_alloc_object_region_tlab_slow_path: + ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeRegionTLAB +END_FUNCTION art_quick_alloc_object_region_tlab + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB). DEFINE_FUNCTION art_quick_alloc_object_resolved_region_tlab // Fast path region tlab allocation. - // RDI: mirror::Class* klass - // RDX, RSI, RCX, R8, R9: free. RAX: return val. + // RDI: mirror::Class* klass, RSI: ArtMethod* + // RDX, RCX, R8, R9: free. RAX: return val. ASSERT_USE_READ_BARRIER + // No read barrier since the caller is responsible for that. + movq %rdi, %rdx ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_region_tlab_slow_path .Lart_quick_alloc_object_resolved_region_tlab_slow_path: ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedRegionTLAB @@ -1285,9 +1343,10 @@ END_FUNCTION art_quick_alloc_object_resolved_region_tlab // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB). DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab // Fast path region tlab allocation. - // RDI: mirror::Class* klass - // RDX, RSI, RCX, R8, R9: free. RAX: return val. + // RDI: mirror::Class* klass, RSI: ArtMethod* + // RDX, RCX, R8, R9: free. RAX: return val. ASSERT_USE_READ_BARRIER + movq %rdi, %rdx // No read barrier since the caller is responsible for that. ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH .Lart_quick_alloc_object_initialized_region_tlab_slow_path .Lart_quick_alloc_object_initialized_region_tlab_slow_path: diff --git a/runtime/art_method.h b/runtime/art_method.h index b38508b757..11dcc35df5 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -95,18 +95,20 @@ class ArtMethod FINAL { // This setter guarantees atomicity. void AddAccessFlags(uint32_t flag) { - uint32_t old_access_flags = access_flags_.load(std::memory_order_relaxed); + uint32_t old_access_flags; uint32_t new_access_flags; do { + old_access_flags = access_flags_.load(std::memory_order_relaxed); new_access_flags = old_access_flags | flag; } while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags)); } // This setter guarantees atomicity. void ClearAccessFlags(uint32_t flag) { - uint32_t old_access_flags = access_flags_.load(std::memory_order_relaxed); + uint32_t old_access_flags; uint32_t new_access_flags; do { + old_access_flags = access_flags_.load(std::memory_order_relaxed); new_access_flags = old_access_flags & ~flag; } while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags)); } diff --git a/runtime/asm_support.h b/runtime/asm_support.h index bfdddf7b03..e4972da13d 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -98,7 +98,7 @@ ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET, art::Thread::ThreadLocalEndOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_objects. -#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__) +#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + 2 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, art::Thread::ThreadLocalObjectsOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_current_ibase. diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index 1dca4286da..55b4306427 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -26,7 +26,7 @@ // Headers for LogMessage::LogLine. #ifdef ART_TARGET_ANDROID -#include <android/log.h> +#include <log/log.h> #else #include <sys/types.h> #include <unistd.h> diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 469c45c10c..14c9c21356 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -127,21 +127,43 @@ inline ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveTyp self->GetManagedStack()->GetTopQuickFrame(), type, true /* do_caller_check */); } -ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(mirror::Class* klass, - Thread* self, - bool* slow_path) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_) { - if (UNLIKELY(!klass->IsInstantiable())) { - self->ThrowNewException("Ljava/lang/InstantiationError;", klass->PrettyDescriptor().c_str()); +template <const bool kAccessCheck> +ALWAYS_INLINE +inline mirror::Class* CheckObjectAlloc(dex::TypeIndex type_idx, + ArtMethod* method, + Thread* self, + bool* slow_path) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + PointerSize pointer_size = class_linker->GetImagePointerSize(); + mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size); + if (UNLIKELY(klass == nullptr)) { + klass = class_linker->ResolveType(type_idx, method); *slow_path = true; - return nullptr; // Failure + if (klass == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; // Failure + } else { + DCHECK(!self->IsExceptionPending()); + } } - if (UNLIKELY(klass->IsClassClass())) { - ThrowIllegalAccessError(nullptr, "Class %s is inaccessible", - klass->PrettyDescriptor().c_str()); - *slow_path = true; - return nullptr; // Failure + if (kAccessCheck) { + if (UNLIKELY(!klass->IsInstantiable())) { + self->ThrowNewException("Ljava/lang/InstantiationError;", klass->PrettyDescriptor().c_str()); + *slow_path = true; + return nullptr; // Failure + } + if (UNLIKELY(klass->IsClassClass())) { + ThrowIllegalAccessError(nullptr, "Class %s is inaccessible", + klass->PrettyDescriptor().c_str()); + *slow_path = true; + return nullptr; // Failure + } + mirror::Class* referrer = method->GetDeclaringClass(); + if (UNLIKELY(!referrer->CanAccess(klass))) { + ThrowIllegalAccessErrorClass(referrer, klass); + *slow_path = true; + return nullptr; // Failure + } } if (UNLIKELY(!klass->IsInitialized())) { StackHandleScope<1> hs(self); @@ -169,9 +191,7 @@ ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(mirror::Class* klass, ALWAYS_INLINE inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass, Thread* self, - bool* slow_path) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_) { + bool* slow_path) { if (UNLIKELY(!klass->IsInitialized())) { StackHandleScope<1> hs(self); Handle<mirror::Class> h_class(hs.NewHandle(klass)); @@ -193,15 +213,18 @@ inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass, return klass; } -// Allocate an instance of klass. Throws InstantationError if klass is not instantiable, -// or IllegalAccessError if klass is j.l.Class. Performs a clinit check too. -template <bool kInstrumented> +// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it +// cannot be resolved, throw an error. If it can, use it to create an instance. +// When verification/compiler hasn't been able to verify access, optionally perform an access +// check. +template <bool kAccessCheck, bool kInstrumented> ALWAYS_INLINE -inline mirror::Object* AllocObjectFromCode(mirror::Class* klass, +inline mirror::Object* AllocObjectFromCode(dex::TypeIndex type_idx, + ArtMethod* method, Thread* self, gc::AllocatorType allocator_type) { bool slow_path = false; - klass = CheckObjectAlloc(klass, self, &slow_path); + mirror::Class* klass = CheckObjectAlloc<kAccessCheck>(type_idx, method, self, &slow_path); if (UNLIKELY(slow_path)) { if (klass == nullptr) { return nullptr; diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 4794610ca8..7cc136e227 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -45,10 +45,27 @@ class OatQuickMethodHeader; class ScopedObjectAccessAlreadyRunnable; class Thread; +template <const bool kAccessCheck> +ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(dex::TypeIndex type_idx, + ArtMethod* method, + Thread* self, + bool* slow_path) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + +ALWAYS_INLINE inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass, + Thread* self, + bool* slow_path) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + // Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it // cannot be resolved, throw an error. If it can, use it to create an instance. -template <bool kInstrumented> -ALWAYS_INLINE inline mirror::Object* AllocObjectFromCode(mirror::Class* klass, +// When verification/compiler hasn't been able to verify access, optionally perform an access +// check. +template <bool kAccessCheck, bool kInstrumented> +ALWAYS_INLINE inline mirror::Object* AllocObjectFromCode(dex::TypeIndex type_idx, + ArtMethod* method, Thread* self, gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 2d06508069..82bb8e53c6 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -29,58 +29,87 @@ namespace art { static constexpr bool kUseTlabFastPath = true; -template <bool kInitialized, - bool kFinalize, - bool kInstrumented, - gc::AllocatorType allocator_type> -static ALWAYS_INLINE inline mirror::Object* artAllocObjectFromCode( - mirror::Class* klass, - Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - ScopedQuickEntrypointChecks sqec(self); - DCHECK(klass != nullptr); - if (kUseTlabFastPath && !kInstrumented && allocator_type == gc::kAllocatorTypeTLAB) { - if (kInitialized || klass->IsInitialized()) { - if (!kFinalize || !klass->IsFinalizable()) { - size_t byte_count = klass->GetObjectSize(); - byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); - mirror::Object* obj; - if (LIKELY(byte_count < self->TlabSize())) { - obj = self->AllocTlab(byte_count); - DCHECK(obj != nullptr) << "AllocTlab can't fail"; - obj->SetClass(klass); - if (kUseBakerReadBarrier) { - obj->AssertReadBarrierState(); - } - QuasiAtomic::ThreadFenceForConstructor(); - return obj; - } - } - } - } - if (kInitialized) { - return AllocObjectFromCodeInitialized<kInstrumented>(klass, self, allocator_type); - } else if (!kFinalize) { - return AllocObjectFromCodeResolved<kInstrumented>(klass, self, allocator_type); - } else { - return AllocObjectFromCode<kInstrumented>(klass, self, allocator_type); - } -} - #define GENERATE_ENTRYPOINTS_FOR_ALLOCATOR_INST(suffix, suffix2, instrumented_bool, allocator_type) \ -extern "C" mirror::Object* artAllocObjectFromCodeWithChecks##suffix##suffix2( \ - mirror::Class* klass, Thread* self) \ +extern "C" mirror::Object* artAllocObjectFromCode ##suffix##suffix2( \ + uint32_t type_idx, ArtMethod* method, Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ - return artAllocObjectFromCode<false, true, instrumented_bool, allocator_type>(klass, self); \ + ScopedQuickEntrypointChecks sqec(self); \ + if (kUseTlabFastPath && !(instrumented_bool) && (allocator_type) == gc::kAllocatorTypeTLAB) { \ + mirror::Class* klass = method->GetDexCacheResolvedType<false>(dex::TypeIndex(type_idx), \ + kRuntimePointerSize); \ + if (LIKELY(klass != nullptr && klass->IsInitialized() && !klass->IsFinalizable())) { \ + size_t byte_count = klass->GetObjectSize(); \ + byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \ + mirror::Object* obj; \ + if (LIKELY(byte_count < self->TlabSize())) { \ + obj = self->AllocTlab(byte_count); \ + DCHECK(obj != nullptr) << "AllocTlab can't fail"; \ + obj->SetClass(klass); \ + if (kUseBakerReadBarrier) { \ + obj->AssertReadBarrierState(); \ + } \ + QuasiAtomic::ThreadFenceForConstructor(); \ + return obj; \ + } \ + } \ + } \ + return AllocObjectFromCode<false, instrumented_bool>(dex::TypeIndex(type_idx), \ + method, \ + self, \ + allocator_type); \ } \ extern "C" mirror::Object* artAllocObjectFromCodeResolved##suffix##suffix2( \ - mirror::Class* klass, Thread* self) \ + mirror::Class* klass, ArtMethod* method ATTRIBUTE_UNUSED, Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ - return artAllocObjectFromCode<false, false, instrumented_bool, allocator_type>(klass, self); \ + ScopedQuickEntrypointChecks sqec(self); \ + if (kUseTlabFastPath && !(instrumented_bool) && (allocator_type) == gc::kAllocatorTypeTLAB) { \ + if (LIKELY(klass->IsInitialized())) { \ + size_t byte_count = klass->GetObjectSize(); \ + byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \ + mirror::Object* obj; \ + if (LIKELY(byte_count < self->TlabSize())) { \ + obj = self->AllocTlab(byte_count); \ + DCHECK(obj != nullptr) << "AllocTlab can't fail"; \ + obj->SetClass(klass); \ + if (kUseBakerReadBarrier) { \ + obj->AssertReadBarrierState(); \ + } \ + QuasiAtomic::ThreadFenceForConstructor(); \ + return obj; \ + } \ + } \ + } \ + return AllocObjectFromCodeResolved<instrumented_bool>(klass, self, allocator_type); \ } \ extern "C" mirror::Object* artAllocObjectFromCodeInitialized##suffix##suffix2( \ - mirror::Class* klass, Thread* self) \ + mirror::Class* klass, ArtMethod* method ATTRIBUTE_UNUSED, Thread* self) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + ScopedQuickEntrypointChecks sqec(self); \ + if (kUseTlabFastPath && !(instrumented_bool) && (allocator_type) == gc::kAllocatorTypeTLAB) { \ + size_t byte_count = klass->GetObjectSize(); \ + byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \ + mirror::Object* obj; \ + if (LIKELY(byte_count < self->TlabSize())) { \ + obj = self->AllocTlab(byte_count); \ + DCHECK(obj != nullptr) << "AllocTlab can't fail"; \ + obj->SetClass(klass); \ + if (kUseBakerReadBarrier) { \ + obj->AssertReadBarrierState(); \ + } \ + QuasiAtomic::ThreadFenceForConstructor(); \ + return obj; \ + } \ + } \ + return AllocObjectFromCodeInitialized<instrumented_bool>(klass, self, allocator_type); \ +} \ +extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheck##suffix##suffix2( \ + uint32_t type_idx, ArtMethod* method, Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ - return artAllocObjectFromCode<true, false, instrumented_bool, allocator_type>(klass, self); \ + ScopedQuickEntrypointChecks sqec(self); \ + return AllocObjectFromCode<true, instrumented_bool>(dex::TypeIndex(type_idx), \ + method, \ + self, \ + allocator_type); \ } \ extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \ uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ @@ -191,9 +220,10 @@ GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(RegionTLAB, gc::kAllocatorTypeRegionTLAB) extern "C" void* art_quick_alloc_array##suffix(uint32_t, int32_t, ArtMethod* ref); \ extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t, ArtMethod* ref); \ extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, int32_t, ArtMethod* ref); \ -extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass); \ -extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass); \ -extern "C" void* art_quick_alloc_object_with_checks##suffix(mirror::Class* klass); \ +extern "C" void* art_quick_alloc_object##suffix(uint32_t type_idx, ArtMethod* ref); \ +extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass, ArtMethod* ref); \ +extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass, ArtMethod* ref); \ +extern "C" void* art_quick_alloc_object_with_access_check##suffix(uint32_t type_idx, ArtMethod* ref); \ extern "C" void* art_quick_check_and_alloc_array##suffix(uint32_t, int32_t, ArtMethod* ref); \ extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix(uint32_t, int32_t, ArtMethod* ref); \ extern "C" void* art_quick_alloc_string_from_bytes##suffix(void*, int32_t, int32_t, int32_t); \ @@ -203,9 +233,9 @@ extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, int32_t, extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t, ArtMethod* ref); \ extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \ extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, ArtMethod* ref); \ -extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass); \ -extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(mirror::Class* klass); \ -extern "C" void* art_quick_alloc_object_with_checks##suffix##_instrumented(mirror::Class* klass); \ +extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass, ArtMethod* ref); \ +extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(mirror::Class* klass, ArtMethod* ref); \ +extern "C" void* art_quick_alloc_object_with_access_check##suffix##_instrumented(uint32_t type_idx, ArtMethod* ref); \ extern "C" void* art_quick_check_and_alloc_array##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \ extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \ extern "C" void* art_quick_alloc_string_from_bytes##suffix##_instrumented(void*, int32_t, int32_t, int32_t); \ @@ -216,9 +246,10 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument qpoints->pAllocArray = art_quick_alloc_array##suffix##_instrumented; \ qpoints->pAllocArrayResolved = art_quick_alloc_array_resolved##suffix##_instrumented; \ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check##suffix##_instrumented; \ + qpoints->pAllocObject = art_quick_alloc_object##suffix##_instrumented; \ qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \ - qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \ + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check##suffix##_instrumented; \ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \ @@ -228,9 +259,10 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument qpoints->pAllocArray = art_quick_alloc_array##suffix; \ qpoints->pAllocArrayResolved = art_quick_alloc_array_resolved##suffix; \ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check##suffix; \ + qpoints->pAllocObject = art_quick_alloc_object##suffix; \ qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \ - qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \ + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check##suffix; \ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \ diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 0911aeb0f4..a1c5082c93 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -23,9 +23,10 @@ V(AllocArray, void*, uint32_t, int32_t, ArtMethod*) \ V(AllocArrayResolved, void*, mirror::Class*, int32_t, ArtMethod*) \ V(AllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \ - V(AllocObjectResolved, void*, mirror::Class*) \ - V(AllocObjectInitialized, void*, mirror::Class*) \ - V(AllocObjectWithChecks, void*, mirror::Class*) \ + V(AllocObject, void*, uint32_t, ArtMethod*) \ + V(AllocObjectResolved, void*, mirror::Class*, ArtMethod*) \ + V(AllocObjectInitialized, void*, mirror::Class*, ArtMethod*) \ + V(AllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*) \ V(CheckAndAllocArray, void*, uint32_t, int32_t, ArtMethod*) \ V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \ V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \ diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index bf1d4ea1a1..a3e5b552b5 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -705,7 +705,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, QuickExceptionHandler::DumpFramesWithType(self, true); } - mirror::Throwable* pending_exception = nullptr; + ObjPtr<mirror::Throwable> pending_exception; bool from_code = false; self->PopDeoptimizationContext(&result, &pending_exception, /* out */ &from_code); diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 6866abb6ae..12836602d5 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -122,9 +122,9 @@ class EntrypointsOrderTest : public CommonRuntimeTest { // Skip across the entrypoints structures. - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_start, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_objects, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(size_t)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*)); @@ -156,13 +156,13 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArray, pAllocArrayResolved, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayResolved, pAllocArrayWithAccessCheck, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayWithAccessCheck, pAllocObjectResolved, - sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayWithAccessCheck, pAllocObject, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObject, pAllocObjectResolved, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectResolved, pAllocObjectInitialized, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks, + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithAccessCheck, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pCheckAndAllocArray, + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithAccessCheck, pCheckAndAllocArray, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck, sizeof(void*)); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index d7dfcd4408..b0d7fb247a 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -508,8 +508,9 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); obj = mirror::String::AllocEmptyString<true>(self, allocator_type); } else { - obj = AllocObjectFromCode<true>( - c.Ptr(), + obj = AllocObjectFromCode<do_access_check, true>( + dex::TypeIndex(inst->VRegB_21c()), + shadow_frame.GetMethod(), self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); } diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 369c2614a7..c8c1563ff6 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -375,9 +375,10 @@ extern "C" size_t MterpNewInstance(ShadowFrame* shadow_frame, Thread* self, uint gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); obj = mirror::String::AllocEmptyString<true>(self, allocator_type); } else { - obj = AllocObjectFromCode<true>(c, - self, - Runtime::Current()->GetHeap()->GetCurrentAllocator()); + obj = AllocObjectFromCode<false, true>(dex::TypeIndex(inst->VRegB_21c()), + shadow_frame->GetMethod(), + self, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); } } if (UNLIKELY(obj == nullptr)) { diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc index d8869ad677..b13d565ec2 100644 --- a/runtime/jdwp/jdwp_adb.cc +++ b/runtime/jdwp/jdwp_adb.cc @@ -47,8 +47,16 @@ * JDWP-handshake, etc... */ -#define kJdwpControlName "\0jdwp-control" -#define kJdwpControlNameLen (sizeof(kJdwpControlName)-1) +static constexpr char kJdwpControlName[] = "\0jdwp-control"; +static constexpr size_t kJdwpControlNameLen = sizeof(kJdwpControlName) - 1; +/* This timeout is for connect/send with control socket. In practice, the + * connect should never timeout since it's just connect to a local unix domain + * socket. But in case adb is buggy and doesn't respond to any connection, the + * connect will block. For send, actually it would never block since we only send + * several bytes and the kernel buffer is big enough to accept it. 10 seconds + * should be far enough. + */ +static constexpr int kControlSockSendTimeout = 10; namespace art { @@ -224,6 +232,10 @@ bool JdwpAdbState::Accept() { PLOG(ERROR) << "Could not create ADB control socket"; return false; } + struct timeval timeout; + timeout.tv_sec = kControlSockSendTimeout; + timeout.tv_usec = 0; + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); { MutexLock mu(Thread::Current(), state_lock_); control_sock_ = sock; diff --git a/runtime/oat.h b/runtime/oat.h index dc103e2b52..1fd906dc1b 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', '9', '5', '\0' }; // alloc entrypoints change + static constexpr uint8_t kOatVersion[] = { '0', '9', '4', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index be06dd7b4c..b757b2114f 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -24,6 +24,7 @@ cc_defaults { "ti_field.cc", "ti_heap.cc", "ti_method.cc", + "ti_monitor.cc", "ti_object.cc", "ti_properties.cc", "ti_stack.cc", diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 936049fe3d..c52dd76b59 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -50,6 +50,7 @@ #include "ti_field.h" #include "ti_heap.h" #include "ti_method.h" +#include "ti_monitor.h" #include "ti_object.h" #include "ti_properties.h" #include "ti_redefine.h" @@ -748,31 +749,31 @@ class JvmtiFunctions { } static jvmtiError CreateRawMonitor(jvmtiEnv* env, const char* name, jrawMonitorID* monitor_ptr) { - return ERR(NOT_IMPLEMENTED); + return MonitorUtil::CreateRawMonitor(env, name, monitor_ptr); } static jvmtiError DestroyRawMonitor(jvmtiEnv* env, jrawMonitorID monitor) { - return ERR(NOT_IMPLEMENTED); + return MonitorUtil::DestroyRawMonitor(env, monitor); } static jvmtiError RawMonitorEnter(jvmtiEnv* env, jrawMonitorID monitor) { - return ERR(NOT_IMPLEMENTED); + return MonitorUtil::RawMonitorEnter(env, monitor); } static jvmtiError RawMonitorExit(jvmtiEnv* env, jrawMonitorID monitor) { - return ERR(NOT_IMPLEMENTED); + return MonitorUtil::RawMonitorExit(env, monitor); } static jvmtiError RawMonitorWait(jvmtiEnv* env, jrawMonitorID monitor, jlong millis) { - return ERR(NOT_IMPLEMENTED); + return MonitorUtil::RawMonitorWait(env, monitor, millis); } static jvmtiError RawMonitorNotify(jvmtiEnv* env, jrawMonitorID monitor) { - return ERR(NOT_IMPLEMENTED); + return MonitorUtil::RawMonitorNotify(env, monitor); } static jvmtiError RawMonitorNotifyAll(jvmtiEnv* env, jrawMonitorID monitor) { - return ERR(NOT_IMPLEMENTED); + return MonitorUtil::RawMonitorNotifyAll(env, monitor); } static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) { diff --git a/runtime/openjdkjvmti/ti_monitor.cc b/runtime/openjdkjvmti/ti_monitor.cc new file mode 100644 index 0000000000..b82768397b --- /dev/null +++ b/runtime/openjdkjvmti/ti_monitor.cc @@ -0,0 +1,302 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_monitor.h" + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <mutex> + +#include "art_jvmti.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +// We cannot use ART monitors, as they require the mutator lock for contention locking. We +// also cannot use pthread mutexes and condition variables (or C++11 abstractions) directly, +// as the do not have the right semantics for recursive mutexes and waiting (wait only unlocks +// the mutex once). +// So go ahead and use a wrapper that does the counting explicitly. + +class JvmtiMonitor { + public: + JvmtiMonitor() : owner_(nullptr), count_(0) { + } + + static bool Destroy(art::Thread* self, JvmtiMonitor* monitor) { + // Check whether this thread holds the monitor, or nobody does. + art::Thread* owner_thread = monitor->owner_.load(std::memory_order_relaxed); + if (owner_thread != nullptr && self != owner_thread) { + return false; + } + + if (monitor->count_ > 0) { + monitor->count_ = 0; + monitor->owner_.store(nullptr, std::memory_order_relaxed); + monitor->mutex_.unlock(); + } + + delete monitor; + return true; + } + + void MonitorEnter(art::Thread* self) { + // Check for recursive enter. + if (IsOwner(self)) { + count_++; + return; + } + + mutex_.lock(); + + DCHECK(owner_.load(std::memory_order_relaxed) == nullptr); + owner_.store(self, std::memory_order_relaxed); + DCHECK_EQ(0u, count_); + count_ = 1; + } + + bool MonitorExit(art::Thread* self) { + if (!IsOwner(self)) { + return false; + } + + --count_; + if (count_ == 0u) { + owner_.store(nullptr, std::memory_order_relaxed); + mutex_.unlock(); + } + + return true; + } + + bool Wait(art::Thread* self) { + auto wait_without_timeout = [&](std::unique_lock<std::mutex>& lk) { + cond_.wait(lk); + }; + return Wait(self, wait_without_timeout); + } + + bool Wait(art::Thread* self, uint64_t timeout_in_ms) { + auto wait_with_timeout = [&](std::unique_lock<std::mutex>& lk) { + cond_.wait_for(lk, std::chrono::milliseconds(timeout_in_ms)); + }; + return Wait(self, wait_with_timeout); + } + + bool Notify(art::Thread* self) { + return Notify(self, [&]() { cond_.notify_one(); }); + } + + bool NotifyAll(art::Thread* self) { + return Notify(self, [&]() { cond_.notify_all(); }); + } + + private: + bool IsOwner(art::Thread* self) { + // There's a subtle correctness argument here for a relaxed load outside the critical section. + // A thread is guaranteed to see either its own latest store or another thread's store. If a + // thread sees another thread's store than it cannot be holding the lock. + art::Thread* owner_thread = owner_.load(std::memory_order_relaxed); + return self == owner_thread; + } + + template <typename T> + bool Wait(art::Thread* self, T how_to_wait) { + if (!IsOwner(self)) { + return false; + } + + size_t old_count = count_; + + count_ = 0; + owner_.store(nullptr, std::memory_order_relaxed); + + { + std::unique_lock<std::mutex> lk(mutex_, std::adopt_lock); + how_to_wait(lk); + lk.release(); // Do not unlock the mutex. + } + + DCHECK(owner_.load(std::memory_order_relaxed) == nullptr); + owner_.store(self, std::memory_order_relaxed); + DCHECK_EQ(0u, count_); + count_ = old_count; + + return true; + } + + template <typename T> + bool Notify(art::Thread* self, T how_to_notify) { + if (!IsOwner(self)) { + return false; + } + + how_to_notify(); + + return true; + } + + std::mutex mutex_; + std::condition_variable cond_; + std::atomic<art::Thread*> owner_; + size_t count_; +}; + +static jrawMonitorID EncodeMonitor(JvmtiMonitor* monitor) { + return reinterpret_cast<jrawMonitorID>(monitor); +} + +static JvmtiMonitor* DecodeMonitor(jrawMonitorID id) { + return reinterpret_cast<JvmtiMonitor*>(id); +} + +jvmtiError MonitorUtil::CreateRawMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED, + const char* name, + jrawMonitorID* monitor_ptr) { + if (name == nullptr || monitor_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + JvmtiMonitor* monitor = new JvmtiMonitor(); + *monitor_ptr = EncodeMonitor(monitor); + + return ERR(NONE); +} + +jvmtiError MonitorUtil::DestroyRawMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { + if (id == nullptr) { + return ERR(INVALID_MONITOR); + } + + JvmtiMonitor* monitor = DecodeMonitor(id); + art::Thread* self = art::Thread::Current(); + + if (!JvmtiMonitor::Destroy(self, monitor)) { + return ERR(NOT_MONITOR_OWNER); + } + + return ERR(NONE); +} + +jvmtiError MonitorUtil::RawMonitorEnter(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { + if (id == nullptr) { + return ERR(INVALID_MONITOR); + } + + JvmtiMonitor* monitor = DecodeMonitor(id); + art::Thread* self = art::Thread::Current(); + + monitor->MonitorEnter(self); + + return ERR(NONE); +} + +jvmtiError MonitorUtil::RawMonitorExit(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { + if (id == nullptr) { + return ERR(INVALID_MONITOR); + } + + JvmtiMonitor* monitor = DecodeMonitor(id); + art::Thread* self = art::Thread::Current(); + + if (!monitor->MonitorExit(self)) { + return ERR(NOT_MONITOR_OWNER); + } + + return ERR(NONE); +} + +jvmtiError MonitorUtil::RawMonitorWait(jvmtiEnv* env ATTRIBUTE_UNUSED, + jrawMonitorID id, + jlong millis) { + if (id == nullptr) { + return ERR(INVALID_MONITOR); + } + + JvmtiMonitor* monitor = DecodeMonitor(id); + art::Thread* self = art::Thread::Current(); + + // This is not in the spec, but it's the only thing that makes sense (and agrees with + // Object.wait). + if (millis < 0) { + return ERR(ILLEGAL_ARGUMENT); + } + + bool result = (millis > 0) + ? monitor->Wait(self, static_cast<uint64_t>(millis)) + : monitor->Wait(self); + + if (!result) { + return ERR(NOT_MONITOR_OWNER); + } + + // TODO: Make sure that is really what we should be checking here. + if (self->IsInterrupted()) { + return ERR(INTERRUPT); + } + + return ERR(NONE); +} + +jvmtiError MonitorUtil::RawMonitorNotify(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { + if (id == nullptr) { + return ERR(INVALID_MONITOR); + } + + JvmtiMonitor* monitor = DecodeMonitor(id); + art::Thread* self = art::Thread::Current(); + + if (!monitor->Notify(self)) { + return ERR(NOT_MONITOR_OWNER); + } + + return ERR(NONE); +} + +jvmtiError MonitorUtil::RawMonitorNotifyAll(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { + if (id == nullptr) { + return ERR(INVALID_MONITOR); + } + + JvmtiMonitor* monitor = DecodeMonitor(id); + art::Thread* self = art::Thread::Current(); + + if (!monitor->NotifyAll(self)) { + return ERR(NOT_MONITOR_OWNER); + } + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_monitor.h b/runtime/openjdkjvmti/ti_monitor.h new file mode 100644 index 0000000000..96ccb0d1c7 --- /dev/null +++ b/runtime/openjdkjvmti/ti_monitor.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_MONITOR_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_MONITOR_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class MonitorUtil { + public: + static jvmtiError CreateRawMonitor(jvmtiEnv* env, const char* name, jrawMonitorID* monitor_ptr); + + static jvmtiError DestroyRawMonitor(jvmtiEnv* env, jrawMonitorID monitor); + + static jvmtiError RawMonitorEnter(jvmtiEnv* env, jrawMonitorID monitor); + + static jvmtiError RawMonitorExit(jvmtiEnv* env, jrawMonitorID monitor); + + static jvmtiError RawMonitorWait(jvmtiEnv* env, jrawMonitorID monitor, jlong millis); + + static jvmtiError RawMonitorNotify(jvmtiEnv* env, jrawMonitorID monitor); + + static jvmtiError RawMonitorNotifyAll(jvmtiEnv* env, jrawMonitorID monitor); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_MONITOR_H_ diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index a81458fded..b809c3eb56 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -140,7 +140,7 @@ class CatchBlockStackVisitor FINAL : public StackVisitor { DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor); }; -void QuickExceptionHandler::FindCatch(mirror::Throwable* exception) { +void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception) { DCHECK(!is_deoptimization_); if (kDebugExceptionDelivery) { mirror::String* msg = exception->GetDetailMessage(); diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h index 5592126a61..3ead7dbe64 100644 --- a/runtime/quick_exception_handler.h +++ b/runtime/quick_exception_handler.h @@ -46,7 +46,7 @@ class QuickExceptionHandler { } // Find the catch handler for the given exception. - void FindCatch(mirror::Throwable* exception) REQUIRES_SHARED(Locks::mutator_lock_); + void FindCatch(ObjPtr<mirror::Throwable> exception) REQUIRES_SHARED(Locks::mutator_lock_); // Deoptimize the stack to the upcall/some code that's not deoptimizeable. For // every compiled frame, we create a "copy" shadow frame that will be executed diff --git a/runtime/thread.cc b/runtime/thread.cc index 33c6a40320..a4f063141b 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -154,18 +154,18 @@ class DeoptimizationContextRecord { DeoptimizationContextRecord(const JValue& ret_val, bool is_reference, bool from_code, - mirror::Throwable* pending_exception, + ObjPtr<mirror::Throwable> pending_exception, DeoptimizationContextRecord* link) : ret_val_(ret_val), is_reference_(is_reference), from_code_(from_code), - pending_exception_(pending_exception), + pending_exception_(pending_exception.Ptr()), link_(link) {} JValue GetReturnValue() const { return ret_val_; } bool IsReference() const { return is_reference_; } bool GetFromCode() const { return from_code_; } - mirror::Throwable* GetPendingException() const { return pending_exception_; } + ObjPtr<mirror::Throwable> GetPendingException() const { return pending_exception_; } DeoptimizationContextRecord* GetLink() const { return link_; } mirror::Object** GetReturnValueAsGCRoot() { DCHECK(is_reference_); @@ -219,7 +219,7 @@ class StackedShadowFrameRecord { void Thread::PushDeoptimizationContext(const JValue& return_value, bool is_reference, bool from_code, - mirror::Throwable* exception) { + ObjPtr<mirror::Throwable> exception) { DeoptimizationContextRecord* record = new DeoptimizationContextRecord( return_value, is_reference, @@ -230,7 +230,7 @@ void Thread::PushDeoptimizationContext(const JValue& return_value, } void Thread::PopDeoptimizationContext(JValue* result, - mirror::Throwable** exception, + ObjPtr<mirror::Throwable>* exception, bool* from_code) { AssertHasDeoptimizationContext(); DeoptimizationContextRecord* record = tlsPtr_.deoptimization_context_stack; @@ -434,7 +434,7 @@ void* Thread::CreateCallback(void* arg) { Dbg::PostThreadStart(self); // Invoke the 'run' method of our java.lang.Thread. - mirror::Object* receiver = self->tlsPtr_.opeer; + ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer; jmethodID mid = WellKnownClasses::java_lang_Thread_run; ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr); @@ -446,7 +446,7 @@ void* Thread::CreateCallback(void* arg) { } Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa, - mirror::Object* thread_peer) { + ObjPtr<mirror::Object> thread_peer) { ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer); Thread* result = reinterpret_cast<Thread*>(static_cast<uintptr_t>(f->GetLong(thread_peer))); // Sanity check that if we have a result it is either suspended or we hold the thread_list_lock_ @@ -1573,8 +1573,8 @@ struct StackDumpVisitor : public StackVisitor { } m = m->GetInterfaceMethodIfProxy(kRuntimePointerSize); const int kMaxRepetition = 3; - mirror::Class* c = m->GetDeclaringClass(); - mirror::DexCache* dex_cache = c->GetDexCache(); + ObjPtr<mirror::Class> c = m->GetDeclaringClass(); + ObjPtr<mirror::DexCache> dex_cache = c->GetDexCache(); int line_number = -1; if (dex_cache != nullptr) { // be tolerant of bad input const DexFile* dex_file = dex_cache->GetDexFile(); @@ -1860,17 +1860,15 @@ void Thread::AssertPendingOOMException() const { void Thread::AssertNoPendingException() const { if (UNLIKELY(IsExceptionPending())) { ScopedObjectAccess soa(Thread::Current()); - mirror::Throwable* exception = GetException(); - LOG(FATAL) << "No pending exception expected: " << exception->Dump(); + LOG(FATAL) << "No pending exception expected: " << GetException()->Dump(); } } void Thread::AssertNoPendingExceptionForNewException(const char* msg) const { if (UNLIKELY(IsExceptionPending())) { ScopedObjectAccess soa(Thread::Current()); - mirror::Throwable* exception = GetException(); LOG(FATAL) << "Throwing new exception '" << msg << "' with unexpected pending exception: " - << exception->Dump(); + << GetException()->Dump(); } } @@ -2213,7 +2211,7 @@ class BuildInternalStackTraceVisitor : public StackVisitor { // class of the ArtMethod pointers. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<1> hs(self_); - mirror::Class* array_class = class_linker->GetClassRoot(ClassLinker::kObjectArrayClass); + ObjPtr<mirror::Class> array_class = class_linker->GetClassRoot(ClassLinker::kObjectArrayClass); // The first element is the methods and dex pc array, the other elements are declaring classes // for the methods to ensure classes in the stack trace don't get unloaded. Handle<mirror::ObjectArray<mirror::Object>> trace( @@ -2225,7 +2223,8 @@ class BuildInternalStackTraceVisitor : public StackVisitor { self_->AssertPendingOOMException(); return false; } - mirror::PointerArray* methods_and_pcs = class_linker->AllocPointerArray(self_, depth * 2); + ObjPtr<mirror::PointerArray> methods_and_pcs = + class_linker->AllocPointerArray(self_, depth * 2); const char* last_no_suspend_cause = self_->StartAssertNoThreadSuspension("Building internal stack trace"); if (methods_and_pcs == nullptr) { @@ -2255,7 +2254,7 @@ class BuildInternalStackTraceVisitor : public StackVisitor { if (m->IsRuntimeMethod()) { return true; // Ignore runtime frames (in particular callee save). } - mirror::PointerArray* trace_methods_and_pcs = GetTraceMethodsAndPCs(); + ObjPtr<mirror::PointerArray> trace_methods_and_pcs = GetTraceMethodsAndPCs(); trace_methods_and_pcs->SetElementPtrSize<kTransactionActive>(count_, m, pointer_size_); trace_methods_and_pcs->SetElementPtrSize<kTransactionActive>( trace_methods_and_pcs->GetLength() / 2 + count_, @@ -2268,8 +2267,8 @@ class BuildInternalStackTraceVisitor : public StackVisitor { return true; } - mirror::PointerArray* GetTraceMethodsAndPCs() const REQUIRES_SHARED(Locks::mutator_lock_) { - return down_cast<mirror::PointerArray*>(trace_->Get(0)); + ObjPtr<mirror::PointerArray> GetTraceMethodsAndPCs() const REQUIRES_SHARED(Locks::mutator_lock_) { + return ObjPtr<mirror::PointerArray>::DownCast(MakeObjPtr(trace_->Get(0))); } mirror::ObjectArray<mirror::Object>* GetInternalStackTrace() const { @@ -2311,7 +2310,7 @@ jobject Thread::CreateInternalStackTrace(const ScopedObjectAccessAlreadyRunnable build_trace_visitor.WalkStack(); mirror::ObjectArray<mirror::Object>* trace = build_trace_visitor.GetInternalStackTrace(); if (kIsDebugBuild) { - mirror::PointerArray* trace_methods = build_trace_visitor.GetTraceMethodsAndPCs(); + ObjPtr<mirror::PointerArray> trace_methods = build_trace_visitor.GetTraceMethodsAndPCs(); // Second half of trace_methods is dex PCs. for (uint32_t i = 0; i < static_cast<uint32_t>(trace_methods->GetLength() / 2); ++i) { auto* method = trace_methods->GetElementPtrSize<ArtMethod*>( @@ -2326,7 +2325,7 @@ template jobject Thread::CreateInternalStackTrace<false>( template jobject Thread::CreateInternalStackTrace<true>( const ScopedObjectAccessAlreadyRunnable& soa) const; -bool Thread::IsExceptionThrownByCurrentMethod(mirror::Throwable* exception) const { +bool Thread::IsExceptionThrownByCurrentMethod(ObjPtr<mirror::Throwable> exception) const { CountStackDepthVisitor count_visitor(const_cast<Thread*>(this)); count_visitor.WalkStack(); return count_visitor.GetDepth() == exception->GetStackDepth(); @@ -2368,12 +2367,12 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( } for (int32_t i = 0; i < depth; ++i) { - mirror::ObjectArray<mirror::Object>* decoded_traces = + ObjPtr<mirror::ObjectArray<mirror::Object>> decoded_traces = soa.Decode<mirror::Object>(internal)->AsObjectArray<mirror::Object>(); // Methods and dex PC trace is element 0. DCHECK(decoded_traces->Get(0)->IsIntArray() || decoded_traces->Get(0)->IsLongArray()); - mirror::PointerArray* const method_trace = - down_cast<mirror::PointerArray*>(decoded_traces->Get(0)); + ObjPtr<mirror::PointerArray> const method_trace = + ObjPtr<mirror::PointerArray>::DownCast(MakeObjPtr(decoded_traces->Get(0))); // Prepare parameters for StackTraceElement(String cls, String method, String file, int line) ArtMethod* method = method_trace->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize); uint32_t dex_pc = method_trace->GetElementPtrSize<uint32_t>( @@ -2415,8 +2414,11 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( if (method_name_object.Get() == nullptr) { return nullptr; } - mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc( - soa.Self(), class_name_object, method_name_object, source_name_object, line_number); + ObjPtr<mirror::StackTraceElement> obj =mirror::StackTraceElement::Alloc(soa.Self(), + class_name_object, + method_name_object, + source_name_object, + line_number); if (obj == nullptr) { return nullptr; } @@ -2447,7 +2449,7 @@ void Thread::ThrowNewException(const char* exception_class_descriptor, ThrowNewWrappedException(exception_class_descriptor, msg); } -static mirror::ClassLoader* GetCurrentClassLoader(Thread* self) +static ObjPtr<mirror::ClassLoader> GetCurrentClassLoader(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* method = self->GetCurrentMethod(nullptr); return method != nullptr @@ -2627,9 +2629,10 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pAllocArray) QUICK_ENTRY_POINT_INFO(pAllocArrayResolved) QUICK_ENTRY_POINT_INFO(pAllocArrayWithAccessCheck) + QUICK_ENTRY_POINT_INFO(pAllocObject) QUICK_ENTRY_POINT_INFO(pAllocObjectResolved) QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized) - QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks) + QUICK_ENTRY_POINT_INFO(pAllocObjectWithAccessCheck) QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray) QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck) QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes) @@ -2793,7 +2796,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { void Thread::QuickDeliverException() { // Get exception from thread. - mirror::Throwable* exception = GetException(); + ObjPtr<mirror::Throwable> exception = GetException(); CHECK(exception != nullptr); if (exception == GetDeoptimizationException()) { artDeoptimize(this); @@ -2806,8 +2809,8 @@ void Thread::QuickDeliverException() { IsExceptionThrownByCurrentMethod(exception)) { // Instrumentation may cause GC so keep the exception object safe. StackHandleScope<1> hs(this); - HandleWrapper<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception)); - instrumentation->ExceptionCaughtEvent(this, exception); + HandleWrapperObjPtr<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception)); + instrumentation->ExceptionCaughtEvent(this, exception.Ptr()); } // Does instrumentation need to deoptimize the stack? // Note: we do this *after* reporting the exception to instrumentation in case it @@ -2869,7 +2872,7 @@ struct CurrentMethodVisitor FINAL : public StackVisitor { dex_pc_ = GetDexPc(abort_on_error_); return false; } - mirror::Object* this_object_; + ObjPtr<mirror::Object> this_object_; ArtMethod* method_; uint32_t dex_pc_; const bool abort_on_error_; @@ -2884,11 +2887,8 @@ ArtMethod* Thread::GetCurrentMethod(uint32_t* dex_pc, bool abort_on_error) const return visitor.method_; } -bool Thread::HoldsLock(mirror::Object* object) const { - if (object == nullptr) { - return false; - } - return object->GetLockOwnerThreadId() == GetThreadId(); +bool Thread::HoldsLock(ObjPtr<mirror::Object> object) const { + return object != nullptr && object->GetLockOwnerThreadId() == GetThreadId(); } // RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor). @@ -2944,7 +2944,7 @@ class ReferenceMapVisitor : public StackVisitor { void VisitDeclaringClass(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) NO_THREAD_SAFETY_ANALYSIS { - mirror::Class* klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>(); + ObjPtr<mirror::Class> klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>(); // klass can be null for runtime methods. if (klass != nullptr) { if (kVerifyImageObjectsMarked) { @@ -2953,10 +2953,10 @@ class ReferenceMapVisitor : public StackVisitor { /*fail_ok*/true); if (space != nullptr && space->IsImageSpace()) { bool failed = false; - if (!space->GetLiveBitmap()->Test(klass)) { + if (!space->GetLiveBitmap()->Test(klass.Ptr())) { failed = true; LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image " << *space; - } else if (!heap->GetLiveBitmap()->Test(klass)) { + } else if (!heap->GetLiveBitmap()->Test(klass.Ptr())) { failed = true; LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image through live bitmap " << *space; } @@ -2964,17 +2964,17 @@ class ReferenceMapVisitor : public StackVisitor { GetThread()->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); space->AsImageSpace()->DumpSections(LOG_STREAM(FATAL_WITHOUT_ABORT)); LOG(FATAL_WITHOUT_ABORT) << "Method@" << method->GetDexMethodIndex() << ":" << method - << " klass@" << klass; + << " klass@" << klass.Ptr(); // Pretty info last in case it crashes. LOG(FATAL) << "Method " << method->PrettyMethod() << " klass " << klass->PrettyClass(); } } } - mirror::Object* new_ref = klass; + mirror::Object* new_ref = klass.Ptr(); visitor_(&new_ref, -1, this); if (new_ref != klass) { - method->CASDeclaringClass(klass, new_ref->AsClass()); + method->CASDeclaringClass(klass.Ptr(), new_ref->AsClass()); } } } @@ -3366,7 +3366,7 @@ void Thread::DeoptimizeWithDeoptimizationException(JValue* result) { ClearException(); ShadowFrame* shadow_frame = PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame); - mirror::Throwable* pending_exception = nullptr; + ObjPtr<mirror::Throwable> pending_exception; bool from_code = false; PopDeoptimizationContext(result, &pending_exception, &from_code); SetTopOfStack(nullptr); diff --git a/runtime/thread.h b/runtime/thread.h index 6308851096..c7acfc4c9c 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -177,7 +177,7 @@ class Thread { void CheckEmptyCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_); static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts, - mirror::Object* thread_peer) + ObjPtr<mirror::Object> thread_peer) REQUIRES(Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_) REQUIRES_SHARED(Locks::mutator_lock_); static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts, jobject thread) @@ -312,7 +312,7 @@ class Thread { size_t NumberOfHeldMutexes() const; - bool HoldsLock(mirror::Object*) const REQUIRES_SHARED(Locks::mutator_lock_); + bool HoldsLock(ObjPtr<mirror::Object> object) const REQUIRES_SHARED(Locks::mutator_lock_); /* * Changes the priority of this thread to match that of the java.lang.Thread object. @@ -413,7 +413,7 @@ class Thread { // Returns whether the given exception was thrown by the current Java method being executed // (Note that this includes native Java methods). - bool IsExceptionThrownByCurrentMethod(mirror::Throwable* exception) const + bool IsExceptionThrownByCurrentMethod(ObjPtr<mirror::Throwable> exception) const REQUIRES_SHARED(Locks::mutator_lock_); void SetTopOfStack(ArtMethod** top_method) { @@ -925,9 +925,11 @@ class Thread { void PushDeoptimizationContext(const JValue& return_value, bool is_reference, bool from_code, - mirror::Throwable* exception) + ObjPtr<mirror::Throwable> exception) REQUIRES_SHARED(Locks::mutator_lock_); - void PopDeoptimizationContext(JValue* result, mirror::Throwable** exception, bool* from_code) + void PopDeoptimizationContext(JValue* result, + ObjPtr<mirror::Throwable>* exception, + bool* from_code) REQUIRES_SHARED(Locks::mutator_lock_); void AssertHasDeoptimizationContext() REQUIRES_SHARED(Locks::mutator_lock_); @@ -1416,7 +1418,7 @@ class Thread { stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr), frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0), last_no_thread_suspension_cause(nullptr), checkpoint_function(nullptr), - thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr), + thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_start(nullptr), thread_local_objects(0), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr), nested_signal_state(nullptr), @@ -1540,12 +1542,12 @@ class Thread { JniEntryPoints jni_entrypoints; QuickEntryPoints quick_entrypoints; - // Thread-local allocation pointer. Moved here to force alignment for thread_local_pos on ARM. - uint8_t* thread_local_start; // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for // potentially better performance. uint8_t* thread_local_pos; uint8_t* thread_local_end; + // Thread-local allocation pointer. + uint8_t* thread_local_start; size_t thread_local_objects; diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 34f9043792..c5c7e2cc16 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -653,8 +653,9 @@ void ThreadList::SuspendAllInternal(Thread* self, // is done with a timeout so that we can detect problems. #if ART_USE_FUTEXES timespec wait_timeout; - InitTimeSpec(false, CLOCK_MONOTONIC, 10000, 0, &wait_timeout); + InitTimeSpec(false, CLOCK_MONOTONIC, kIsDebugBuild ? 50000 : 10000, 0, &wait_timeout); #endif + const uint64_t start_time = NanoTime(); while (true) { int32_t cur_val = pending_threads.LoadRelaxed(); if (LIKELY(cur_val > 0)) { @@ -664,7 +665,8 @@ void ThreadList::SuspendAllInternal(Thread* self, if ((errno != EAGAIN) && (errno != EINTR)) { if (errno == ETIMEDOUT) { LOG(kIsDebugBuild ? ::android::base::FATAL : ::android::base::ERROR) - << "Unexpected time out during suspend all."; + << "Timed out waiting for threads to suspend, waited for " + << PrettyDuration(NanoTime() - start_time); } else { PLOG(FATAL) << "futex wait failed for SuspendAllInternal()"; } @@ -672,6 +674,7 @@ void ThreadList::SuspendAllInternal(Thread* self, } // else re-check pending_threads in the next iteration (this may be a spurious wake-up). #else // Spin wait. This is likely to be slow, but on most architecture ART_USE_FUTEXES is set. + UNUSED(start_time); #endif } else { CHECK_EQ(cur_val, 0); diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java index 89b9cb45c3..5fd51e1dca 100644 --- a/test/529-checker-unresolved/src/Main.java +++ b/test/529-checker-unresolved/src/Main.java @@ -192,13 +192,13 @@ public class Main extends UnresolvedSuperClass { /// CHECK-START: void Main.testLicm(int) licm (before) /// CHECK: <<Class:l\d+>> LoadClass loop:B2 /// CHECK-NEXT: <<Clinit:l\d+>> ClinitCheck [<<Class>>] loop:B2 - /// CHECK-NEXT: <<New:l\d+>> NewInstance [<<Clinit>>] loop:B2 + /// CHECK-NEXT: <<New:l\d+>> NewInstance [<<Clinit>>,<<Method:[i|j]\d+>>] loop:B2 /// CHECK-NEXT: InvokeUnresolved [<<New>>] loop:B2 /// CHECK-START: void Main.testLicm(int) licm (after) /// CHECK: <<Class:l\d+>> LoadClass loop:none /// CHECK-NEXT: <<Clinit:l\d+>> ClinitCheck [<<Class>>] loop:none - /// CHECK: <<New:l\d+>> NewInstance [<<Clinit>>] loop:B2 + /// CHECK: <<New:l\d+>> NewInstance [<<Clinit>>,<<Method:[i|j]\d+>>] loop:B2 /// CHECK-NEXT: InvokeUnresolved [<<New>>] loop:B2 static public void testLicm(int count) { // Test to make sure we keep the initialization check after loading an unresolved class. diff --git a/test/621-checker-new-instance/expected.txt b/test/621-checker-new-instance/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/621-checker-new-instance/expected.txt diff --git a/test/621-checker-new-instance/info.txt b/test/621-checker-new-instance/info.txt new file mode 100644 index 0000000000..c27c45ca7f --- /dev/null +++ b/test/621-checker-new-instance/info.txt @@ -0,0 +1 @@ +Tests for removing useless load class. diff --git a/test/621-checker-new-instance/src/Main.java b/test/621-checker-new-instance/src/Main.java new file mode 100644 index 0000000000..68a46449f0 --- /dev/null +++ b/test/621-checker-new-instance/src/Main.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + /// CHECK-START: java.lang.Object Main.newObject() prepare_for_register_allocation (before) + /// CHECK: LoadClass + /// CHECK: NewInstance + + /// CHECK-START: java.lang.Object Main.newObject() prepare_for_register_allocation (after) + /// CHECK-NOT: LoadClass + /// CHECK: NewInstance + public static Object newObject() { + return new Object(); + } + + /// CHECK-START: java.lang.Object Main.newFinalizableMayThrow() prepare_for_register_allocation (after) + /// CHECK: LoadClass + /// CHECK: NewInstance + public static Object newFinalizableMayThrow() { + return $inline$newFinalizableMayThrow(); + } + + public static Object $inline$newFinalizableMayThrow() { + return new FinalizableMayThrow(); + } + + public static void main(String[] args) { + newFinalizableMayThrow(); + newObject(); + } +} + +class FinalizableMayThrow { + // clinit may throw OOME. + static Object o = new Object(); + static String s; + public void finalize() { + s = "Test"; + } +} diff --git a/test/923-monitors/build b/test/923-monitors/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/923-monitors/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-build "$@" --experimental agents diff --git a/test/923-monitors/expected.txt b/test/923-monitors/expected.txt new file mode 100644 index 0000000000..5fbfb9866e --- /dev/null +++ b/test/923-monitors/expected.txt @@ -0,0 +1,38 @@ +Unlock +JVMTI_ERROR_NOT_MONITOR_OWNER +Lock +Unlock +Unlock +JVMTI_ERROR_NOT_MONITOR_OWNER +Lock +Lock +Unlock +Unlock +Unlock +JVMTI_ERROR_NOT_MONITOR_OWNER +Wait +JVMTI_ERROR_NOT_MONITOR_OWNER +Wait +JVMTI_ERROR_ILLEGAL_ARGUMENT +Wait +JVMTI_ERROR_NOT_MONITOR_OWNER +Lock +Wait +Unlock +Unlock +JVMTI_ERROR_NOT_MONITOR_OWNER +Notify +JVMTI_ERROR_NOT_MONITOR_OWNER +Lock +Notify +Unlock +Unlock +JVMTI_ERROR_NOT_MONITOR_OWNER +NotifyAll +JVMTI_ERROR_NOT_MONITOR_OWNER +Lock +NotifyAll +Unlock +Unlock +JVMTI_ERROR_NOT_MONITOR_OWNER +Done diff --git a/test/923-monitors/info.txt b/test/923-monitors/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/923-monitors/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/923-monitors/monitors.cc b/test/923-monitors/monitors.cc new file mode 100644 index 0000000000..2aa36cbdba --- /dev/null +++ b/test/923-monitors/monitors.cc @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2017 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 "monitors.h" + +#include <stdio.h> + +#include "base/macros.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "ScopedUtfChars.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test923Monitors { + + +static jlong MonitorToLong(jrawMonitorID id) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(id)); +} + +static jrawMonitorID LongToMonitor(jlong l) { + return reinterpret_cast<jrawMonitorID>(static_cast<uintptr_t>(l)); +} + +extern "C" JNIEXPORT jlong JNICALL Java_Main_createRawMonitor( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jrawMonitorID id; + jvmtiError result = jvmti_env->CreateRawMonitor("dummy", &id); + if (JvmtiErrorToException(env, result)) { + return 0; + } + return MonitorToLong(id); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_destroyRawMonitor( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) { + jvmtiError result = jvmti_env->DestroyRawMonitor(LongToMonitor(l)); + JvmtiErrorToException(env, result); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorEnter( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) { + jvmtiError result = jvmti_env->RawMonitorEnter(LongToMonitor(l)); + JvmtiErrorToException(env, result); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorExit( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) { + jvmtiError result = jvmti_env->RawMonitorExit(LongToMonitor(l)); + JvmtiErrorToException(env, result); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorWait( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l, jlong millis) { + jvmtiError result = jvmti_env->RawMonitorWait(LongToMonitor(l), millis); + JvmtiErrorToException(env, result); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorNotify( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) { + jvmtiError result = jvmti_env->RawMonitorNotify(LongToMonitor(l)); + JvmtiErrorToException(env, result); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorNotifyAll( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) { + jvmtiError result = jvmti_env->RawMonitorNotifyAll(LongToMonitor(l)); + JvmtiErrorToException(env, result); +} + +// Don't do anything +jint OnLoad(JavaVM* vm, + char* options ATTRIBUTE_UNUSED, + void* reserved ATTRIBUTE_UNUSED) { + if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { + printf("Unable to get jvmti env!\n"); + return 1; + } + SetAllCapabilities(jvmti_env); + return 0; +} + +} // namespace Test923Monitors +} // namespace art diff --git a/test/923-monitors/monitors.h b/test/923-monitors/monitors.h new file mode 100644 index 0000000000..14cd5cd633 --- /dev/null +++ b/test/923-monitors/monitors.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 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_TEST_923_MONITORS_MONITORS_H_ +#define ART_TEST_923_MONITORS_MONITORS_H_ + +#include <jni.h> + +namespace art { +namespace Test923Monitors { + +jint OnLoad(JavaVM* vm, char* options, void* reserved); + +} // namespace Test923Monitors +} // namespace art + +#endif // ART_TEST_923_MONITORS_MONITORS_H_ diff --git a/test/923-monitors/run b/test/923-monitors/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/923-monitors/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/923-monitors/src/Main.java b/test/923-monitors/src/Main.java new file mode 100644 index 0000000000..e35ce12608 --- /dev/null +++ b/test/923-monitors/src/Main.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2017 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.util.concurrent.CountDownLatch; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + private static void doTest() throws Exception { + // Start a watchdog, to make sure on deadlocks etc the test dies. + startWatchdog(); + + sharedId = createRawMonitor(); + + output = new ArrayList<String>(100); + + simpleTests(sharedId); + + for (String s : output) { + System.out.println(s); + } + output.clear(); + + threadTests(sharedId); + + destroyRawMonitor(sharedId); + } + + private static void simpleTests(long id) { + unlock(id); // Should fail. + + lock(id); + unlock(id); + unlock(id); // Should fail. + + lock(id); + lock(id); + unlock(id); + unlock(id); + unlock(id); // Should fail. + + rawWait(id, 0); // Should fail. + rawWait(id, -1); // Should fail. + rawWait(id, 1); // Should fail. + + lock(id); + rawWait(id, 50); + unlock(id); + unlock(id); // Should fail. + + rawNotify(id); // Should fail. + lock(id); + rawNotify(id); + unlock(id); + unlock(id); // Should fail. + + rawNotifyAll(id); // Should fail. + lock(id); + rawNotifyAll(id); + unlock(id); + unlock(id); // Should fail. + } + + private static void threadTests(final long id) throws Exception { + final int N = 10; + + final CountDownLatch waitLatch = new CountDownLatch(N); + final CountDownLatch wait2Latch = new CountDownLatch(1); + + Runnable r = new Runnable() { + @Override + public void run() { + lock(id); + waitLatch.countDown(); + rawWait(id, 0); + firstAwakened = Thread.currentThread(); + appendToLog("Awakened"); + unlock(id); + wait2Latch.countDown(); + } + }; + + List<Thread> threads = new ArrayList<Thread>(); + for (int i = 0; i < N; i++) { + Thread t = new Thread(r); + threads.add(t); + t.start(); + } + + // Wait till all threads have been started. + waitLatch.await(); + + // Hopefully enough time for all the threads to progress into wait. + Thread.yield(); + Thread.sleep(500); + + // Wake up one. + lock(id); + rawNotify(id); + unlock(id); + + wait2Latch.await(); + + // Wait a little bit more to see stragglers. This is flaky - spurious wakeups could + // make the test fail. + Thread.yield(); + Thread.sleep(500); + if (firstAwakened != null) { + firstAwakened.join(); + } + + // Wake up everyone else. + lock(id); + rawNotifyAll(id); + unlock(id); + + // Wait for everyone to die. + for (Thread t : threads) { + t.join(); + } + + // Check threaded output. + Iterator<String> it = output.iterator(); + // 1) Start with N locks and Waits. + { + int locks = 0; + int waits = 0; + for (int i = 0; i < 2*N; i++) { + String s = it.next(); + if (s.equals("Lock")) { + locks++; + } else if (s.equals("Wait")) { + if (locks <= waits) { + System.out.println(output); + throw new RuntimeException("Wait before Lock"); + } + waits++; + } else { + System.out.println(output); + throw new RuntimeException("Unexpected operation: " + s); + } + } + } + + // 2) Expect Lock + Notify + Unlock. + expect("Lock", it, output); + expect("Notify", it, output); + expect("Unlock", it, output); + + // 3) A single thread wakes up, runs, and dies. + expect("Awakened", it, output); + expect("Unlock", it, output); + + // 4) Expect Lock + NotifyAll + Unlock. + expect("Lock", it, output); + expect("NotifyAll", it, output); + expect("Unlock", it, output); + + // 5) N-1 threads wake up, run, and die. + { + int expectedUnlocks = 0; + int ops = 2 * (N-1); + for (int i = 0; i < ops; i++) { + String s = it.next(); + if (s.equals("Awakened")) { + expectedUnlocks++; + } else if (s.equals("Unlock")) { + expectedUnlocks--; + if (expectedUnlocks < 0) { + System.out.println(output); + throw new RuntimeException("Unexpected unlock"); + } + } + } + } + + // 6) That should be it. + if (it.hasNext()) { + System.out.println(output); + throw new RuntimeException("Unexpected trailing output, starting with " + it.next()); + } + + output.clear(); + System.out.println("Done"); + } + + private static void expect(String s, Iterator<String> it, List<String> output) { + String t = it.next(); + if (!s.equals(t)) { + System.out.println(output); + throw new RuntimeException("Expected " + s + " but got " + t); + } + } + + private static void lock(long id) { + appendToLog("Lock"); + rawMonitorEnter(id); + } + + private static void unlock(long id) { + appendToLog("Unlock"); + try { + rawMonitorExit(id); + } catch (RuntimeException e) { + appendToLog(e.getMessage()); + } + } + + private static void rawWait(long id, long millis) { + appendToLog("Wait"); + try { + rawMonitorWait(id, millis); + } catch (RuntimeException e) { + appendToLog(e.getMessage()); + } + } + + private static void rawNotify(long id) { + appendToLog("Notify"); + try { + rawMonitorNotify(id); + } catch (RuntimeException e) { + appendToLog(e.getMessage()); + } + } + + private static void rawNotifyAll(long id) { + appendToLog("NotifyAll"); + try { + rawMonitorNotifyAll(id); + } catch (RuntimeException e) { + appendToLog(e.getMessage()); + } + } + + private static synchronized void appendToLog(String s) { + output.add(s); + } + + private static void startWatchdog() { + Runnable r = new Runnable() { + @Override + public void run() { + long start = System.currentTimeMillis(); + // Give it a minute. + long end = 60 * 1000 + start; + for (;;) { + long delta = end - System.currentTimeMillis(); + if (delta <= 0) { + break; + } + + try { + Thread.currentThread().sleep(delta); + } catch (Exception e) { + } + } + System.out.println("TIMEOUT!"); + System.exit(1); + } + }; + Thread t = new Thread(r); + t.setDaemon(true); + t.start(); + } + + static volatile long sharedId; + static List<String> output; + static Thread firstAwakened; + + private static native long createRawMonitor(); + private static native void destroyRawMonitor(long id); + private static native void rawMonitorEnter(long id); + private static native void rawMonitorExit(long id); + private static native void rawMonitorWait(long id, long millis); + private static native void rawMonitorNotify(long id); + private static native void rawMonitorNotifyAll(long id); +} diff --git a/test/Android.bp b/test/Android.bp index f6648d1cdc..a223c3aa29 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -263,6 +263,7 @@ art_cc_defaults { "918-fields/fields.cc", "920-objects/objects.cc", "922-properties/properties.cc", + "923-monitors/monitors.cc", ], shared_libs: [ "libbase", diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index a3f6864883..fd3a897dae 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -295,6 +295,7 @@ TEST_ART_BROKEN_TARGET_TESTS += \ 920-objects \ 921-hello-failure \ 922-properties \ + 923-monitors \ ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index e309a8920b..33e132103e 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -40,6 +40,7 @@ #include "918-fields/fields.h" #include "920-objects/objects.h" #include "922-properties/properties.h" +#include "923-monitors/monitors.h" namespace art { @@ -78,6 +79,7 @@ AgentLib agents[] = { { "920-objects", Test920Objects::OnLoad, nullptr }, { "921-hello-failure", common_redefine::OnLoad, nullptr }, { "922-properties", Test922Properties::OnLoad, nullptr }, + { "923-monitors", Test923Monitors::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { |