summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/oat_test.cc2
-rw-r--r--compiler/optimizing/code_generator_arm.cc3
-rw-r--r--compiler/optimizing/code_generator_arm64.cc3
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc3
-rw-r--r--compiler/optimizing/code_generator_mips.cc3
-rw-r--r--compiler/optimizing/code_generator_mips64.cc3
-rw-r--r--compiler/optimizing/code_generator_x86.cc3
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc3
-rw-r--r--compiler/optimizing/inliner.cc9
-rw-r--r--compiler/optimizing/instruction_builder.cc5
-rw-r--r--compiler/optimizing/nodes.h4
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.cc33
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.h1
-rw-r--r--compiler/utils/assembler_thumb_test_expected.cc.inc2
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S112
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S128
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S112
-rw-r--r--runtime/arch/mips64/quick_entrypoints_mips64.S104
-rw-r--r--runtime/arch/quick_alloc_entrypoints.S34
-rw-r--r--runtime/arch/stub_test.cc12
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S175
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S127
-rw-r--r--runtime/art_method.h6
-rw-r--r--runtime/asm_support.h2
-rw-r--r--runtime/base/logging.cc2
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h65
-rw-r--r--runtime/entrypoints/entrypoint_utils.h21
-rw-r--r--runtime/entrypoints/quick/quick_alloc_entrypoints.cc136
-rw-r--r--runtime/entrypoints/quick/quick_entrypoints_list.h7
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc2
-rw-r--r--runtime/entrypoints_order_test.cc12
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc5
-rw-r--r--runtime/interpreter/mterp/mterp.cc7
-rw-r--r--runtime/jdwp/jdwp_adb.cc16
-rw-r--r--runtime/oat.h2
-rw-r--r--runtime/openjdkjvmti/Android.bp1
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc15
-rw-r--r--runtime/openjdkjvmti/ti_monitor.cc302
-rw-r--r--runtime/openjdkjvmti/ti_monitor.h59
-rw-r--r--runtime/quick_exception_handler.cc2
-rw-r--r--runtime/quick_exception_handler.h2
-rw-r--r--runtime/thread.cc86
-rw-r--r--runtime/thread.h18
-rw-r--r--runtime/thread_list.cc7
-rw-r--r--test/529-checker-unresolved/src/Main.java4
-rw-r--r--test/621-checker-new-instance/expected.txt0
-rw-r--r--test/621-checker-new-instance/info.txt1
-rw-r--r--test/621-checker-new-instance/src/Main.java53
-rwxr-xr-xtest/923-monitors/build17
-rw-r--r--test/923-monitors/expected.txt38
-rw-r--r--test/923-monitors/info.txt1
-rw-r--r--test/923-monitors/monitors.cc100
-rw-r--r--test/923-monitors/monitors.h30
-rwxr-xr-xtest/923-monitors/run19
-rw-r--r--test/923-monitors/src/Main.java298
-rw-r--r--test/Android.bp1
-rw-r--r--test/Android.run-test.mk1
-rw-r--r--test/ti-agent/common_load.cc2
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) {